Merge branch 'main' into amammad-python-WebAppsConstatntSecretKeys

This commit is contained in:
Rasmus Wriedt Larsen
2023-07-14 14:32:43 +02:00
677 changed files with 15882 additions and 4544 deletions

View File

@@ -14,14 +14,16 @@ If you have an idea for a query that you would like to share with other CodeQL u
1. **Directory structure**
There are six language-specific query directories in this repository:
There are eight language-specific query directories in this repository:
* C/C++: `cpp/ql/src`
* C#: `csharp/ql/src`
* Java: `java/ql/src`
* Go: `go/ql/src`
* Java/Kotlin: `java/ql/src`
* JavaScript: `javascript/ql/src`
* Python: `python/ql/src`
* Ruby: `ruby/ql/src`
* Swift: `swift/ql/src`
Each language-specific directory contains further subdirectories that group queries based on their `@tags` or purpose.
- Experimental queries and libraries are stored in the `experimental` subdirectory within each language-specific directory in the [CodeQL repository](https://github.com/github/codeql). For example, experimental Java queries and libraries are stored in `java/ql/src/experimental` and any corresponding tests in `java/ql/test/experimental`.

View File

@@ -1,3 +1,17 @@
## 0.8.0
### New Features
* The `ProductFlow::StateConfigSig` signature now includes default predicates for `isBarrier1`, `isBarrier2`, `isAdditionalFlowStep1`, and `isAdditionalFlowStep1`. Hence, it is no longer needed to provide `none()` implementations of these predicates if they are not needed.
### Minor Analysis Improvements
* Deleted the deprecated `getURL` predicate from the `Container`, `Folder`, and `File` classes. Use the `getLocation` predicate instead.
## 0.7.4
No user-facing changes.
## 0.7.3
### Minor Analysis Improvements

View File

@@ -1,4 +0,0 @@
---
category: minorAnalysis
---
* Deleted the deprecated `getURL` predicate from the `Container`, `Folder`, and `File` classes. Use the `getLocation` predicate instead.

View File

@@ -0,0 +1,4 @@
---
category: minorAnalysis
---
* The `IRGuards` library has improved handling of pointer addition and subtraction operations.

View File

@@ -0,0 +1,6 @@
---
category: feature
---
* The `DataFlow::StateConfigSig` signature module has gained default implementations for `isBarrier/2` and `isAdditionalFlowStep/4`.
Hence it is no longer needed to provide `none()` implementations of these predicates if they are not needed.

View File

@@ -0,0 +1,3 @@
## 0.7.4
No user-facing changes.

View File

@@ -0,0 +1,9 @@
## 0.8.0
### New Features
* The `ProductFlow::StateConfigSig` signature now includes default predicates for `isBarrier1`, `isBarrier2`, `isAdditionalFlowStep1`, and `isAdditionalFlowStep1`. Hence, it is no longer needed to provide `none()` implementations of these predicates if they are not needed.
### Minor Analysis Improvements
* Deleted the deprecated `getURL` predicate from the `Container`, `Folder`, and `File` classes. Use the `getLocation` predicate instead.

View File

@@ -1,2 +1,2 @@
---
lastReleaseVersion: 0.7.3
lastReleaseVersion: 0.8.0

View File

@@ -1,5 +1,5 @@
name: codeql/cpp-all
version: 0.7.4-dev
version: 0.8.1-dev
groups: cpp
dbscheme: semmlecode.cpp.dbscheme
extractor: cpp

View File

@@ -627,6 +627,20 @@ private predicate sub_lt(
x = int_value(rhs.getRight()) and
k = c - x
)
or
exists(PointerSubInstruction lhs, int c, int x |
compares_lt(cmp, lhs.getAUse(), right, c, isLt, testIsTrue) and
left = lhs.getLeftOperand() and
x = int_value(lhs.getRight()) and
k = c + x
)
or
exists(PointerSubInstruction rhs, int c, int x |
compares_lt(cmp, left, rhs.getAUse(), c, isLt, testIsTrue) and
right = rhs.getLeftOperand() and
x = int_value(rhs.getRight()) and
k = c - x
)
}
// left + x < right + c => left < right + (c-x)
@@ -653,6 +667,26 @@ private predicate add_lt(
) and
k = c + x
)
or
exists(PointerAddInstruction lhs, int c, int x |
compares_lt(cmp, lhs.getAUse(), right, c, isLt, testIsTrue) and
(
left = lhs.getLeftOperand() and x = int_value(lhs.getRight())
or
left = lhs.getRightOperand() and x = int_value(lhs.getLeft())
) and
k = c - x
)
or
exists(PointerAddInstruction rhs, int c, int x |
compares_lt(cmp, left, rhs.getAUse(), c, isLt, testIsTrue) and
(
right = rhs.getLeftOperand() and x = int_value(rhs.getRight())
or
right = rhs.getRightOperand() and x = int_value(rhs.getLeft())
) and
k = c + x
)
}
// left - x == right + c => left == right + (c+x)
@@ -673,6 +707,20 @@ private predicate sub_eq(
x = int_value(rhs.getRight()) and
k = c - x
)
or
exists(PointerSubInstruction lhs, int c, int x |
compares_eq(cmp, lhs.getAUse(), right, c, areEqual, testIsTrue) and
left = lhs.getLeftOperand() and
x = int_value(lhs.getRight()) and
k = c + x
)
or
exists(PointerSubInstruction rhs, int c, int x |
compares_eq(cmp, left, rhs.getAUse(), c, areEqual, testIsTrue) and
right = rhs.getLeftOperand() and
x = int_value(rhs.getRight()) and
k = c - x
)
}
// left + x == right + c => left == right + (c-x)
@@ -699,6 +747,26 @@ private predicate add_eq(
) and
k = c + x
)
or
exists(PointerAddInstruction lhs, int c, int x |
compares_eq(cmp, lhs.getAUse(), right, c, areEqual, testIsTrue) and
(
left = lhs.getLeftOperand() and x = int_value(lhs.getRight())
or
left = lhs.getRightOperand() and x = int_value(lhs.getLeft())
) and
k = c - x
)
or
exists(PointerAddInstruction rhs, int c, int x |
compares_eq(cmp, left, rhs.getAUse(), c, areEqual, testIsTrue) and
(
right = rhs.getLeftOperand() and x = int_value(rhs.getRight())
or
right = rhs.getRightOperand() and x = int_value(rhs.getLeft())
) and
k = c + x
)
}
/** The int value of integer constant expression. */

View File

@@ -114,7 +114,7 @@ signature module StateConfigSig {
* Holds if data flow through `node` is prohibited when the flow state is
* `state`.
*/
predicate isBarrier(Node node, FlowState state);
default predicate isBarrier(Node node, FlowState state) { none() }
/** Holds if data flow into `node` is prohibited. */
default predicate isBarrierIn(Node node) { none() }
@@ -131,7 +131,9 @@ signature module StateConfigSig {
* Holds if data may flow from `node1` to `node2` in addition to the normal data-flow steps.
* This step is only applicable in `state1` and updates the flow state to `state2`.
*/
predicate isAdditionalFlowStep(Node node1, FlowState state1, Node node2, FlowState state2);
default predicate isAdditionalFlowStep(Node node1, FlowState state1, Node node2, FlowState state2) {
none()
}
/**
* Holds if an arbitrary number of implicit read steps of content `c` may be

View File

@@ -254,6 +254,11 @@ module Impl<FullStateConfigSig Config> {
not fullBarrier(node2)
}
pragma[nomagic]
private predicate isUnreachableInCall1(NodeEx n, LocalCallContextSpecificCall cc) {
isUnreachableInCallCached(n.asNode(), cc.getCall())
}
/**
* Holds if data can flow in one local step from `node1` to `node2`.
*/
@@ -460,7 +465,6 @@ module Impl<FullStateConfigSig Config> {
* The Boolean `cc` records whether the node is reached through an
* argument in a call.
*/
pragma[assume_small_delta]
private predicate fwdFlow(NodeEx node, Cc cc) {
sourceNode(node, _) and
if hasSourceCallCtx() then cc = true else cc = false
@@ -570,7 +574,6 @@ module Impl<FullStateConfigSig Config> {
/**
* Holds if `c` is the target of a store in the flow covered by `fwdFlow`.
*/
pragma[assume_small_delta]
pragma[nomagic]
private predicate fwdFlowConsCand(Content c) {
exists(NodeEx mid, NodeEx node |
@@ -1216,7 +1219,6 @@ module Impl<FullStateConfigSig Config> {
fwdFlow1(_, _, _, _, _, _, t0, t, ap, _) and t0 != t
}
pragma[assume_small_delta]
pragma[nomagic]
private predicate fwdFlow0(
NodeEx node, FlowState state, Cc cc, ParamNodeOption summaryCtx, TypOption argT,
@@ -2111,7 +2113,7 @@ module Impl<FullStateConfigSig Config> {
NodeEx node1, FlowState state, NodeEx node2, boolean preservesValue, DataFlowType t,
LocalCallContext cc
) {
not isUnreachableInCallCached(node2.asNode(), cc.(LocalCallContextSpecificCall).getCall()) and
not isUnreachableInCall1(node2, cc) and
(
localFlowEntry(node1, pragma[only_bind_into](state)) and
(
@@ -2126,7 +2128,7 @@ module Impl<FullStateConfigSig Config> {
) and
node1 != node2 and
cc.relevantFor(node1.getEnclosingCallable()) and
not isUnreachableInCallCached(node1.asNode(), cc.(LocalCallContextSpecificCall).getCall())
not isUnreachableInCall1(node1, cc)
or
exists(NodeEx mid |
localFlowStepPlus(node1, pragma[only_bind_into](state), mid, preservesValue, t, cc) and
@@ -2163,10 +2165,8 @@ module Impl<FullStateConfigSig Config> {
preservesValue = false and
t = node2.getDataFlowType() and
callContext.relevantFor(node1.getEnclosingCallable()) and
not exists(DataFlowCall call | call = callContext.(LocalCallContextSpecificCall).getCall() |
isUnreachableInCallCached(node1.asNode(), call) or
isUnreachableInCallCached(node2.asNode(), call)
)
not isUnreachableInCall1(node1, callContext) and
not isUnreachableInCall1(node2, callContext)
}
}
@@ -2706,7 +2706,7 @@ module Impl<FullStateConfigSig Config> {
ParamNodeEx getParamNode() { result = p }
override string toString() { result = p + ": " + ap }
override string toString() { result = p + concat(" : " + ppReprType(t)) + " " + ap }
predicate hasLocationInfo(
string filepath, int startline, int startcolumn, int endline, int endcolumn
@@ -2758,12 +2758,21 @@ module Impl<FullStateConfigSig Config> {
)
}
private predicate forceUnfold(AccessPathApprox apa) {
forceHighPrecision(apa.getHead())
or
exists(Content c2 |
apa = TConsCons(_, _, c2, _) and
forceHighPrecision(c2)
)
}
/**
* Holds with `unfold = false` if a precise head-tail representation of `apa` is
* expected to be expensive. Holds with `unfold = true` otherwise.
*/
private predicate evalUnfold(AccessPathApprox apa, boolean unfold) {
if forceHighPrecision(apa.getHead())
if forceUnfold(apa)
then unfold = true
else
exists(int aps, int nodes, int apLimit, int tupleLimit |
@@ -2777,7 +2786,6 @@ module Impl<FullStateConfigSig Config> {
/**
* Gets the number of `AccessPath`s that correspond to `apa`.
*/
pragma[assume_small_delta]
private int countAps(AccessPathApprox apa) {
evalUnfold(apa, false) and
result = 1 and
@@ -2796,7 +2804,6 @@ module Impl<FullStateConfigSig Config> {
* that it is expanded to a precise head-tail representation.
*/
language[monotonicAggregates]
pragma[assume_small_delta]
private int countPotentialAps(AccessPathApprox apa) {
apa instanceof AccessPathApproxNil and result = 1
or
@@ -2833,7 +2840,6 @@ module Impl<FullStateConfigSig Config> {
}
private newtype TPathNode =
pragma[assume_small_delta]
TPathNodeMid(
NodeEx node, FlowState state, CallContext cc, SummaryCtx sc, DataFlowType t, AccessPath ap
) {
@@ -2918,7 +2924,6 @@ module Impl<FullStateConfigSig Config> {
override AccessPathFrontHead getFront() { result = TFrontHead(head_) }
pragma[assume_small_delta]
override AccessPathApproxCons getApprox() {
result = TConsNil(head_, t) and tail_ = TAccessPathNil()
or
@@ -2927,7 +2932,6 @@ module Impl<FullStateConfigSig Config> {
result = TCons1(head_, this.length())
}
pragma[assume_small_delta]
override int length() { result = 1 + tail_.length() }
private string toStringImpl(boolean needsSuffix) {
@@ -3097,6 +3101,12 @@ module Impl<FullStateConfigSig Config> {
result = " <" + this.(PathNodeMid).getCallContext().toString() + ">"
}
private string ppSummaryCtx() {
this instanceof PathNodeSink and result = ""
or
result = " <" + this.(PathNodeMid).getSummaryCtx().toString() + ">"
}
/** Gets a textual representation of this element. */
string toString() { result = this.getNodeEx().toString() + this.ppType() + this.ppAp() }
@@ -3105,7 +3115,9 @@ module Impl<FullStateConfigSig Config> {
* representation of the call context.
*/
string toStringWithContext() {
result = this.getNodeEx().toString() + this.ppType() + this.ppAp() + this.ppCtx()
result =
this.getNodeEx().toString() + this.ppType() + this.ppAp() + this.ppCtx() +
this.ppSummaryCtx()
}
/**
@@ -3379,7 +3391,6 @@ module Impl<FullStateConfigSig Config> {
* Holds if data may flow from `mid` to `node`. The last step in or out of
* a callable is recorded by `cc`.
*/
pragma[assume_small_delta]
pragma[nomagic]
private predicate pathStep0(
PathNodeMid mid, NodeEx node, FlowState state, CallContext cc, SummaryCtx sc, DataFlowType t,
@@ -3592,7 +3603,6 @@ module Impl<FullStateConfigSig Config> {
)
}
pragma[assume_small_delta]
pragma[nomagic]
private predicate pathThroughCallable0(
DataFlowCall call, PathNodeMid mid, ReturnKindExt kind, FlowState state, CallContext cc,

View File

@@ -187,7 +187,6 @@ private module LambdaFlow {
else any()
}
pragma[assume_small_delta]
pragma[nomagic]
predicate revLambdaFlow0(
DataFlowCall lambdaCall, LambdaCallKind kind, Node node, DataFlowType t, boolean toReturn,
@@ -274,7 +273,6 @@ private module LambdaFlow {
)
}
pragma[assume_small_delta]
pragma[nomagic]
predicate revLambdaFlowOut(
DataFlowCall lambdaCall, LambdaCallKind kind, TReturnPositionSimple pos, DataFlowType t,

View File

@@ -114,7 +114,7 @@ signature module StateConfigSig {
* Holds if data flow through `node` is prohibited when the flow state is
* `state`.
*/
predicate isBarrier(Node node, FlowState state);
default predicate isBarrier(Node node, FlowState state) { none() }
/** Holds if data flow into `node` is prohibited. */
default predicate isBarrierIn(Node node) { none() }
@@ -131,7 +131,9 @@ signature module StateConfigSig {
* Holds if data may flow from `node1` to `node2` in addition to the normal data-flow steps.
* This step is only applicable in `state1` and updates the flow state to `state2`.
*/
predicate isAdditionalFlowStep(Node node1, FlowState state1, Node node2, FlowState state2);
default predicate isAdditionalFlowStep(Node node1, FlowState state1, Node node2, FlowState state2) {
none()
}
/**
* Holds if an arbitrary number of implicit read steps of content `c` may be

View File

@@ -254,6 +254,11 @@ module Impl<FullStateConfigSig Config> {
not fullBarrier(node2)
}
pragma[nomagic]
private predicate isUnreachableInCall1(NodeEx n, LocalCallContextSpecificCall cc) {
isUnreachableInCallCached(n.asNode(), cc.getCall())
}
/**
* Holds if data can flow in one local step from `node1` to `node2`.
*/
@@ -460,7 +465,6 @@ module Impl<FullStateConfigSig Config> {
* The Boolean `cc` records whether the node is reached through an
* argument in a call.
*/
pragma[assume_small_delta]
private predicate fwdFlow(NodeEx node, Cc cc) {
sourceNode(node, _) and
if hasSourceCallCtx() then cc = true else cc = false
@@ -570,7 +574,6 @@ module Impl<FullStateConfigSig Config> {
/**
* Holds if `c` is the target of a store in the flow covered by `fwdFlow`.
*/
pragma[assume_small_delta]
pragma[nomagic]
private predicate fwdFlowConsCand(Content c) {
exists(NodeEx mid, NodeEx node |
@@ -1216,7 +1219,6 @@ module Impl<FullStateConfigSig Config> {
fwdFlow1(_, _, _, _, _, _, t0, t, ap, _) and t0 != t
}
pragma[assume_small_delta]
pragma[nomagic]
private predicate fwdFlow0(
NodeEx node, FlowState state, Cc cc, ParamNodeOption summaryCtx, TypOption argT,
@@ -2111,7 +2113,7 @@ module Impl<FullStateConfigSig Config> {
NodeEx node1, FlowState state, NodeEx node2, boolean preservesValue, DataFlowType t,
LocalCallContext cc
) {
not isUnreachableInCallCached(node2.asNode(), cc.(LocalCallContextSpecificCall).getCall()) and
not isUnreachableInCall1(node2, cc) and
(
localFlowEntry(node1, pragma[only_bind_into](state)) and
(
@@ -2126,7 +2128,7 @@ module Impl<FullStateConfigSig Config> {
) and
node1 != node2 and
cc.relevantFor(node1.getEnclosingCallable()) and
not isUnreachableInCallCached(node1.asNode(), cc.(LocalCallContextSpecificCall).getCall())
not isUnreachableInCall1(node1, cc)
or
exists(NodeEx mid |
localFlowStepPlus(node1, pragma[only_bind_into](state), mid, preservesValue, t, cc) and
@@ -2163,10 +2165,8 @@ module Impl<FullStateConfigSig Config> {
preservesValue = false and
t = node2.getDataFlowType() and
callContext.relevantFor(node1.getEnclosingCallable()) and
not exists(DataFlowCall call | call = callContext.(LocalCallContextSpecificCall).getCall() |
isUnreachableInCallCached(node1.asNode(), call) or
isUnreachableInCallCached(node2.asNode(), call)
)
not isUnreachableInCall1(node1, callContext) and
not isUnreachableInCall1(node2, callContext)
}
}
@@ -2706,7 +2706,7 @@ module Impl<FullStateConfigSig Config> {
ParamNodeEx getParamNode() { result = p }
override string toString() { result = p + ": " + ap }
override string toString() { result = p + concat(" : " + ppReprType(t)) + " " + ap }
predicate hasLocationInfo(
string filepath, int startline, int startcolumn, int endline, int endcolumn
@@ -2758,12 +2758,21 @@ module Impl<FullStateConfigSig Config> {
)
}
private predicate forceUnfold(AccessPathApprox apa) {
forceHighPrecision(apa.getHead())
or
exists(Content c2 |
apa = TConsCons(_, _, c2, _) and
forceHighPrecision(c2)
)
}
/**
* Holds with `unfold = false` if a precise head-tail representation of `apa` is
* expected to be expensive. Holds with `unfold = true` otherwise.
*/
private predicate evalUnfold(AccessPathApprox apa, boolean unfold) {
if forceHighPrecision(apa.getHead())
if forceUnfold(apa)
then unfold = true
else
exists(int aps, int nodes, int apLimit, int tupleLimit |
@@ -2777,7 +2786,6 @@ module Impl<FullStateConfigSig Config> {
/**
* Gets the number of `AccessPath`s that correspond to `apa`.
*/
pragma[assume_small_delta]
private int countAps(AccessPathApprox apa) {
evalUnfold(apa, false) and
result = 1 and
@@ -2796,7 +2804,6 @@ module Impl<FullStateConfigSig Config> {
* that it is expanded to a precise head-tail representation.
*/
language[monotonicAggregates]
pragma[assume_small_delta]
private int countPotentialAps(AccessPathApprox apa) {
apa instanceof AccessPathApproxNil and result = 1
or
@@ -2833,7 +2840,6 @@ module Impl<FullStateConfigSig Config> {
}
private newtype TPathNode =
pragma[assume_small_delta]
TPathNodeMid(
NodeEx node, FlowState state, CallContext cc, SummaryCtx sc, DataFlowType t, AccessPath ap
) {
@@ -2918,7 +2924,6 @@ module Impl<FullStateConfigSig Config> {
override AccessPathFrontHead getFront() { result = TFrontHead(head_) }
pragma[assume_small_delta]
override AccessPathApproxCons getApprox() {
result = TConsNil(head_, t) and tail_ = TAccessPathNil()
or
@@ -2927,7 +2932,6 @@ module Impl<FullStateConfigSig Config> {
result = TCons1(head_, this.length())
}
pragma[assume_small_delta]
override int length() { result = 1 + tail_.length() }
private string toStringImpl(boolean needsSuffix) {
@@ -3097,6 +3101,12 @@ module Impl<FullStateConfigSig Config> {
result = " <" + this.(PathNodeMid).getCallContext().toString() + ">"
}
private string ppSummaryCtx() {
this instanceof PathNodeSink and result = ""
or
result = " <" + this.(PathNodeMid).getSummaryCtx().toString() + ">"
}
/** Gets a textual representation of this element. */
string toString() { result = this.getNodeEx().toString() + this.ppType() + this.ppAp() }
@@ -3105,7 +3115,9 @@ module Impl<FullStateConfigSig Config> {
* representation of the call context.
*/
string toStringWithContext() {
result = this.getNodeEx().toString() + this.ppType() + this.ppAp() + this.ppCtx()
result =
this.getNodeEx().toString() + this.ppType() + this.ppAp() + this.ppCtx() +
this.ppSummaryCtx()
}
/**
@@ -3379,7 +3391,6 @@ module Impl<FullStateConfigSig Config> {
* Holds if data may flow from `mid` to `node`. The last step in or out of
* a callable is recorded by `cc`.
*/
pragma[assume_small_delta]
pragma[nomagic]
private predicate pathStep0(
PathNodeMid mid, NodeEx node, FlowState state, CallContext cc, SummaryCtx sc, DataFlowType t,
@@ -3592,7 +3603,6 @@ module Impl<FullStateConfigSig Config> {
)
}
pragma[assume_small_delta]
pragma[nomagic]
private predicate pathThroughCallable0(
DataFlowCall call, PathNodeMid mid, ReturnKindExt kind, FlowState state, CallContext cc,

View File

@@ -187,7 +187,6 @@ private module LambdaFlow {
else any()
}
pragma[assume_small_delta]
pragma[nomagic]
predicate revLambdaFlow0(
DataFlowCall lambdaCall, LambdaCallKind kind, Node node, DataFlowType t, boolean toReturn,
@@ -274,7 +273,6 @@ private module LambdaFlow {
)
}
pragma[assume_small_delta]
pragma[nomagic]
predicate revLambdaFlowOut(
DataFlowCall lambdaCall, LambdaCallKind kind, TReturnPositionSimple pos, DataFlowType t,

View File

@@ -192,13 +192,13 @@ module ProductFlow {
* Holds if data flow through `node` is prohibited through the first projection of the product
* dataflow graph when the flow state is `state`.
*/
predicate isBarrier1(DataFlow::Node node, FlowState1 state);
default predicate isBarrier1(DataFlow::Node node, FlowState1 state) { none() }
/**
* Holds if data flow through `node` is prohibited through the second projection of the product
* dataflow graph when the flow state is `state`.
*/
predicate isBarrier2(DataFlow::Node node, FlowState2 state);
default predicate isBarrier2(DataFlow::Node node, FlowState2 state) { none() }
/**
* Holds if data flow through `node` is prohibited through the first projection of the product
@@ -237,9 +237,11 @@ module ProductFlow {
*
* This step is only applicable in `state1` and updates the flow state to `state2`.
*/
predicate isAdditionalFlowStep1(
default predicate isAdditionalFlowStep1(
DataFlow::Node node1, FlowState1 state1, DataFlow::Node node2, FlowState1 state2
);
) {
none()
}
/**
* Holds if data may flow from `node1` to `node2` in addition to the normal data-flow steps in
@@ -253,9 +255,11 @@ module ProductFlow {
*
* This step is only applicable in `state1` and updates the flow state to `state2`.
*/
predicate isAdditionalFlowStep2(
default predicate isAdditionalFlowStep2(
DataFlow::Node node1, FlowState2 state1, DataFlow::Node node2, FlowState2 state2
);
) {
none()
}
/**
* Holds if data flow into `node` is prohibited in the first projection of the product
@@ -359,7 +363,6 @@ module ProductFlow {
Config::isSinkPair(node1.getNode(), node1.getState(), node2.getNode(), node2.getState())
}
pragma[assume_small_delta]
pragma[nomagic]
private predicate fwdReachableInterprocEntry(Flow1::PathNode node1, Flow2::PathNode node2) {
isSourcePair(node1, node2)
@@ -396,7 +399,6 @@ module ProductFlow {
fwdIsSuccessorExit(pragma[only_bind_into](mid1), pragma[only_bind_into](mid2), succ1, succ2)
}
pragma[assume_small_delta]
private predicate fwdIsSuccessor(
Flow1::PathNode pred1, Flow2::PathNode pred2, Flow1::PathNode succ1, Flow2::PathNode succ2
) {
@@ -406,7 +408,6 @@ module ProductFlow {
)
}
pragma[assume_small_delta]
pragma[nomagic]
private predicate revReachableInterprocEntry(Flow1::PathNode node1, Flow2::PathNode node2) {
fwdReachableInterprocEntry(node1, node2) and

View File

@@ -588,7 +588,6 @@ private module Cached {
)
}
pragma[assume_small_delta]
private predicate convertsIntoArgumentRev(Instruction instr) {
convertsIntoArgumentFwd(instr) and
(

View File

@@ -176,7 +176,6 @@ private predicate binaryValueNumber0(
)
}
pragma[assume_small_delta]
private predicate binaryValueNumber(
BinaryInstruction instr, IRFunction irFunc, Opcode opcode, TValueNumber leftOperand,
TValueNumber rightOperand
@@ -202,7 +201,6 @@ private predicate pointerArithmeticValueNumber0(
)
}
pragma[assume_small_delta]
private predicate pointerArithmeticValueNumber(
PointerArithmeticInstruction instr, IRFunction irFunc, Opcode opcode, int elementSize,
TValueNumber leftOperand, TValueNumber rightOperand
@@ -249,7 +247,6 @@ private predicate loadTotalOverlapValueNumber0(
)
}
pragma[assume_small_delta]
private predicate loadTotalOverlapValueNumber(
LoadTotalOverlapInstruction instr, IRFunction irFunc, IRType type, TValueNumber memOperand,
TValueNumber operand

View File

@@ -176,7 +176,6 @@ private predicate binaryValueNumber0(
)
}
pragma[assume_small_delta]
private predicate binaryValueNumber(
BinaryInstruction instr, IRFunction irFunc, Opcode opcode, TValueNumber leftOperand,
TValueNumber rightOperand
@@ -202,7 +201,6 @@ private predicate pointerArithmeticValueNumber0(
)
}
pragma[assume_small_delta]
private predicate pointerArithmeticValueNumber(
PointerArithmeticInstruction instr, IRFunction irFunc, Opcode opcode, int elementSize,
TValueNumber leftOperand, TValueNumber rightOperand
@@ -249,7 +247,6 @@ private predicate loadTotalOverlapValueNumber0(
)
}
pragma[assume_small_delta]
private predicate loadTotalOverlapValueNumber(
LoadTotalOverlapInstruction instr, IRFunction irFunc, IRType type, TValueNumber memOperand,
TValueNumber operand

View File

@@ -176,7 +176,6 @@ private predicate binaryValueNumber0(
)
}
pragma[assume_small_delta]
private predicate binaryValueNumber(
BinaryInstruction instr, IRFunction irFunc, Opcode opcode, TValueNumber leftOperand,
TValueNumber rightOperand
@@ -202,7 +201,6 @@ private predicate pointerArithmeticValueNumber0(
)
}
pragma[assume_small_delta]
private predicate pointerArithmeticValueNumber(
PointerArithmeticInstruction instr, IRFunction irFunc, Opcode opcode, int elementSize,
TValueNumber leftOperand, TValueNumber rightOperand
@@ -249,7 +247,6 @@ private predicate loadTotalOverlapValueNumber0(
)
}
pragma[assume_small_delta]
private predicate loadTotalOverlapValueNumber(
LoadTotalOverlapInstruction instr, IRFunction irFunc, IRType type, TValueNumber memOperand,
TValueNumber operand

View File

@@ -877,7 +877,6 @@ module RangeStage<
)
}
pragma[assume_small_delta]
pragma[nomagic]
private predicate boundedPhiRankStep(
SemSsaPhiNode phi, SemBound b, D::Delta delta, boolean upper, boolean fromBackEdge,

View File

@@ -1,3 +1,13 @@
## 0.7.0
### Minor Analysis Improvements
* The `cpp/comparison-with-wider-type` query now correctly handles relational operations on signed operators. As a result the query may find more results.
## 0.6.4
No user-facing changes.
## 0.6.3
### New Queries

View File

@@ -88,14 +88,6 @@ module FlowFromFree<isSinkSig/2 isASink, isExcludedSig/2 isExcluded> {
e = any(StoreInstruction store).getDestinationAddress().getUnconvertedResultExpression()
)
}
predicate isBarrier(DataFlow::Node n, FlowState state) { none() }
predicate isAdditionalFlowStep(
DataFlow::Node n1, FlowState state1, DataFlow::Node n2, FlowState state2
) {
none()
}
}
import DataFlow::GlobalWithState<FlowFromFreeConfig>

View File

@@ -135,18 +135,24 @@ module ParameterSinks {
}
}
predicate isUse(DataFlow::Node n, Expr e) {
isUse0(n, e)
or
exists(CallInstruction call, int i, InitializeParameterInstruction init |
n.asOperand().getDef().getUnconvertedResultExpression() = e and
init = ParameterSinks::getAnAlwaysDereferencedParameter() and
call.getArgumentOperand(i) = n.asOperand() and
init.hasIndex(i) and
init.getEnclosingFunction() = call.getStaticCallTarget()
)
module IsUse {
private import semmle.code.cpp.ir.dataflow.internal.DataFlowImplCommon
predicate isUse(DataFlow::Node n, Expr e) {
isUse0(n, e)
or
exists(CallInstruction call, InitializeParameterInstruction init |
n.asOperand().getDef().getUnconvertedResultExpression() = e and
pragma[only_bind_into](init) = ParameterSinks::getAnAlwaysDereferencedParameter() and
viableParamArg(call, DataFlow::instructionNode(init), n) and
pragma[only_bind_out](init.getEnclosingFunction()) =
pragma[only_bind_out](call.getStaticCallTarget())
)
}
}
import IsUse
/**
* `dealloc1` is a deallocation expression, `e` is an expression that dereferences a
* pointer, and the `(dealloc1, e)` pair should be excluded by the `FlowFromFree` library.

View File

@@ -44,14 +44,6 @@ module CastToPointerArithFlowConfig implements DataFlow::StateConfigSig {
) and
getFullyConvertedType(node) = state
}
predicate isBarrier(DataFlow::Node node, FlowState state) { none() }
predicate isAdditionalFlowStep(
DataFlow::Node node1, FlowState state1, DataFlow::Node node2, FlowState state2
) {
none()
}
}
/**

View File

@@ -72,6 +72,11 @@ VariableAccess commonException() {
or
result.getParent() instanceof BuiltInOperation
or
// Ignore any uninitialized use that is explicitly cast to void and
// is an expression statement.
result.getActualType() instanceof VoidType and
result.getParent() instanceof ExprStmt
or
// Finally, exclude functions that contain assembly blocks. It's
// anyone's guess what happens in those.
containsInlineAssembly(result.getEnclosingFunction())

View File

@@ -134,8 +134,6 @@ module ExecTaintConfig implements DataFlow::StateConfigSig {
predicate isBarrier(DataFlow::Node node) { isBarrierImpl(node) }
predicate isBarrier(DataFlow::Node node, FlowState state) { none() }
predicate isBarrierOut(DataFlow::Node node) {
isSink(node, _) // Prevent duplicates along a call chain, since `shellCommand` will include wrappers
}

View File

@@ -5,7 +5,7 @@
* @kind path-problem
* @problem.severity error
* @security-severity 9.3
* @precision low
* @precision medium
* @id cpp/overrun-write
* @tags reliability
* security
@@ -118,12 +118,6 @@ module ValidState {
state = [false, true]
}
predicate isBarrier(DataFlow::Node node, FlowState state) { none() }
predicate isBarrierOut(DataFlow::Node node) {
node = any(DataFlow::SsaPhiNode phi).getAnInput(true)
}
predicate isAdditionalFlowStep(
DataFlow::Node node1, FlowState state1, DataFlow::Node node2, FlowState state2
) {
@@ -252,20 +246,10 @@ module StringSizeConfig implements ProductFlow::StateConfigSig {
)
}
predicate isBarrier1(DataFlow::Node node, FlowState1 state) { none() }
predicate isBarrier2(DataFlow::Node node, FlowState2 state) { none() }
predicate isBarrierOut2(DataFlow::Node node) {
node = any(DataFlow::SsaPhiNode phi).getAnInput(true)
}
predicate isAdditionalFlowStep1(
DataFlow::Node node1, FlowState1 state1, DataFlow::Node node2, FlowState1 state2
) {
none()
}
predicate isAdditionalFlowStep2(
DataFlow::Node node1, FlowState2 state1, DataFlow::Node node2, FlowState2 state2
) {

View File

@@ -45,13 +45,20 @@ Element friendlyLoc(Expr e) {
not e instanceof Access and not e instanceof Call and result = e
}
int getComparisonSizeAdjustment(Expr e) {
if e.getType().(IntegralType).isSigned() then result = 1 else result = 0
}
from Loop l, RelationalOperation rel, VariableAccess small, Expr large
where
small = rel.getLesserOperand() and
large = rel.getGreaterOperand() and
rel = l.getCondition().getAChild*() and
forall(Expr conv | conv = large.getConversion*() |
upperBound(conv).log2() > getComparisonSize(small) * 8
// We adjust the comparison size in the case of a signed integer type.
// This is to exclude the sign bit from the comparison that determines if the small type's size is sufficient to hold
// the value of the larger type determined with range analysis.
upperBound(conv).log2() > (getComparisonSize(small) * 8 - getComparisonSizeAdjustment(small))
) and
// Ignore cases where the smaller type is int or larger
// These are still bugs, but you should need a very large string or array to

View File

@@ -0,0 +1,4 @@
---
category: minorAnalysis
---
* The `cpp/uninitialized-local` query now excludes uninitialized uses that are explicitly cast to void and are expression statements. As a result, the query will report less false positives.

View File

@@ -0,0 +1,3 @@
## 0.6.4
No user-facing changes.

View File

@@ -0,0 +1,5 @@
## 0.7.0
### Minor Analysis Improvements
* The `cpp/comparison-with-wider-type` query now correctly handles relational operations on signed operators. As a result the query may find more results.

View File

@@ -1,2 +1,2 @@
---
lastReleaseVersion: 0.6.3
lastReleaseVersion: 0.7.0

View File

@@ -168,8 +168,6 @@ module ArrayAddressToDerefConfig implements DataFlow::StateConfigSig {
)
}
predicate isBarrier(DataFlow::Node node, FlowState state) { none() }
predicate isBarrierIn(DataFlow::Node node) { isSource(node, _) }
predicate isBarrierOut(DataFlow::Node node) { isSink(node, _) }

View File

@@ -196,8 +196,6 @@ module AllocToInvalidPointerConfig implements ProductFlow::StateConfigSig {
isSinkImpl(_, sink1, sink2, state2)
}
predicate isBarrier1(DataFlow::Node node, FlowState1 state) { none() }
predicate isBarrier2(DataFlow::Node node, FlowState2 state) {
node = Barrier2::getABarrierNode(state)
}
@@ -207,18 +205,6 @@ module AllocToInvalidPointerConfig implements ProductFlow::StateConfigSig {
predicate isBarrierOut2(DataFlow::Node node) {
node = any(DataFlow::SsaPhiNode phi).getAnInput(true)
}
predicate isAdditionalFlowStep1(
DataFlow::Node node1, FlowState1 state1, DataFlow::Node node2, FlowState1 state2
) {
none()
}
predicate isAdditionalFlowStep2(
DataFlow::Node node1, FlowState2 state1, DataFlow::Node node2, FlowState2 state2
) {
none()
}
}
module AllocToInvalidPointerFlow = ProductFlow::GlobalWithState<AllocToInvalidPointerConfig>;

View File

@@ -1,5 +1,5 @@
name: codeql/cpp-queries
version: 0.6.4-dev
version: 0.7.1-dev
groups:
- cpp
- queries

View File

@@ -35,22 +35,46 @@ edges
| test.cpp:136:9:136:16 | ... += ... | test.cpp:138:13:138:15 | arr |
| test.cpp:143:18:143:21 | asdf | test.cpp:134:25:134:27 | arr |
| test.cpp:143:18:143:21 | asdf | test.cpp:143:18:143:21 | asdf |
| test.cpp:148:23:148:28 | buffer | test.cpp:150:5:150:11 | access to array |
| test.cpp:148:23:148:28 | buffer | test.cpp:151:5:151:11 | access to array |
| test.cpp:159:25:159:29 | array | test.cpp:161:5:161:10 | access to array |
| test.cpp:159:25:159:29 | array | test.cpp:162:5:162:10 | access to array |
| test.cpp:175:30:175:30 | p | test.cpp:191:27:191:30 | access to array |
| test.cpp:175:30:175:30 | p | test.cpp:191:27:191:30 | access to array |
| test.cpp:204:14:204:20 | buffer3 | test.cpp:175:30:175:30 | p |
| test.cpp:204:14:204:20 | buffer3 | test.cpp:204:14:204:20 | buffer3 |
| test.cpp:207:35:207:35 | p | test.cpp:208:14:208:14 | p |
| test.cpp:208:14:208:14 | p | test.cpp:175:30:175:30 | p |
| test.cpp:213:19:213:25 | buffer1 | test.cpp:207:35:207:35 | p |
| test.cpp:213:19:213:25 | buffer1 | test.cpp:213:19:213:25 | buffer1 |
| test.cpp:216:19:216:25 | buffer2 | test.cpp:207:35:207:35 | p |
| test.cpp:216:19:216:25 | buffer2 | test.cpp:216:19:216:25 | buffer2 |
| test.cpp:219:19:219:25 | buffer3 | test.cpp:207:35:207:35 | p |
| test.cpp:219:19:219:25 | buffer3 | test.cpp:219:19:219:25 | buffer3 |
| test.cpp:146:26:146:26 | p indirection | test.cpp:148:6:148:9 | * ... |
| test.cpp:156:12:156:14 | buf | test.cpp:156:12:156:18 | ... + ... |
| test.cpp:156:12:156:18 | ... + ... | test.cpp:158:17:158:18 | & ... indirection |
| test.cpp:158:17:158:18 | & ... indirection | test.cpp:146:26:146:26 | p indirection |
| test.cpp:218:23:218:28 | buffer | test.cpp:220:5:220:11 | access to array |
| test.cpp:218:23:218:28 | buffer | test.cpp:221:5:221:11 | access to array |
| test.cpp:229:25:229:29 | array | test.cpp:231:5:231:10 | access to array |
| test.cpp:229:25:229:29 | array | test.cpp:232:5:232:10 | access to array |
| test.cpp:245:30:245:30 | p | test.cpp:261:27:261:30 | access to array |
| test.cpp:245:30:245:30 | p | test.cpp:261:27:261:30 | access to array |
| test.cpp:274:14:274:20 | buffer3 | test.cpp:245:30:245:30 | p |
| test.cpp:274:14:274:20 | buffer3 | test.cpp:274:14:274:20 | buffer3 |
| test.cpp:277:35:277:35 | p | test.cpp:278:14:278:14 | p |
| test.cpp:278:14:278:14 | p | test.cpp:245:30:245:30 | p |
| test.cpp:283:19:283:25 | buffer1 | test.cpp:277:35:277:35 | p |
| test.cpp:283:19:283:25 | buffer1 | test.cpp:283:19:283:25 | buffer1 |
| test.cpp:286:19:286:25 | buffer2 | test.cpp:277:35:277:35 | p |
| test.cpp:286:19:286:25 | buffer2 | test.cpp:286:19:286:25 | buffer2 |
| test.cpp:289:19:289:25 | buffer3 | test.cpp:277:35:277:35 | p |
| test.cpp:289:19:289:25 | buffer3 | test.cpp:289:19:289:25 | buffer3 |
| test.cpp:292:25:292:27 | arr | test.cpp:299:16:299:21 | access to array |
| test.cpp:292:25:292:27 | arr | test.cpp:299:16:299:21 | access to array |
| test.cpp:306:20:306:23 | arr1 | test.cpp:292:25:292:27 | arr |
| test.cpp:306:20:306:23 | arr1 | test.cpp:306:20:306:23 | arr1 |
| test.cpp:309:20:309:23 | arr2 | test.cpp:292:25:292:27 | arr |
| test.cpp:309:20:309:23 | arr2 | test.cpp:309:20:309:23 | arr2 |
| test.cpp:319:19:319:22 | temp | test.cpp:319:19:319:27 | ... + ... |
| test.cpp:319:19:319:22 | temp | test.cpp:324:23:324:32 | ... + ... |
| test.cpp:319:19:319:27 | ... + ... | test.cpp:325:24:325:26 | end |
| test.cpp:322:19:322:22 | temp | test.cpp:322:19:322:27 | ... + ... |
| test.cpp:322:19:322:22 | temp | test.cpp:324:23:324:32 | ... + ... |
| test.cpp:322:19:322:27 | ... + ... | test.cpp:325:24:325:26 | end |
| test.cpp:324:23:324:26 | temp | test.cpp:324:23:324:32 | ... + ... |
| test.cpp:324:23:324:32 | ... + ... | test.cpp:325:15:325:19 | temp2 |
| test.cpp:351:9:351:11 | arr | test.cpp:351:9:351:14 | access to array |
| test.cpp:351:9:351:11 | arr | test.cpp:351:18:351:25 | access to array |
| test.cpp:351:18:351:20 | arr | test.cpp:351:9:351:14 | access to array |
| test.cpp:351:18:351:20 | arr | test.cpp:351:18:351:25 | access to array |
| test.cpp:351:29:351:31 | arr | test.cpp:351:9:351:14 | access to array |
| test.cpp:351:29:351:31 | arr | test.cpp:351:18:351:25 | access to array |
nodes
| test.cpp:34:5:34:24 | access to array | semmle.label | access to array |
| test.cpp:34:10:34:12 | buf | semmle.label | buf |
@@ -103,25 +127,51 @@ nodes
| test.cpp:138:13:138:15 | arr | semmle.label | arr |
| test.cpp:143:18:143:21 | asdf | semmle.label | asdf |
| test.cpp:143:18:143:21 | asdf | semmle.label | asdf |
| test.cpp:148:23:148:28 | buffer | semmle.label | buffer |
| test.cpp:150:5:150:11 | access to array | semmle.label | access to array |
| test.cpp:151:5:151:11 | access to array | semmle.label | access to array |
| test.cpp:159:25:159:29 | array | semmle.label | array |
| test.cpp:161:5:161:10 | access to array | semmle.label | access to array |
| test.cpp:162:5:162:10 | access to array | semmle.label | access to array |
| test.cpp:175:30:175:30 | p | semmle.label | p |
| test.cpp:175:30:175:30 | p | semmle.label | p |
| test.cpp:191:27:191:30 | access to array | semmle.label | access to array |
| test.cpp:204:14:204:20 | buffer3 | semmle.label | buffer3 |
| test.cpp:204:14:204:20 | buffer3 | semmle.label | buffer3 |
| test.cpp:207:35:207:35 | p | semmle.label | p |
| test.cpp:208:14:208:14 | p | semmle.label | p |
| test.cpp:213:19:213:25 | buffer1 | semmle.label | buffer1 |
| test.cpp:213:19:213:25 | buffer1 | semmle.label | buffer1 |
| test.cpp:216:19:216:25 | buffer2 | semmle.label | buffer2 |
| test.cpp:216:19:216:25 | buffer2 | semmle.label | buffer2 |
| test.cpp:219:19:219:25 | buffer3 | semmle.label | buffer3 |
| test.cpp:219:19:219:25 | buffer3 | semmle.label | buffer3 |
| test.cpp:146:26:146:26 | p indirection | semmle.label | p indirection |
| test.cpp:148:6:148:9 | * ... | semmle.label | * ... |
| test.cpp:156:12:156:14 | buf | semmle.label | buf |
| test.cpp:156:12:156:18 | ... + ... | semmle.label | ... + ... |
| test.cpp:158:17:158:18 | & ... indirection | semmle.label | & ... indirection |
| test.cpp:218:23:218:28 | buffer | semmle.label | buffer |
| test.cpp:220:5:220:11 | access to array | semmle.label | access to array |
| test.cpp:221:5:221:11 | access to array | semmle.label | access to array |
| test.cpp:229:25:229:29 | array | semmle.label | array |
| test.cpp:231:5:231:10 | access to array | semmle.label | access to array |
| test.cpp:232:5:232:10 | access to array | semmle.label | access to array |
| test.cpp:245:30:245:30 | p | semmle.label | p |
| test.cpp:245:30:245:30 | p | semmle.label | p |
| test.cpp:261:27:261:30 | access to array | semmle.label | access to array |
| test.cpp:274:14:274:20 | buffer3 | semmle.label | buffer3 |
| test.cpp:274:14:274:20 | buffer3 | semmle.label | buffer3 |
| test.cpp:277:35:277:35 | p | semmle.label | p |
| test.cpp:278:14:278:14 | p | semmle.label | p |
| test.cpp:283:19:283:25 | buffer1 | semmle.label | buffer1 |
| test.cpp:283:19:283:25 | buffer1 | semmle.label | buffer1 |
| test.cpp:286:19:286:25 | buffer2 | semmle.label | buffer2 |
| test.cpp:286:19:286:25 | buffer2 | semmle.label | buffer2 |
| test.cpp:289:19:289:25 | buffer3 | semmle.label | buffer3 |
| test.cpp:289:19:289:25 | buffer3 | semmle.label | buffer3 |
| test.cpp:292:25:292:27 | arr | semmle.label | arr |
| test.cpp:292:25:292:27 | arr | semmle.label | arr |
| test.cpp:299:16:299:21 | access to array | semmle.label | access to array |
| test.cpp:306:20:306:23 | arr1 | semmle.label | arr1 |
| test.cpp:306:20:306:23 | arr1 | semmle.label | arr1 |
| test.cpp:309:20:309:23 | arr2 | semmle.label | arr2 |
| test.cpp:309:20:309:23 | arr2 | semmle.label | arr2 |
| test.cpp:319:19:319:22 | temp | semmle.label | temp |
| test.cpp:319:19:319:27 | ... + ... | semmle.label | ... + ... |
| test.cpp:322:19:322:22 | temp | semmle.label | temp |
| test.cpp:322:19:322:27 | ... + ... | semmle.label | ... + ... |
| test.cpp:324:23:324:26 | temp | semmle.label | temp |
| test.cpp:324:23:324:32 | ... + ... | semmle.label | ... + ... |
| test.cpp:325:15:325:19 | temp2 | semmle.label | temp2 |
| test.cpp:325:24:325:26 | end | semmle.label | end |
| test.cpp:325:24:325:26 | end | semmle.label | end |
| test.cpp:351:9:351:11 | arr | semmle.label | arr |
| test.cpp:351:9:351:14 | access to array | semmle.label | access to array |
| test.cpp:351:18:351:20 | arr | semmle.label | arr |
| test.cpp:351:18:351:25 | access to array | semmle.label | access to array |
| test.cpp:351:29:351:31 | arr | semmle.label | arr |
subpaths
#select
| test.cpp:35:5:35:22 | PointerAdd: access to array | test.cpp:35:10:35:12 | buf | test.cpp:35:5:35:22 | access to array | This pointer arithmetic may have an off-by-1 error allowing it to overrun $@ at this $@. | test.cpp:15:9:15:11 | buf | buf | test.cpp:35:5:35:26 | Store: ... = ... | write |
@@ -136,6 +186,14 @@ subpaths
| test.cpp:88:5:88:27 | PointerAdd: access to array | test.cpp:85:34:85:36 | buf | test.cpp:88:5:88:27 | access to array | This pointer arithmetic may have an off-by-1 error allowing it to overrun $@ at this $@. | test.cpp:15:9:15:11 | buf | buf | test.cpp:88:5:88:31 | Store: ... = ... | write |
| test.cpp:128:9:128:14 | PointerAdd: access to array | test.cpp:128:9:128:11 | arr | test.cpp:128:9:128:14 | access to array | This pointer arithmetic may have an off-by-1 error allowing it to overrun $@ at this $@. | test.cpp:125:11:125:13 | arr | arr | test.cpp:128:9:128:18 | Store: ... = ... | write |
| test.cpp:136:9:136:16 | PointerAdd: ... += ... | test.cpp:143:18:143:21 | asdf | test.cpp:138:13:138:15 | arr | This pointer arithmetic may have an off-by-2 error allowing it to overrun $@ at this $@. | test.cpp:142:10:142:13 | asdf | asdf | test.cpp:138:12:138:15 | Load: * ... | read |
| test.cpp:151:5:151:11 | PointerAdd: access to array | test.cpp:148:23:148:28 | buffer | test.cpp:151:5:151:11 | access to array | This pointer arithmetic may have an off-by-1 error allowing it to overrun $@ at this $@. | test.cpp:147:19:147:24 | buffer | buffer | test.cpp:151:5:151:15 | Store: ... = ... | write |
| test.cpp:162:5:162:10 | PointerAdd: access to array | test.cpp:159:25:159:29 | array | test.cpp:162:5:162:10 | access to array | This pointer arithmetic may have an off-by-1 error allowing it to overrun $@ at this $@. | test.cpp:158:10:158:14 | array | array | test.cpp:162:5:162:19 | Store: ... = ... | write |
| test.cpp:191:27:191:30 | PointerAdd: access to array | test.cpp:216:19:216:25 | buffer2 | test.cpp:191:27:191:30 | access to array | This pointer arithmetic may have an off-by-1 error allowing it to overrun $@ at this $@. | test.cpp:215:19:215:25 | buffer2 | buffer2 | test.cpp:191:27:191:30 | Load: access to array | read |
| test.cpp:156:12:156:18 | PointerAdd: ... + ... | test.cpp:156:12:156:14 | buf | test.cpp:148:6:148:9 | * ... | This pointer arithmetic may have an off-by-1 error allowing it to overrun $@ at this $@. | test.cpp:154:7:154:9 | buf | buf | test.cpp:147:3:147:13 | Store: ... = ... | write |
| test.cpp:221:5:221:11 | PointerAdd: access to array | test.cpp:218:23:218:28 | buffer | test.cpp:221:5:221:11 | access to array | This pointer arithmetic may have an off-by-1 error allowing it to overrun $@ at this $@. | test.cpp:217:19:217:24 | buffer | buffer | test.cpp:221:5:221:15 | Store: ... = ... | write |
| test.cpp:232:5:232:10 | PointerAdd: access to array | test.cpp:229:25:229:29 | array | test.cpp:232:5:232:10 | access to array | This pointer arithmetic may have an off-by-1 error allowing it to overrun $@ at this $@. | test.cpp:228:10:228:14 | array | array | test.cpp:232:5:232:19 | Store: ... = ... | write |
| test.cpp:261:27:261:30 | PointerAdd: access to array | test.cpp:286:19:286:25 | buffer2 | test.cpp:261:27:261:30 | access to array | This pointer arithmetic may have an off-by-1 error allowing it to overrun $@ at this $@. | test.cpp:285:19:285:25 | buffer2 | buffer2 | test.cpp:261:27:261:30 | Load: access to array | read |
| test.cpp:299:16:299:21 | PointerAdd: access to array | test.cpp:309:20:309:23 | arr2 | test.cpp:299:16:299:21 | access to array | This pointer arithmetic may have an off-by-1014 error allowing it to overrun $@ at this $@. | test.cpp:308:9:308:12 | arr2 | arr2 | test.cpp:299:16:299:21 | Load: access to array | read |
| test.cpp:322:19:322:27 | PointerAdd: ... + ... | test.cpp:322:19:322:22 | temp | test.cpp:325:24:325:26 | end | This pointer arithmetic may have an off-by-1 error allowing it to overrun $@ at this $@. | test.cpp:314:10:314:13 | temp | temp | test.cpp:330:13:330:24 | Store: ... = ... | write |
| test.cpp:322:19:322:27 | PointerAdd: ... + ... | test.cpp:322:19:322:22 | temp | test.cpp:325:24:325:26 | end | This pointer arithmetic may have an off-by-1 error allowing it to overrun $@ at this $@. | test.cpp:314:10:314:13 | temp | temp | test.cpp:331:13:331:24 | Store: ... = ... | write |
| test.cpp:322:19:322:27 | PointerAdd: ... + ... | test.cpp:322:19:322:22 | temp | test.cpp:325:24:325:26 | end | This pointer arithmetic may have an off-by-1 error allowing it to overrun $@ at this $@. | test.cpp:314:10:314:13 | temp | temp | test.cpp:333:13:333:24 | Store: ... = ... | write |
| test.cpp:351:18:351:25 | PointerAdd: access to array | test.cpp:351:9:351:11 | arr | test.cpp:351:18:351:25 | access to array | This pointer arithmetic may have an off-by-1 error allowing it to overrun $@ at this $@. | test.cpp:348:9:348:11 | arr | arr | test.cpp:351:18:351:25 | Load: access to array | read |
| test.cpp:351:18:351:25 | PointerAdd: access to array | test.cpp:351:18:351:20 | arr | test.cpp:351:18:351:25 | access to array | This pointer arithmetic may have an off-by-1 error allowing it to overrun $@ at this $@. | test.cpp:348:9:348:11 | arr | arr | test.cpp:351:18:351:25 | Load: access to array | read |
| test.cpp:351:18:351:25 | PointerAdd: access to array | test.cpp:351:29:351:31 | arr | test.cpp:351:18:351:25 | access to array | This pointer arithmetic may have an off-by-1 error allowing it to overrun $@ at this $@. | test.cpp:348:9:348:11 | arr | arr | test.cpp:351:18:351:25 | Load: access to array | read |

View File

@@ -143,6 +143,76 @@ void testStrncmp1() {
testStrncmp2(asdf);
}
void countdownBuf1(int **p) {
*--(*p) = 1; // GOOD [FALSE POSITIVE]
*--(*p) = 2; // GOOD
*--(*p) = 3; // GOOD
*--(*p) = 4; // GOOD
}
void countdownBuf2() {
int buf[4];
int *x = buf + 4;
countdownBuf1(&x);
}
int access(int *p) {
return p[0];
}
// unrolled loop style seen in crypto code.
int countdownLength1(int *p, int len) {
while(len > 0) {
access(p);
p[1] = 1;
p[2] = 2;
p[3] = 3;
p[4] = 4;
p[5] = 5;
p[6] = 6; // BAD [FALSE NEGATIVE]
p[7] = 7; // BAD [FALSE NEGATIVE]
p += 8;
len -= 8;
}
return p[5];
}
int callCountdownLength() {
int buf[6];
return countdownLength1(buf, 6);
}
int countdownLength2() {
int buf[6];
int len = 6;
int *p = buf;
if(len % 8) {
return -1;
}
while(len > 0) {
p[0] = 0;
p[1] = 1;
p[2] = 2;
p[3] = 3;
p[4] = 4;
p[5] = 5;
p[6] = 6; // GOOD
p[7] = 7; // GOOD
p += 8;
len -= 8;
}
return p[5];
}
void pointer_size_larger_than_array_element_size() {
unsigned char buffer[100]; // getByteSize() = 100
int *ptr = (int *)buffer; // pai.getElementSize() will be sizeof(int) = 4 -> size = 25
@@ -218,3 +288,67 @@ void test_call_use2() {
unsigned char buffer3[3];
call_call_use(buffer3,3);
}
int guardingCallee(int *arr, int size) {
if (size > MAX_SIZE) {
return -1;
}
int sum;
for (int i = 0; i < size; i++) {
sum += arr[i]; // GOOD [FALSE POSITIVE] - guarded by size
}
return sum;
}
int guardingCaller() {
int arr1[MAX_SIZE];
guardingCallee(arr1, MAX_SIZE);
int arr2[10];
guardingCallee(arr2, 10);
}
// simplified md5 padding
void correlatedCondition(int num) {
char temp[64];
char *end;
if(num < 64) {
if (num < 56) {
end = temp + 56;
}
else if (num < 64) {
end = temp + 64; // GOOD [FALSE POSITVE]
}
char *temp2 = temp + num;
while(temp2 != end) {
*temp2 = 0;
temp2++;
}
if(num < 56) {
temp2[0] = 0;
temp2[1] = 0;
// ...
temp2[7] = 0;
}
}
}
int positiveRange(int x) {
if (x < 40) {
return -1;
}
if (x > 1024) {
return -1;
}
int offset = (unsigned char)(x + 7)/8;
int arr[128];
for(int i=127-offset; i>= 0; i--) {
arr[i] = arr[i+1] + arr[i+offset]; // GOOD [FALSE POSITIVE]
}
return arr[0];
}

View File

@@ -151,3 +151,19 @@ void test5(int x) {
void test6(int x, int y) {
return x && y;
}
int ptr_test(int *x, int *y) {
if (x == y + 42) {
}
if (x == y - 42) {
}
if (x < y + 42) {
}
if (x < y - 42) {
}
return 0;
}

View File

@@ -30,6 +30,10 @@ astGuards
| test.c:152:10:152:10 | x |
| test.c:152:10:152:15 | ... && ... |
| test.c:152:15:152:15 | y |
| test.c:156:9:156:19 | ... == ... |
| test.c:159:9:159:19 | ... == ... |
| test.c:162:9:162:18 | ... < ... |
| test.c:165:9:165:18 | ... < ... |
| test.cpp:18:8:18:10 | call to get |
| test.cpp:31:7:31:13 | ... == ... |
| test.cpp:42:13:42:20 | call to getABool |
@@ -122,6 +126,38 @@ astGuardsCompare
| 109 | y < 0+0 when ... < ... is true |
| 109 | y >= 0+0 when ... < ... is false |
| 109 | y >= 0+0 when ... \|\| ... is false |
| 156 | ... + ... != x+0 when ... == ... is false |
| 156 | ... + ... == x+0 when ... == ... is true |
| 156 | x != ... + ...+0 when ... == ... is false |
| 156 | x != y+42 when ... == ... is false |
| 156 | x == ... + ...+0 when ... == ... is true |
| 156 | x == y+42 when ... == ... is true |
| 156 | y != x+-42 when ... == ... is false |
| 156 | y == x+-42 when ... == ... is true |
| 159 | ... - ... != x+0 when ... == ... is false |
| 159 | ... - ... == x+0 when ... == ... is true |
| 159 | x != ... - ...+0 when ... == ... is false |
| 159 | x != y+-42 when ... == ... is false |
| 159 | x == ... - ...+0 when ... == ... is true |
| 159 | x == y+-42 when ... == ... is true |
| 159 | y != x+42 when ... == ... is false |
| 159 | y == x+42 when ... == ... is true |
| 162 | ... + ... < x+1 when ... < ... is false |
| 162 | ... + ... >= x+1 when ... < ... is true |
| 162 | x < ... + ...+0 when ... < ... is true |
| 162 | x < y+42 when ... < ... is true |
| 162 | x >= ... + ...+0 when ... < ... is false |
| 162 | x >= y+42 when ... < ... is false |
| 162 | y < x+-41 when ... < ... is false |
| 162 | y >= x+-41 when ... < ... is true |
| 165 | ... - ... < x+1 when ... < ... is false |
| 165 | ... - ... >= x+1 when ... < ... is true |
| 165 | x < ... - ...+0 when ... < ... is true |
| 165 | x < y+-42 when ... < ... is true |
| 165 | x >= ... - ...+0 when ... < ... is false |
| 165 | x >= y+-42 when ... < ... is false |
| 165 | y < x+43 when ... < ... is false |
| 165 | y >= x+43 when ... < ... is true |
astGuardsControl
| test.c:7:9:7:13 | ... > ... | false | 10 | 11 |
| test.c:7:9:7:13 | ... > ... | true | 7 | 9 |
@@ -208,6 +244,10 @@ astGuardsControl
| test.c:152:10:152:10 | x | true | 152 | 152 |
| test.c:152:10:152:15 | ... && ... | true | 151 | 152 |
| test.c:152:15:152:15 | y | true | 151 | 152 |
| test.c:156:9:156:19 | ... == ... | true | 156 | 157 |
| test.c:159:9:159:19 | ... == ... | true | 159 | 160 |
| test.c:162:9:162:18 | ... < ... | true | 162 | 163 |
| test.c:165:9:165:18 | ... < ... | true | 165 | 166 |
| test.cpp:18:8:18:10 | call to get | true | 19 | 19 |
| test.cpp:31:7:31:13 | ... == ... | false | 30 | 30 |
| test.cpp:31:7:31:13 | ... == ... | false | 34 | 34 |
@@ -364,6 +404,22 @@ astGuardsEnsure
| test.c:109:9:109:23 | ... \|\| ... | test.c:109:23:109:23 | 0 | < | test.c:109:19:109:19 | y | 1 | 113 | 113 |
| test.c:109:19:109:23 | ... < ... | test.c:109:19:109:19 | y | >= | test.c:109:23:109:23 | 0 | 0 | 113 | 113 |
| test.c:109:19:109:23 | ... < ... | test.c:109:23:109:23 | 0 | < | test.c:109:19:109:19 | y | 1 | 113 | 113 |
| test.c:156:9:156:19 | ... == ... | test.c:156:9:156:9 | x | == | test.c:156:14:156:14 | y | 42 | 156 | 157 |
| test.c:156:9:156:19 | ... == ... | test.c:156:9:156:9 | x | == | test.c:156:14:156:19 | ... + ... | 0 | 156 | 157 |
| test.c:156:9:156:19 | ... == ... | test.c:156:14:156:14 | y | == | test.c:156:9:156:9 | x | -42 | 156 | 157 |
| test.c:156:9:156:19 | ... == ... | test.c:156:14:156:19 | ... + ... | == | test.c:156:9:156:9 | x | 0 | 156 | 157 |
| test.c:159:9:159:19 | ... == ... | test.c:159:9:159:9 | x | == | test.c:159:14:159:14 | y | -42 | 159 | 160 |
| test.c:159:9:159:19 | ... == ... | test.c:159:9:159:9 | x | == | test.c:159:14:159:19 | ... - ... | 0 | 159 | 160 |
| test.c:159:9:159:19 | ... == ... | test.c:159:14:159:14 | y | == | test.c:159:9:159:9 | x | 42 | 159 | 160 |
| test.c:159:9:159:19 | ... == ... | test.c:159:14:159:19 | ... - ... | == | test.c:159:9:159:9 | x | 0 | 159 | 160 |
| test.c:162:9:162:18 | ... < ... | test.c:162:9:162:9 | x | < | test.c:162:13:162:13 | y | 42 | 162 | 163 |
| test.c:162:9:162:18 | ... < ... | test.c:162:9:162:9 | x | < | test.c:162:13:162:18 | ... + ... | 0 | 162 | 163 |
| test.c:162:9:162:18 | ... < ... | test.c:162:13:162:13 | y | >= | test.c:162:9:162:9 | x | -41 | 162 | 163 |
| test.c:162:9:162:18 | ... < ... | test.c:162:13:162:18 | ... + ... | >= | test.c:162:9:162:9 | x | 1 | 162 | 163 |
| test.c:165:9:165:18 | ... < ... | test.c:165:9:165:9 | x | < | test.c:165:13:165:13 | y | -42 | 165 | 166 |
| test.c:165:9:165:18 | ... < ... | test.c:165:9:165:9 | x | < | test.c:165:13:165:18 | ... - ... | 0 | 165 | 166 |
| test.c:165:9:165:18 | ... < ... | test.c:165:13:165:13 | y | >= | test.c:165:9:165:9 | x | 43 | 165 | 166 |
| test.c:165:9:165:18 | ... < ... | test.c:165:13:165:18 | ... - ... | >= | test.c:165:9:165:9 | x | 1 | 165 | 166 |
| test.cpp:31:7:31:13 | ... == ... | test.cpp:31:7:31:7 | x | != | test.cpp:31:12:31:13 | - ... | 0 | 30 | 30 |
| test.cpp:31:7:31:13 | ... == ... | test.cpp:31:7:31:7 | x | != | test.cpp:31:12:31:13 | - ... | 0 | 34 | 34 |
| test.cpp:31:7:31:13 | ... == ... | test.cpp:31:7:31:7 | x | == | test.cpp:31:12:31:13 | - ... | 0 | 30 | 30 |
@@ -397,6 +453,10 @@ irGuards
| test.c:146:8:146:8 | Load: x |
| test.c:152:10:152:10 | Load: x |
| test.c:152:15:152:15 | Load: y |
| test.c:156:9:156:19 | CompareEQ: ... == ... |
| test.c:159:9:159:19 | CompareEQ: ... == ... |
| test.c:162:9:162:18 | CompareLT: ... < ... |
| test.c:165:9:165:18 | CompareLT: ... < ... |
| test.cpp:18:8:18:12 | CompareNE: (bool)... |
| test.cpp:31:7:31:13 | CompareEQ: ... == ... |
| test.cpp:42:13:42:20 | Call: call to getABool |
@@ -473,6 +533,38 @@ irGuardsCompare
| 109 | x == 0+0 when CompareEQ: ... == ... is true |
| 109 | y < 0+0 when CompareLT: ... < ... is true |
| 109 | y >= 0+0 when CompareLT: ... < ... is false |
| 156 | ... + ... != x+0 when CompareEQ: ... == ... is false |
| 156 | ... + ... == x+0 when CompareEQ: ... == ... is true |
| 156 | x != ... + ...+0 when CompareEQ: ... == ... is false |
| 156 | x != y+42 when CompareEQ: ... == ... is false |
| 156 | x == ... + ...+0 when CompareEQ: ... == ... is true |
| 156 | x == y+42 when CompareEQ: ... == ... is true |
| 156 | y != x+-42 when CompareEQ: ... == ... is false |
| 156 | y == x+-42 when CompareEQ: ... == ... is true |
| 159 | ... - ... != x+0 when CompareEQ: ... == ... is false |
| 159 | ... - ... == x+0 when CompareEQ: ... == ... is true |
| 159 | x != ... - ...+0 when CompareEQ: ... == ... is false |
| 159 | x != y+-42 when CompareEQ: ... == ... is false |
| 159 | x == ... - ...+0 when CompareEQ: ... == ... is true |
| 159 | x == y+-42 when CompareEQ: ... == ... is true |
| 159 | y != x+42 when CompareEQ: ... == ... is false |
| 159 | y == x+42 when CompareEQ: ... == ... is true |
| 162 | ... + ... < x+1 when CompareLT: ... < ... is false |
| 162 | ... + ... >= x+1 when CompareLT: ... < ... is true |
| 162 | x < ... + ...+0 when CompareLT: ... < ... is true |
| 162 | x < y+42 when CompareLT: ... < ... is true |
| 162 | x >= ... + ...+0 when CompareLT: ... < ... is false |
| 162 | x >= y+42 when CompareLT: ... < ... is false |
| 162 | y < x+-41 when CompareLT: ... < ... is false |
| 162 | y >= x+-41 when CompareLT: ... < ... is true |
| 165 | ... - ... < x+1 when CompareLT: ... < ... is false |
| 165 | ... - ... >= x+1 when CompareLT: ... < ... is true |
| 165 | x < ... - ...+0 when CompareLT: ... < ... is true |
| 165 | x < y+-42 when CompareLT: ... < ... is true |
| 165 | x >= ... - ...+0 when CompareLT: ... < ... is false |
| 165 | x >= y+-42 when CompareLT: ... < ... is false |
| 165 | y < x+43 when CompareLT: ... < ... is false |
| 165 | y >= x+43 when CompareLT: ... < ... is true |
irGuardsControl
| test.c:7:9:7:13 | CompareGT: ... > ... | false | 11 | 11 |
| test.c:7:9:7:13 | CompareGT: ... > ... | true | 8 | 8 |
@@ -551,6 +643,10 @@ irGuardsControl
| test.c:146:8:146:8 | Load: x | false | 147 | 147 |
| test.c:152:10:152:10 | Load: x | true | 152 | 152 |
| test.c:152:15:152:15 | Load: y | true | 152 | 152 |
| test.c:156:9:156:19 | CompareEQ: ... == ... | true | 156 | 157 |
| test.c:159:9:159:19 | CompareEQ: ... == ... | true | 159 | 160 |
| test.c:162:9:162:18 | CompareLT: ... < ... | true | 162 | 163 |
| test.c:165:9:165:18 | CompareLT: ... < ... | true | 165 | 166 |
| test.cpp:18:8:18:12 | CompareNE: (bool)... | true | 19 | 19 |
| test.cpp:31:7:31:13 | CompareEQ: ... == ... | false | 34 | 34 |
| test.cpp:31:7:31:13 | CompareEQ: ... == ... | true | 30 | 30 |
@@ -690,6 +786,22 @@ irGuardsEnsure
| test.c:109:9:109:14 | CompareEQ: ... == ... | test.c:109:14:109:14 | Constant: 0 | != | test.c:109:9:109:9 | Load: x | 0 | 113 | 113 |
| test.c:109:19:109:23 | CompareLT: ... < ... | test.c:109:19:109:19 | Load: y | >= | test.c:109:23:109:23 | Constant: (long)... | 0 | 113 | 113 |
| test.c:109:19:109:23 | CompareLT: ... < ... | test.c:109:23:109:23 | Constant: (long)... | < | test.c:109:19:109:19 | Load: y | 1 | 113 | 113 |
| test.c:156:9:156:19 | CompareEQ: ... == ... | test.c:156:9:156:9 | Load: x | == | test.c:156:14:156:14 | Load: y | 42 | 156 | 157 |
| test.c:156:9:156:19 | CompareEQ: ... == ... | test.c:156:9:156:9 | Load: x | == | test.c:156:14:156:19 | PointerAdd: ... + ... | 0 | 156 | 157 |
| test.c:156:9:156:19 | CompareEQ: ... == ... | test.c:156:14:156:14 | Load: y | == | test.c:156:9:156:9 | Load: x | -42 | 156 | 157 |
| test.c:156:9:156:19 | CompareEQ: ... == ... | test.c:156:14:156:19 | PointerAdd: ... + ... | == | test.c:156:9:156:9 | Load: x | 0 | 156 | 157 |
| test.c:159:9:159:19 | CompareEQ: ... == ... | test.c:159:9:159:9 | Load: x | == | test.c:159:14:159:14 | Load: y | -42 | 159 | 160 |
| test.c:159:9:159:19 | CompareEQ: ... == ... | test.c:159:9:159:9 | Load: x | == | test.c:159:14:159:19 | PointerSub: ... - ... | 0 | 159 | 160 |
| test.c:159:9:159:19 | CompareEQ: ... == ... | test.c:159:14:159:14 | Load: y | == | test.c:159:9:159:9 | Load: x | 42 | 159 | 160 |
| test.c:159:9:159:19 | CompareEQ: ... == ... | test.c:159:14:159:19 | PointerSub: ... - ... | == | test.c:159:9:159:9 | Load: x | 0 | 159 | 160 |
| test.c:162:9:162:18 | CompareLT: ... < ... | test.c:162:9:162:9 | Load: x | < | test.c:162:13:162:13 | Load: y | 42 | 162 | 163 |
| test.c:162:9:162:18 | CompareLT: ... < ... | test.c:162:9:162:9 | Load: x | < | test.c:162:13:162:18 | PointerAdd: ... + ... | 0 | 162 | 163 |
| test.c:162:9:162:18 | CompareLT: ... < ... | test.c:162:13:162:13 | Load: y | >= | test.c:162:9:162:9 | Load: x | -41 | 162 | 163 |
| test.c:162:9:162:18 | CompareLT: ... < ... | test.c:162:13:162:18 | PointerAdd: ... + ... | >= | test.c:162:9:162:9 | Load: x | 1 | 162 | 163 |
| test.c:165:9:165:18 | CompareLT: ... < ... | test.c:165:9:165:9 | Load: x | < | test.c:165:13:165:13 | Load: y | -42 | 165 | 166 |
| test.c:165:9:165:18 | CompareLT: ... < ... | test.c:165:9:165:9 | Load: x | < | test.c:165:13:165:18 | PointerSub: ... - ... | 0 | 165 | 166 |
| test.c:165:9:165:18 | CompareLT: ... < ... | test.c:165:13:165:13 | Load: y | >= | test.c:165:9:165:9 | Load: x | 43 | 165 | 166 |
| test.c:165:9:165:18 | CompareLT: ... < ... | test.c:165:13:165:18 | PointerSub: ... - ... | >= | test.c:165:9:165:9 | Load: x | 1 | 165 | 166 |
| test.cpp:18:8:18:12 | CompareNE: (bool)... | test.cpp:18:8:18:10 | Call: call to get | != | test.cpp:18:8:18:12 | Constant: (bool)... | 0 | 19 | 19 |
| test.cpp:18:8:18:12 | CompareNE: (bool)... | test.cpp:18:8:18:12 | Constant: (bool)... | != | test.cpp:18:8:18:10 | Call: call to get | 0 | 19 | 19 |
| test.cpp:31:7:31:13 | CompareEQ: ... == ... | test.cpp:31:7:31:7 | Load: x | != | test.cpp:31:12:31:13 | Constant: - ... | 0 | 34 | 34 |

View File

@@ -14462,6 +14462,140 @@ ir.cpp:
# 1911| getExpr(): [VariableAccess] x
# 1911| Type = [IntType] int
# 1911| ValueCategory = prvalue(load)
# 1914| [TopLevelFunction] int static_function(int)
# 1914| <params>:
# 1914| getParameter(0): [Parameter] x
# 1914| Type = [IntType] int
# 1914| getEntryPoint(): [BlockStmt] { ... }
# 1915| getStmt(0): [ReturnStmt] return ...
# 1915| getExpr(): [VariableAccess] x
# 1915| Type = [IntType] int
# 1915| ValueCategory = prvalue(load)
# 1918| [TopLevelFunction] void test_static_functions_with_assignments()
# 1918| <params>:
# 1918| getEntryPoint(): [BlockStmt] { ... }
# 1919| getStmt(0): [DeclStmt] declaration
# 1919| getDeclarationEntry(0): [VariableDeclarationEntry] definition of c
# 1919| Type = [Class] C
# 1919| getVariable().getInitializer(): [Initializer] initializer for c
# 1919| getExpr(): [ConstructorCall] call to C
# 1919| Type = [VoidType] void
# 1919| ValueCategory = prvalue
# 1920| getStmt(1): [DeclStmt] declaration
# 1920| getDeclarationEntry(0): [VariableDeclarationEntry] definition of x
# 1920| Type = [IntType] int
# 1921| getStmt(2): [ExprStmt] ExprStmt
# 1921| getExpr(): [AssignExpr] ... = ...
# 1921| Type = [IntType] int
# 1921| ValueCategory = lvalue
# 1921| getLValue(): [VariableAccess] x
# 1921| Type = [IntType] int
# 1921| ValueCategory = lvalue
# 1921| getRValue(): [FunctionCall] call to StaticMemberFunction
# 1921| Type = [IntType] int
# 1921| ValueCategory = prvalue
# 1921| getQualifier(): [VariableAccess] c
# 1921| Type = [Class] C
# 1921| ValueCategory = lvalue
# 1921| getArgument(0): [Literal] 10
# 1921| Type = [IntType] int
# 1921| Value = [Literal] 10
# 1921| ValueCategory = prvalue
# 1922| getStmt(3): [DeclStmt] declaration
# 1922| getDeclarationEntry(0): [VariableDeclarationEntry] definition of y
# 1922| Type = [IntType] int
# 1923| getStmt(4): [ExprStmt] ExprStmt
# 1923| getExpr(): [AssignExpr] ... = ...
# 1923| Type = [IntType] int
# 1923| ValueCategory = lvalue
# 1923| getLValue(): [VariableAccess] y
# 1923| Type = [IntType] int
# 1923| ValueCategory = lvalue
# 1923| getRValue(): [FunctionCall] call to StaticMemberFunction
# 1923| Type = [IntType] int
# 1923| ValueCategory = prvalue
# 1923| getArgument(0): [Literal] 10
# 1923| Type = [IntType] int
# 1923| Value = [Literal] 10
# 1923| ValueCategory = prvalue
# 1924| getStmt(5): [DeclStmt] declaration
# 1924| getDeclarationEntry(0): [VariableDeclarationEntry] definition of z
# 1924| Type = [IntType] int
# 1925| getStmt(6): [ExprStmt] ExprStmt
# 1925| getExpr(): [AssignExpr] ... = ...
# 1925| Type = [IntType] int
# 1925| ValueCategory = lvalue
# 1925| getLValue(): [VariableAccess] z
# 1925| Type = [IntType] int
# 1925| ValueCategory = lvalue
# 1925| getRValue(): [FunctionCall] call to static_function
# 1925| Type = [IntType] int
# 1925| ValueCategory = prvalue
# 1925| getArgument(0): [Literal] 10
# 1925| Type = [IntType] int
# 1925| Value = [Literal] 10
# 1925| ValueCategory = prvalue
# 1926| getStmt(7): [ReturnStmt] return ...
# 1928| [TopLevelFunction] void test_double_assign()
# 1928| <params>:
# 1928| getEntryPoint(): [BlockStmt] { ... }
# 1929| getStmt(0): [DeclStmt] declaration
# 1929| getDeclarationEntry(0): [VariableDeclarationEntry] definition of i
# 1929| Type = [IntType] int
# 1929| getDeclarationEntry(1): [VariableDeclarationEntry] definition of j
# 1929| Type = [IntType] int
# 1930| getStmt(1): [ExprStmt] ExprStmt
# 1930| getExpr(): [AssignExpr] ... = ...
# 1930| Type = [IntType] int
# 1930| ValueCategory = lvalue
# 1930| getLValue(): [VariableAccess] i
# 1930| Type = [IntType] int
# 1930| ValueCategory = lvalue
# 1930| getRValue(): [AssignExpr] ... = ...
# 1930| Type = [IntType] int
# 1930| ValueCategory = prvalue
# 1930| getLValue(): [VariableAccess] j
# 1930| Type = [IntType] int
# 1930| ValueCategory = lvalue
# 1930| getRValue(): [Literal] 40
# 1930| Type = [IntType] int
# 1930| Value = [Literal] 40
# 1930| ValueCategory = prvalue
# 1931| getStmt(2): [ReturnStmt] return ...
# 1933| [TopLevelFunction] void test_assign_with_assign_operation()
# 1933| <params>:
# 1933| getEntryPoint(): [BlockStmt] { ... }
# 1934| getStmt(0): [DeclStmt] declaration
# 1934| getDeclarationEntry(0): [VariableDeclarationEntry] definition of i
# 1934| Type = [IntType] int
# 1934| getDeclarationEntry(1): [VariableDeclarationEntry] definition of j
# 1934| Type = [IntType] int
# 1934| getVariable().getInitializer(): [Initializer] initializer for j
# 1934| getExpr(): [Literal] 0
# 1934| Type = [IntType] int
# 1934| Value = [Literal] 0
# 1934| ValueCategory = prvalue
# 1935| getStmt(1): [ExprStmt] ExprStmt
# 1935| getExpr(): [AssignExpr] ... = ...
# 1935| Type = [IntType] int
# 1935| ValueCategory = lvalue
# 1935| getLValue(): [VariableAccess] i
# 1935| Type = [IntType] int
# 1935| ValueCategory = lvalue
# 1935| getRValue(): [AssignAddExpr] ... += ...
# 1935| Type = [IntType] int
# 1935| ValueCategory = prvalue
# 1935| getLValue(): [VariableAccess] j
# 1935| Type = [IntType] int
# 1935| ValueCategory = lvalue
# 1935| getRValue(): [Literal] 40
# 1935| Type = [IntType] int
# 1935| Value = [Literal] 40
# 1935| ValueCategory = prvalue
# 1935| getRValue().getFullyConverted(): [ParenthesisExpr] (...)
# 1935| Type = [IntType] int
# 1935| ValueCategory = prvalue
# 1936| getStmt(2): [ReturnStmt] return ...
perf-regression.cpp:
# 4| [CopyAssignmentOperator] Big& Big::operator=(Big const&)
# 4| <params>:

View File

@@ -1911,4 +1911,28 @@ int noreturnTest2(int x) {
return x;
}
int static_function(int x) {
return x;
}
void test_static_functions_with_assignments() {
C c;
int x;
x = c.StaticMemberFunction(10);
int y;
y = C::StaticMemberFunction(10);
int z;
z = static_function(10);
}
void test_double_assign() {
int i, j;
i = j = 40;
}
void test_assign_with_assign_operation() {
int i, j = 0;
i = (j += 40);
}
// semmle-extractor-options: -std=c++17 --clang

View File

@@ -8821,6 +8821,76 @@
| ir.cpp:1911:12:1911:12 | Address | &:r1911_2 |
| ir.cpp:1911:12:1911:12 | Load | m1907_6 |
| ir.cpp:1911:12:1911:12 | StoreValue | r1911_3 |
| ir.cpp:1914:5:1914:19 | Address | &:r1914_7 |
| ir.cpp:1914:5:1914:19 | ChiPartial | partial:m1914_3 |
| ir.cpp:1914:5:1914:19 | ChiTotal | total:m1914_2 |
| ir.cpp:1914:5:1914:19 | Load | m1915_4 |
| ir.cpp:1914:5:1914:19 | SideEffect | m1914_3 |
| ir.cpp:1914:25:1914:25 | Address | &:r1914_5 |
| ir.cpp:1915:5:1915:13 | Address | &:r1915_1 |
| ir.cpp:1915:12:1915:12 | Address | &:r1915_2 |
| ir.cpp:1915:12:1915:12 | Load | m1914_6 |
| ir.cpp:1915:12:1915:12 | StoreValue | r1915_3 |
| ir.cpp:1918:6:1918:43 | ChiPartial | partial:m1918_3 |
| ir.cpp:1918:6:1918:43 | ChiTotal | total:m1918_2 |
| ir.cpp:1918:6:1918:43 | SideEffect | ~m1925_5 |
| ir.cpp:1919:7:1919:7 | Address | &:r1919_1 |
| ir.cpp:1919:7:1919:7 | Address | &:r1919_1 |
| ir.cpp:1919:7:1919:7 | Arg(this) | this:r1919_1 |
| ir.cpp:1919:7:1919:7 | CallTarget | func:r1919_3 |
| ir.cpp:1919:7:1919:7 | ChiPartial | partial:m1919_5 |
| ir.cpp:1919:7:1919:7 | ChiPartial | partial:m1919_7 |
| ir.cpp:1919:7:1919:7 | ChiTotal | total:m1918_4 |
| ir.cpp:1919:7:1919:7 | ChiTotal | total:m1919_2 |
| ir.cpp:1919:7:1919:7 | SideEffect | ~m1918_4 |
| ir.cpp:1920:9:1920:9 | Address | &:r1920_1 |
| ir.cpp:1921:5:1921:5 | Address | &:r1921_7 |
| ir.cpp:1921:11:1921:30 | CallTarget | func:r1921_2 |
| ir.cpp:1921:11:1921:30 | ChiPartial | partial:m1921_5 |
| ir.cpp:1921:11:1921:30 | ChiTotal | total:m1919_6 |
| ir.cpp:1921:11:1921:30 | SideEffect | ~m1919_6 |
| ir.cpp:1921:11:1921:30 | StoreValue | r1921_4 |
| ir.cpp:1921:32:1921:33 | Arg(0) | 0:r1921_3 |
| ir.cpp:1922:9:1922:9 | Address | &:r1922_1 |
| ir.cpp:1923:5:1923:5 | Address | &:r1923_6 |
| ir.cpp:1923:9:1923:31 | CallTarget | func:r1923_1 |
| ir.cpp:1923:9:1923:31 | ChiPartial | partial:m1923_4 |
| ir.cpp:1923:9:1923:31 | ChiTotal | total:m1921_6 |
| ir.cpp:1923:9:1923:31 | SideEffect | ~m1921_6 |
| ir.cpp:1923:9:1923:31 | StoreValue | r1923_3 |
| ir.cpp:1923:33:1923:34 | Arg(0) | 0:r1923_2 |
| ir.cpp:1924:9:1924:9 | Address | &:r1924_1 |
| ir.cpp:1925:5:1925:5 | Address | &:r1925_6 |
| ir.cpp:1925:9:1925:23 | CallTarget | func:r1925_1 |
| ir.cpp:1925:9:1925:23 | ChiPartial | partial:m1925_4 |
| ir.cpp:1925:9:1925:23 | ChiTotal | total:m1923_5 |
| ir.cpp:1925:9:1925:23 | SideEffect | ~m1923_5 |
| ir.cpp:1925:9:1925:23 | StoreValue | r1925_3 |
| ir.cpp:1925:25:1925:26 | Arg(0) | 0:r1925_2 |
| ir.cpp:1928:6:1928:23 | ChiPartial | partial:m1928_3 |
| ir.cpp:1928:6:1928:23 | ChiTotal | total:m1928_2 |
| ir.cpp:1928:6:1928:23 | SideEffect | m1928_3 |
| ir.cpp:1929:7:1929:7 | Address | &:r1929_1 |
| ir.cpp:1929:10:1929:10 | Address | &:r1929_3 |
| ir.cpp:1930:3:1930:3 | Address | &:r1930_5 |
| ir.cpp:1930:7:1930:7 | Address | &:r1930_2 |
| ir.cpp:1930:7:1930:12 | StoreValue | r1930_4 |
| ir.cpp:1930:11:1930:12 | StoreValue | r1930_1 |
| ir.cpp:1930:11:1930:12 | Unary | r1930_1 |
| ir.cpp:1933:6:1933:38 | ChiPartial | partial:m1933_3 |
| ir.cpp:1933:6:1933:38 | ChiTotal | total:m1933_2 |
| ir.cpp:1933:6:1933:38 | SideEffect | m1933_3 |
| ir.cpp:1934:7:1934:7 | Address | &:r1934_1 |
| ir.cpp:1934:10:1934:10 | Address | &:r1934_3 |
| ir.cpp:1934:13:1934:14 | StoreValue | r1934_4 |
| ir.cpp:1935:3:1935:3 | Address | &:r1935_6 |
| ir.cpp:1935:8:1935:8 | Address | &:r1935_2 |
| ir.cpp:1935:8:1935:8 | Address | &:r1935_2 |
| ir.cpp:1935:8:1935:8 | Left | r1935_3 |
| ir.cpp:1935:8:1935:8 | Load | m1934_5 |
| ir.cpp:1935:8:1935:14 | StoreValue | r1935_4 |
| ir.cpp:1935:8:1935:14 | StoreValue | r1935_4 |
| ir.cpp:1935:13:1935:14 | Right | r1935_1 |
| perf-regression.cpp:6:3:6:5 | Address | &:r6_5 |
| perf-regression.cpp:6:3:6:5 | Address | &:r6_5 |
| perf-regression.cpp:6:3:6:5 | Address | &:r6_7 |

View File

@@ -10167,6 +10167,105 @@ ir.cpp:
# 1907| v1907_9(void) = AliasedUse : ~m?
# 1907| v1907_10(void) = ExitFunction :
# 1914| int static_function(int)
# 1914| Block 0
# 1914| v1914_1(void) = EnterFunction :
# 1914| mu1914_2(unknown) = AliasedDefinition :
# 1914| mu1914_3(unknown) = InitializeNonLocal :
# 1914| r1914_4(glval<int>) = VariableAddress[x] :
# 1914| mu1914_5(int) = InitializeParameter[x] : &:r1914_4
# 1915| r1915_1(glval<int>) = VariableAddress[#return] :
# 1915| r1915_2(glval<int>) = VariableAddress[x] :
# 1915| r1915_3(int) = Load[x] : &:r1915_2, ~m?
# 1915| mu1915_4(int) = Store[#return] : &:r1915_1, r1915_3
# 1914| r1914_6(glval<int>) = VariableAddress[#return] :
# 1914| v1914_7(void) = ReturnValue : &:r1914_6, ~m?
# 1914| v1914_8(void) = AliasedUse : ~m?
# 1914| v1914_9(void) = ExitFunction :
# 1918| void test_static_functions_with_assignments()
# 1918| Block 0
# 1918| v1918_1(void) = EnterFunction :
# 1918| mu1918_2(unknown) = AliasedDefinition :
# 1918| mu1918_3(unknown) = InitializeNonLocal :
# 1919| r1919_1(glval<C>) = VariableAddress[c] :
# 1919| mu1919_2(C) = Uninitialized[c] : &:r1919_1
# 1919| r1919_3(glval<unknown>) = FunctionAddress[C] :
# 1919| v1919_4(void) = Call[C] : func:r1919_3, this:r1919_1
# 1919| mu1919_5(unknown) = ^CallSideEffect : ~m?
# 1919| mu1919_6(C) = ^IndirectMayWriteSideEffect[-1] : &:r1919_1
# 1920| r1920_1(glval<int>) = VariableAddress[x] :
# 1920| mu1920_2(int) = Uninitialized[x] : &:r1920_1
# 1921| r1921_1(glval<C>) = VariableAddress[c] :
# 1921| r1921_2(glval<unknown>) = FunctionAddress[StaticMemberFunction] :
# 1921| r1921_3(int) = Constant[10] :
# 1921| r1921_4(int) = Call[StaticMemberFunction] : func:r1921_2, 0:r1921_3
# 1921| mu1921_5(unknown) = ^CallSideEffect : ~m?
# 1921| r1921_6(glval<int>) = VariableAddress[x] :
# 1921| mu1921_7(int) = Store[x] : &:r1921_6, r1921_4
# 1922| r1922_1(glval<int>) = VariableAddress[y] :
# 1922| mu1922_2(int) = Uninitialized[y] : &:r1922_1
# 1923| r1923_1(glval<unknown>) = FunctionAddress[StaticMemberFunction] :
# 1923| r1923_2(int) = Constant[10] :
# 1923| r1923_3(int) = Call[StaticMemberFunction] : func:r1923_1, 0:r1923_2
# 1923| mu1923_4(unknown) = ^CallSideEffect : ~m?
# 1923| r1923_5(glval<int>) = VariableAddress[y] :
# 1923| mu1923_6(int) = Store[y] : &:r1923_5, r1923_3
# 1924| r1924_1(glval<int>) = VariableAddress[z] :
# 1924| mu1924_2(int) = Uninitialized[z] : &:r1924_1
# 1925| r1925_1(glval<unknown>) = FunctionAddress[static_function] :
# 1925| r1925_2(int) = Constant[10] :
# 1925| r1925_3(int) = Call[static_function] : func:r1925_1, 0:r1925_2
# 1925| mu1925_4(unknown) = ^CallSideEffect : ~m?
# 1925| r1925_5(glval<int>) = VariableAddress[z] :
# 1925| mu1925_6(int) = Store[z] : &:r1925_5, r1925_3
# 1926| v1926_1(void) = NoOp :
# 1918| v1918_4(void) = ReturnVoid :
# 1918| v1918_5(void) = AliasedUse : ~m?
# 1918| v1918_6(void) = ExitFunction :
# 1928| void test_double_assign()
# 1928| Block 0
# 1928| v1928_1(void) = EnterFunction :
# 1928| mu1928_2(unknown) = AliasedDefinition :
# 1928| mu1928_3(unknown) = InitializeNonLocal :
# 1929| r1929_1(glval<int>) = VariableAddress[i] :
# 1929| mu1929_2(int) = Uninitialized[i] : &:r1929_1
# 1929| r1929_3(glval<int>) = VariableAddress[j] :
# 1929| mu1929_4(int) = Uninitialized[j] : &:r1929_3
# 1930| r1930_1(int) = Constant[40] :
# 1930| r1930_2(glval<int>) = VariableAddress[j] :
# 1930| mu1930_3(int) = Store[j] : &:r1930_2, r1930_1
# 1930| r1930_4(int) = CopyValue : r1930_1
# 1930| r1930_5(glval<int>) = VariableAddress[i] :
# 1930| mu1930_6(int) = Store[i] : &:r1930_5, r1930_4
# 1931| v1931_1(void) = NoOp :
# 1928| v1928_4(void) = ReturnVoid :
# 1928| v1928_5(void) = AliasedUse : ~m?
# 1928| v1928_6(void) = ExitFunction :
# 1933| void test_assign_with_assign_operation()
# 1933| Block 0
# 1933| v1933_1(void) = EnterFunction :
# 1933| mu1933_2(unknown) = AliasedDefinition :
# 1933| mu1933_3(unknown) = InitializeNonLocal :
# 1934| r1934_1(glval<int>) = VariableAddress[i] :
# 1934| mu1934_2(int) = Uninitialized[i] : &:r1934_1
# 1934| r1934_3(glval<int>) = VariableAddress[j] :
# 1934| r1934_4(int) = Constant[0] :
# 1934| mu1934_5(int) = Store[j] : &:r1934_3, r1934_4
# 1935| r1935_1(int) = Constant[40] :
# 1935| r1935_2(glval<int>) = VariableAddress[j] :
# 1935| r1935_3(int) = Load[j] : &:r1935_2, ~m?
# 1935| r1935_4(int) = Add : r1935_3, r1935_1
# 1935| mu1935_5(int) = Store[j] : &:r1935_2, r1935_4
# 1935| r1935_6(glval<int>) = VariableAddress[i] :
# 1935| mu1935_7(int) = Store[i] : &:r1935_6, r1935_4
# 1936| v1936_1(void) = NoOp :
# 1933| v1933_4(void) = ReturnVoid :
# 1933| v1933_5(void) = AliasedUse : ~m?
# 1933| v1933_6(void) = ExitFunction :
perf-regression.cpp:
# 6| void Big::Big()
# 6| Block 0

View File

@@ -75,6 +75,7 @@ edges
| test.cpp:214:24:214:24 | p | test.cpp:216:10:216:10 | p |
| test.cpp:220:43:220:48 | call to malloc | test.cpp:222:15:222:20 | buffer |
| test.cpp:222:15:222:20 | buffer | test.cpp:214:24:214:24 | p |
| test.cpp:228:43:228:48 | call to malloc | test.cpp:232:10:232:15 | buffer |
| test.cpp:235:40:235:45 | buffer | test.cpp:236:5:236:26 | ... = ... |
| test.cpp:236:5:236:26 | ... = ... | test.cpp:236:12:236:17 | p_str indirection [post update] [string] |
| test.cpp:241:27:241:32 | call to malloc | test.cpp:242:22:242:27 | buffer |
@@ -85,6 +86,7 @@ edges
| test.cpp:243:12:243:14 | str indirection [string] | test.cpp:243:16:243:21 | string indirection |
| test.cpp:243:16:243:21 | string indirection | test.cpp:243:12:243:21 | string |
| test.cpp:249:20:249:27 | call to my_alloc | test.cpp:250:12:250:12 | p |
| test.cpp:256:17:256:22 | call to malloc | test.cpp:257:12:257:12 | p |
| test.cpp:262:22:262:27 | call to malloc | test.cpp:266:12:266:12 | p |
| test.cpp:264:20:264:25 | call to malloc | test.cpp:266:12:266:12 | p |
nodes
@@ -153,6 +155,8 @@ nodes
| test.cpp:216:10:216:10 | p | semmle.label | p |
| test.cpp:220:43:220:48 | call to malloc | semmle.label | call to malloc |
| test.cpp:222:15:222:20 | buffer | semmle.label | buffer |
| test.cpp:228:43:228:48 | call to malloc | semmle.label | call to malloc |
| test.cpp:232:10:232:15 | buffer | semmle.label | buffer |
| test.cpp:235:40:235:45 | buffer | semmle.label | buffer |
| test.cpp:236:5:236:26 | ... = ... | semmle.label | ... = ... |
| test.cpp:236:12:236:17 | p_str indirection [post update] [string] | semmle.label | p_str indirection [post update] [string] |
@@ -164,6 +168,8 @@ nodes
| test.cpp:243:16:243:21 | string indirection | semmle.label | string indirection |
| test.cpp:249:20:249:27 | call to my_alloc | semmle.label | call to my_alloc |
| test.cpp:250:12:250:12 | p | semmle.label | p |
| test.cpp:256:17:256:22 | call to malloc | semmle.label | call to malloc |
| test.cpp:257:12:257:12 | p | semmle.label | p |
| test.cpp:262:22:262:27 | call to malloc | semmle.label | call to malloc |
| test.cpp:264:20:264:25 | call to malloc | semmle.label | call to malloc |
| test.cpp:266:12:266:12 | p | semmle.label | p |

View File

@@ -1,6 +1,7 @@
| test.c:4:14:4:18 | ... < ... | Comparison between $@ of type char and $@ of wider type int. | test.c:3:7:3:7 | c | c | test.c:2:17:2:17 | x | x |
| test.c:9:14:9:18 | ... > ... | Comparison between $@ of type char and $@ of wider type int. | test.c:8:7:8:7 | c | c | test.c:7:17:7:17 | x | x |
| test.c:14:14:14:18 | ... < ... | Comparison between $@ of type short and $@ of wider type int. | test.c:13:8:13:8 | s | s | test.c:12:17:12:17 | x | x |
| test.c:42:15:42:29 | ... < ... | Comparison between $@ of type short and $@ of wider type int. | test.c:41:9:41:10 | s1 | s1 | test.c:42:20:42:29 | 65535 | 65535 |
| test.c:65:14:65:18 | ... < ... | Comparison between $@ of type short and $@ of wider type int. | test.c:64:8:64:8 | s | s | test.c:63:17:63:17 | x | x |
| test.c:87:14:87:18 | ... < ... | Comparison between $@ of type unsigned char and $@ of wider type unsigned int. | test.c:83:16:83:16 | c | c | test.c:84:15:84:15 | x | x |
| test.c:91:14:91:23 | ... < ... | Comparison between $@ of type unsigned char and $@ of wider type int. | test.c:83:16:83:16 | c | c | test.c:91:18:91:23 | 65280 | 65280 |
@@ -13,3 +14,4 @@
| test.c:107:14:107:26 | ... < ... | Comparison between $@ of type unsigned char and $@ of wider type unsigned int. | test.c:83:16:83:16 | c | c | test.c:107:19:107:25 | ... >> ... | ... >> ... |
| test.c:128:15:128:21 | ... < ... | Comparison between $@ of type unsigned char and $@ of wider type unsigned int. | test.c:121:16:121:17 | uc | uc | test.c:123:19:123:20 | sz | sz |
| test.c:139:15:139:21 | ... < ... | Comparison between $@ of type unsigned char and $@ of wider type unsigned int. | test.c:121:16:121:17 | uc | uc | test.c:123:19:123:20 | sz | sz |
| test.c:156:9:156:14 | ... < ... | Comparison between $@ of type short and $@ of wider type int. | test.c:150:8:150:8 | s | s | test.c:151:6:151:7 | sx | sx |

View File

@@ -39,7 +39,7 @@ void test5 () {
void test6() {
short s1;
for (s1 = 0; s1 < 0x0000ffff; s1++) {}
for (s1 = 0; s1 < 0x0000ffff; s1++) {} // BAD
}
void test7(long long l) {
@@ -145,3 +145,22 @@ void test13() {
sz = (unsigned)sx & (unsigned)sy;
for (uc = 0; uc < sz; uc++) {} // GOOD
}
void test14() {
short s = 0;
int sx = 0x7FFF + 1;
// BAD: 's' is compared with a value of a wider type.
// 's' overflows before reaching 'sx',
// causing an infinite loop
while (s < sx) {
s += 1;
}
unsigned int ux = 0;
// GOOD: 'ux' has a type at least as wide as 'max_get'
while (ux < sx) {
ux += 1;
}
}

View File

@@ -14,3 +14,7 @@
| test.cpp:378:9:378:11 | val | The variable $@ may not be initialized at this access. | test.cpp:359:6:359:8 | val | val |
| test.cpp:417:10:417:10 | j | The variable $@ may not be initialized at this access. | test.cpp:414:9:414:9 | j | j |
| test.cpp:436:9:436:9 | j | The variable $@ may not be initialized at this access. | test.cpp:431:9:431:9 | j | j |
| test.cpp:454:2:454:2 | x | The variable $@ may not be initialized at this access. | test.cpp:452:6:452:6 | x | x |
| test.cpp:460:7:460:7 | x | The variable $@ may not be initialized at this access. | test.cpp:458:6:458:6 | x | x |
| test.cpp:467:2:467:2 | x | The variable $@ may not be initialized at this access. | test.cpp:464:6:464:6 | x | x |
| test.cpp:474:7:474:7 | x | The variable $@ may not be initialized at this access. | test.cpp:471:6:471:6 | x | x |

View File

@@ -435,3 +435,41 @@ int test38() {
return j; // BAD
}
void test39() {
int x;
x; // GOOD, in void context
}
void test40() {
int x;
(void)x; // GOOD, explicitly cast to void
}
void test41() {
int x;
x++; // BAD
}
void test42() {
int x;
void(x++); // BAD
}
void test43() {
int x;
int y = 1;
x + y; // BAD
}
void test44() {
int x;
int y = 1;
void(x + y); // BAD
}

View File

@@ -16,12 +16,12 @@ namespace Semmle.BuildAnalyser
/// Locate all reference files and index them.
/// </summary>
/// <param name="dirs">Directories to search.</param>
/// <param name="progress">Callback for progress.</param>
public AssemblyCache(IEnumerable<string> dirs, IProgressMonitor progress)
/// <param name="progressMonitor">Callback for progress.</param>
public AssemblyCache(IEnumerable<string> dirs, ProgressMonitor progressMonitor)
{
foreach (var dir in dirs)
{
progress.FindingFiles(dir);
progressMonitor.FindingFiles(dir);
AddReferenceDirectory(dir);
}
IndexReferences();
@@ -41,6 +41,8 @@ namespace Semmle.BuildAnalyser
}
}
private static readonly Version emptyVersion = new Version(0, 0, 0, 0);
/// <summary>
/// Indexes all DLLs we have located.
/// Because this is a potentially time-consuming operation, it is put into a separate stage.
@@ -55,7 +57,9 @@ namespace Semmle.BuildAnalyser
// Index "assemblyInfo" by version string
// The OrderBy is used to ensure that we by default select the highest version number.
foreach (var info in assemblyInfoByFileName.Values.OrderBy(info => info.Id))
foreach (var info in assemblyInfoByFileName.Values
.OrderBy(info => info.Name)
.ThenBy(info => info.Version ?? emptyVersion))
{
foreach (var index in info.IndexStrings)
assemblyInfoById[index] = info;

View File

@@ -8,122 +8,106 @@ using System.Threading.Tasks;
using System.Collections.Concurrent;
using System.Text;
using System.Security.Cryptography;
using System.Text.RegularExpressions;
namespace Semmle.BuildAnalyser
{
/// <summary>
/// The output of a build analysis.
/// </summary>
internal interface IBuildAnalysis
{
/// <summary>
/// Full filepaths of external references.
/// </summary>
IEnumerable<string> ReferenceFiles { get; }
/// <summary>
/// Full filepaths of C# source files from project files.
/// </summary>
IEnumerable<string> ProjectSourceFiles { get; }
/// <summary>
/// Full filepaths of C# source files in the filesystem.
/// </summary>
IEnumerable<string> AllSourceFiles { get; }
/// <summary>
/// The assembly IDs which could not be resolved.
/// </summary>
IEnumerable<string> UnresolvedReferences { get; }
/// <summary>
/// List of source files referenced by projects but
/// which were not found in the filesystem.
/// </summary>
IEnumerable<string> MissingSourceFiles { get; }
}
/// <summary>
/// Main implementation of the build analysis.
/// </summary>
internal sealed class BuildAnalysis : IBuildAnalysis, IDisposable
internal sealed partial class BuildAnalysis : IDisposable
{
private readonly AssemblyCache assemblyCache;
private readonly IProgressMonitor progressMonitor;
private readonly ProgressMonitor progressMonitor;
private readonly IDictionary<string, bool> usedReferences = new ConcurrentDictionary<string, bool>();
private readonly IDictionary<string, bool> sources = new ConcurrentDictionary<string, bool>();
private readonly IDictionary<string, string> unresolvedReferences = new ConcurrentDictionary<string, string>();
private int failedProjects, succeededProjects;
private int failedProjects;
private int succeededProjects;
private readonly string[] allSources;
private int conflictedReferences = 0;
private readonly Options options;
private readonly DirectoryInfo sourceDir;
private readonly DotNet dotnet;
/// <summary>
/// Performs a C# build analysis.
/// </summary>
/// <param name="options">Analysis options from the command line.</param>
/// <param name="progress">Display of analysis progress.</param>
public BuildAnalysis(Options options, IProgressMonitor progress)
/// <param name="progressMonitor">Display of analysis progress.</param>
public BuildAnalysis(Options options, ProgressMonitor progressMonitor)
{
var startTime = DateTime.Now;
progressMonitor = progress;
var sourceDir = new DirectoryInfo(options.SrcDir);
this.options = options;
this.progressMonitor = progressMonitor;
this.sourceDir = new DirectoryInfo(options.SrcDir);
progressMonitor.FindingFiles(options.SrcDir);
try
{
this.dotnet = new DotNet(progressMonitor);
}
catch
{
progressMonitor.MissingDotNet();
throw;
}
allSources = sourceDir.GetFiles("*.cs", SearchOption.AllDirectories)
.Select(d => d.FullName)
.Where(d => !options.ExcludesFile(d))
.ToArray();
this.progressMonitor.FindingFiles(options.SrcDir);
this.allSources = GetFiles("*.cs").ToArray();
var allProjects = GetFiles("*.csproj");
var solutions = options.SolutionFile is not null
? new[] { options.SolutionFile }
: GetFiles("*.sln");
var dllDirNames = options.DllDirs.Select(Path.GetFullPath).ToList();
packageDirectory = new TemporaryDirectory(ComputeTempDirectory(sourceDir.FullName));
if (options.UseNuGet)
{
try
{
var nuget = new NugetPackages(sourceDir.FullName, packageDirectory);
nuget.InstallPackages(progressMonitor);
}
catch (FileNotFoundException)
{
progressMonitor.MissingNuGet();
}
}
// Find DLLs in the .Net Framework
if (options.ScanNetFrameworkDlls)
{
var runtimeLocation = Runtime.GetRuntime(options.UseSelfContainedDotnet);
var runtimeLocation = new Runtime(dotnet).GetRuntime(options.UseSelfContainedDotnet);
progressMonitor.Log(Util.Logging.Severity.Debug, $"Runtime location selected: {runtimeLocation}");
dllDirNames.Add(runtimeLocation);
}
// TODO: remove the below when the required SDK is installed
using (new FileRenamer(sourceDir.GetFiles("global.json", SearchOption.AllDirectories)))
{
var solutions = options.SolutionFile is not null ?
new[] { options.SolutionFile } :
sourceDir.GetFiles("*.sln", SearchOption.AllDirectories).Select(d => d.FullName);
if (options.UseNuGet)
{
RestoreSolutions(solutions);
}
dllDirNames.Add(packageDirectory.DirInfo.FullName);
assemblyCache = new BuildAnalyser.AssemblyCache(dllDirNames, progress);
AnalyseSolutions(solutions);
foreach (var filename in assemblyCache.AllAssemblies.Select(a => a.Filename))
UseReference(filename);
}
if (options.UseMscorlib)
{
UseReference(typeof(object).Assembly.Location);
}
packageDirectory = new TemporaryDirectory(ComputeTempDirectory(sourceDir.FullName));
if (options.UseNuGet)
{
dllDirNames.Add(packageDirectory.DirInfo.FullName);
try
{
var nuget = new NugetPackages(sourceDir.FullName, packageDirectory, progressMonitor);
nuget.InstallPackages();
}
catch (FileNotFoundException)
{
progressMonitor.MissingNuGet();
}
// TODO: remove the below when the required SDK is installed
using (new FileRenamer(sourceDir.GetFiles("global.json", SearchOption.AllDirectories)))
{
Restore(solutions);
Restore(allProjects);
DownloadMissingPackages(allProjects);
}
}
assemblyCache = new AssemblyCache(dllDirNames, progressMonitor);
AnalyseSolutions(solutions);
foreach (var filename in assemblyCache.AllAssemblies.Select(a => a.Filename))
{
UseReference(filename);
}
ResolveConflicts();
// Output the findings
@@ -149,6 +133,13 @@ namespace Semmle.BuildAnalyser
DateTime.Now - startTime);
}
private IEnumerable<string> GetFiles(string pattern, bool recurseSubdirectories = true)
{
return sourceDir.GetFiles(pattern, new EnumerationOptions { RecurseSubdirectories = recurseSubdirectories, MatchCasing = MatchCasing.CaseInsensitive })
.Select(d => d.FullName)
.Where(d => !options.ExcludesFile(d));
}
/// <summary>
/// Computes a unique temp directory for the packages associated
/// with this source tree. Use a SHA1 of the directory name.
@@ -158,9 +149,7 @@ namespace Semmle.BuildAnalyser
private static string ComputeTempDirectory(string srcDir)
{
var bytes = Encoding.Unicode.GetBytes(srcDir);
using var sha1 = SHA1.Create();
var sha = sha1.ComputeHash(bytes);
var sha = SHA1.HashData(bytes);
var sb = new StringBuilder();
foreach (var b in sha.Take(8))
sb.AppendFormat("{0:x2}", b);
@@ -195,12 +184,15 @@ namespace Semmle.BuildAnalyser
// Pick the highest version for each assembly name
foreach (var r in sortedReferences)
{
finalAssemblyList[r.Name] = r;
}
// Update the used references list
usedReferences.Clear();
foreach (var r in finalAssemblyList.Select(r => r.Value.Filename))
{
UseReference(r);
}
// Report the results
foreach (var r in sortedReferences)
@@ -278,7 +270,9 @@ namespace Semmle.BuildAnalyser
private void AnalyseProjectFiles(IEnumerable<FileInfo> projectFiles)
{
foreach (var proj in projectFiles)
{
AnalyseProject(proj);
}
}
private void AnalyseProject(FileInfo project)
@@ -324,36 +318,106 @@ namespace Semmle.BuildAnalyser
}
private void Restore(string projectOrSolution)
private bool Restore(string target, string? pathToNugetConfig = null)
{
int exit;
try
{
exit = DotNet.RestoreToDirectory(projectOrSolution, packageDirectory.DirInfo.FullName);
}
catch (FileNotFoundException)
{
exit = 2;
}
return dotnet.RestoreToDirectory(target, packageDirectory.DirInfo.FullName, pathToNugetConfig);
}
switch (exit)
private void Restore(IEnumerable<string> targets, string? pathToNugetConfig = null)
{
foreach (var target in targets)
{
case 0:
case 1:
// No errors
break;
default:
progressMonitor.CommandFailed("dotnet", $"restore \"{projectOrSolution}\"", exit);
break;
Restore(target, pathToNugetConfig);
}
}
public void RestoreSolutions(IEnumerable<string> solutions)
private void DownloadMissingPackages(IEnumerable<string> restoreTargets)
{
Parallel.ForEach(solutions, new ParallelOptions { MaxDegreeOfParallelism = 4 }, Restore);
var alreadyDownloadedPackages = Directory.GetDirectories(packageDirectory.DirInfo.FullName).Select(d => Path.GetFileName(d).ToLowerInvariant()).ToHashSet();
var notYetDownloadedPackages = new HashSet<string>();
var nugetConfigs = GetFiles("nuget.config", recurseSubdirectories: true).ToArray();
string? nugetConfig = null;
if (nugetConfigs.Length > 1)
{
progressMonitor.MultipleNugetConfig(nugetConfigs);
nugetConfig = GetFiles("nuget.config", recurseSubdirectories: false).FirstOrDefault();
if (nugetConfig == null)
{
progressMonitor.NoTopLevelNugetConfig();
}
}
else
{
nugetConfig = nugetConfigs.FirstOrDefault();
}
var allFiles = GetFiles("*.*");
foreach (var file in allFiles)
{
try
{
using var sr = new StreamReader(file);
ReadOnlySpan<char> line;
while ((line = sr.ReadLine()) != null)
{
foreach (var valueMatch in PackageReference().EnumerateMatches(line))
{
// We can't get the group from the ValueMatch, so doing it manually:
var match = line.Slice(valueMatch.Index, valueMatch.Length);
var includeIndex = match.IndexOf("Include", StringComparison.InvariantCultureIgnoreCase);
if (includeIndex == -1)
{
continue;
}
match = match.Slice(includeIndex + "Include".Length + 1);
var quoteIndex1 = match.IndexOf("\"");
var quoteIndex2 = match.Slice(quoteIndex1 + 1).IndexOf("\"");
var packageName = match.Slice(quoteIndex1 + 1, quoteIndex2).ToString().ToLowerInvariant();
if (!alreadyDownloadedPackages.Contains(packageName))
{
notYetDownloadedPackages.Add(packageName);
}
}
}
}
catch (Exception ex)
{
progressMonitor.FailedToReadFile(file, ex);
continue;
}
}
foreach (var package in notYetDownloadedPackages)
{
progressMonitor.NugetInstall(package);
using var tempDir = new TemporaryDirectory(ComputeTempDirectory(package));
var success = dotnet.New(tempDir.DirInfo.FullName);
if (!success)
{
continue;
}
success = dotnet.AddPackage(tempDir.DirInfo.FullName, package);
if (!success)
{
continue;
}
success = Restore(tempDir.DirInfo.FullName, nugetConfig);
// TODO: the restore might fail, we could retry with a prerelease (*-* instead of *) version of the package.
if (!success)
{
progressMonitor.FailedToRestoreNugetPackage(package);
}
}
}
public void AnalyseSolutions(IEnumerable<string> solutions)
private void AnalyseSolutions(IEnumerable<string> solutions)
{
Parallel.ForEach(solutions, new ParallelOptions { MaxDegreeOfParallelism = 4 }, solutionFile =>
{
@@ -374,5 +438,8 @@ namespace Semmle.BuildAnalyser
{
packageDirectory?.Dispose();
}
[GeneratedRegex("<PackageReference .*Include=\"(.*?)\".*/>", RegexOptions.IgnoreCase | RegexOptions.Compiled | RegexOptions.Singleline)]
private static partial Regex PackageReference();
}
}

View File

@@ -1,17 +1,96 @@
using System.Diagnostics;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using Semmle.Util;
namespace Semmle.BuildAnalyser
{
internal interface IDotNet
{
bool RestoreToDirectory(string project, string directory, string? pathToNugetConfig = null);
bool New(string folder);
bool AddPackage(string folder, string package);
public IList<string> GetListedRuntimes();
}
/// <summary>
/// Utilities to run the "dotnet" command.
/// </summary>
internal static class DotNet
internal class DotNet : IDotNet
{
public static int RestoreToDirectory(string projectOrSolutionFile, string packageDirectory)
private const string dotnet = "dotnet";
private readonly ProgressMonitor progressMonitor;
public DotNet(ProgressMonitor progressMonitor)
{
using var proc = Process.Start("dotnet", $"restore --no-dependencies \"{projectOrSolutionFile}\" --packages \"{packageDirectory}\" /p:DisableImplicitNuGetFallbackFolder=true");
this.progressMonitor = progressMonitor;
Info();
}
private void Info()
{
// TODO: make sure the below `dotnet` version is matching the one specified in global.json
progressMonitor.RunningProcess($"{dotnet} --info");
using var proc = Process.Start(dotnet, "--info");
proc.WaitForExit();
return proc.ExitCode;
var ret = proc.ExitCode;
if (ret != 0)
{
progressMonitor.CommandFailed(dotnet, "--info", ret);
throw new Exception($"{dotnet} --info failed with exit code {ret}.");
}
}
private bool RunCommand(string args)
{
progressMonitor.RunningProcess($"{dotnet} {args}");
using var proc = Process.Start(dotnet, args);
proc.WaitForExit();
if (proc.ExitCode != 0)
{
progressMonitor.CommandFailed(dotnet, args, proc.ExitCode);
return false;
}
return true;
}
public bool RestoreToDirectory(string projectOrSolutionFile, string packageDirectory, string? pathToNugetConfig = null)
{
var args = $"restore --no-dependencies \"{projectOrSolutionFile}\" --packages \"{packageDirectory}\" /p:DisableImplicitNuGetFallbackFolder=true";
if (pathToNugetConfig != null)
args += $" --configfile \"{pathToNugetConfig}\"";
return RunCommand(args);
}
public bool New(string folder)
{
var args = $"new console --no-restore --output \"{folder}\"";
return RunCommand(args);
}
public bool AddPackage(string folder, string package)
{
var args = $"add \"{folder}\" package \"{package}\" --no-restore";
return RunCommand(args);
}
public IList<string> GetListedRuntimes()
{
var args = "--list-runtimes";
var pi = new ProcessStartInfo(dotnet, args)
{
RedirectStandardOutput = true,
UseShellExecute = false
};
var exitCode = pi.ReadOutput(out var runtimes);
if (exitCode != 0)
{
progressMonitor.CommandFailed(dotnet, args, exitCode);
return new List<string>();
}
return runtimes;
}
}
}

View File

@@ -17,26 +17,24 @@ namespace Semmle.BuildAnalyser
/// <summary>
/// Create the package manager for a specified source tree.
/// </summary>
/// <param name="sourceDir">The source directory.</param>
public NugetPackages(string sourceDir, TemporaryDirectory packageDirectory)
public NugetPackages(string sourceDir, TemporaryDirectory packageDirectory, ProgressMonitor progressMonitor)
{
SourceDirectory = sourceDir;
PackageDirectory = packageDirectory;
this.progressMonitor = progressMonitor;
// Expect nuget.exe to be in a `nuget` directory under the directory containing this exe.
var currentAssembly = System.Reflection.Assembly.GetExecutingAssembly().Location;
var directory = Path.GetDirectoryName(currentAssembly);
if (directory is null)
throw new FileNotFoundException($"Directory path '{currentAssembly}' of current assembly is null");
var directory = Path.GetDirectoryName(currentAssembly)
?? throw new FileNotFoundException($"Directory path '{currentAssembly}' of current assembly is null");
nugetExe = Path.Combine(directory, "nuget", "nuget.exe");
if (!File.Exists(nugetExe))
throw new FileNotFoundException(string.Format("NuGet could not be found at {0}", nugetExe));
packages = new DirectoryInfo(SourceDirectory).
EnumerateFiles("packages.config", SearchOption.AllDirectories).
ToArray();
packages = new DirectoryInfo(SourceDirectory)
.EnumerateFiles("packages.config", SearchOption.AllDirectories)
.ToArray();
}
// List of package files to download.
@@ -51,11 +49,11 @@ namespace Semmle.BuildAnalyser
/// Download the packages to the temp folder.
/// </summary>
/// <param name="pm">The progress monitor used for reporting errors etc.</param>
public void InstallPackages(IProgressMonitor pm)
public void InstallPackages()
{
foreach (var package in packages)
{
RestoreNugetPackage(package.FullName, pm);
RestoreNugetPackage(package.FullName);
}
}
@@ -80,9 +78,9 @@ namespace Semmle.BuildAnalyser
/// </summary>
/// <param name="package">The package file.</param>
/// <param name="pm">Where to log progress/errors.</param>
private void RestoreNugetPackage(string package, IProgressMonitor pm)
private void RestoreNugetPackage(string package)
{
pm.NugetInstall(package);
progressMonitor.NugetInstall(package);
/* Use nuget.exe to install a package.
* Note that there is a clutch of NuGet assemblies which could be used to
@@ -115,7 +113,7 @@ namespace Semmle.BuildAnalyser
if (p is null)
{
pm.FailedNugetCommand(pi.FileName, pi.Arguments, "Couldn't start process.");
progressMonitor.FailedNugetCommand(pi.FileName, pi.Arguments, "Couldn't start process.");
return;
}
@@ -125,16 +123,17 @@ namespace Semmle.BuildAnalyser
p.WaitForExit();
if (p.ExitCode != 0)
{
pm.FailedNugetCommand(pi.FileName, pi.Arguments, output + error);
progressMonitor.FailedNugetCommand(pi.FileName, pi.Arguments, output + error);
}
}
catch (Exception ex)
when (ex is System.ComponentModel.Win32Exception || ex is FileNotFoundException)
{
pm.FailedNugetCommand(pi.FileName, pi.Arguments, ex.Message);
progressMonitor.FailedNugetCommand(pi.FileName, pi.Arguments, ex.Message);
}
}
private readonly string nugetExe;
private readonly ProgressMonitor progressMonitor;
}
}

View File

@@ -3,27 +3,7 @@ using System;
namespace Semmle.BuildAnalyser
{
/// <summary>
/// Callback for various events that may happen during the build analysis.
/// </summary>
internal interface IProgressMonitor
{
void FindingFiles(string dir);
void UnresolvedReference(string id, string project);
void AnalysingSolution(string filename);
void FailedProjectFile(string filename, string reason);
void FailedNugetCommand(string exe, string args, string message);
void NugetInstall(string package);
void ResolvedReference(string filename);
void Summary(int existingSources, int usedSources, int missingSources, int references, int unresolvedReferences, int resolvedConflicts, int totalProjects, int failedProjects, TimeSpan analysisTime);
void Log(Severity severity, string message);
void ResolvedConflict(string asm1, string asm2);
void MissingProject(string projectFile);
void CommandFailed(string exe, string arguments, int exitCode);
void MissingNuGet();
}
internal class ProgressMonitor : IProgressMonitor
internal class ProgressMonitor
{
private readonly ILogger logger;
@@ -117,5 +97,36 @@ namespace Semmle.BuildAnalyser
{
logger.Log(Severity.Error, "Missing nuget.exe");
}
public void MissingDotNet()
{
logger.Log(Severity.Error, "Missing dotnet CLI");
}
public void RunningProcess(string command)
{
logger.Log(Severity.Info, $"Running {command}");
}
public void FailedToRestoreNugetPackage(string package)
{
logger.Log(Severity.Info, $"Failed to restore nuget package {package}");
}
public void FailedToReadFile(string file, Exception ex)
{
logger.Log(Severity.Info, $"Failed to read file {file}");
logger.Log(Severity.Debug, $"Failed to read file {file}, exception: {ex}");
}
public void MultipleNugetConfig(string[] nugetConfigs)
{
logger.Log(Severity.Info, $"Found multiple nuget.config files: {string.Join(", ", nugetConfigs)}.");
}
internal void NoTopLevelNugetConfig()
{
logger.Log(Severity.Info, $"Could not find a top-level nuget.config file.");
}
}
}

View File

@@ -1,4 +1,5 @@
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
// General Information about an assembly is controlled through the following
@@ -13,6 +14,9 @@ using System.Runtime.InteropServices;
[assembly: AssemblyTrademark("")]
[assembly: AssemblyCulture("")]
// Expose internals for testing purposes.
[assembly: InternalsVisibleTo("Semmle.Extraction.Tests")]
// Setting ComVisible to false makes the types in this assembly not visible
// to COM components. If you need to access a type in this assembly from
// COM, set the ComVisible attribute to true on that type.

View File

@@ -3,6 +3,8 @@ using System.Collections.Generic;
using System.Runtime.InteropServices;
using System.IO;
using System.Linq;
using System.Text.RegularExpressions;
using Semmle.BuildAnalyser;
using Semmle.Util;
namespace Semmle.Extraction.CSharp.Standalone
@@ -10,31 +12,105 @@ namespace Semmle.Extraction.CSharp.Standalone
/// <summary>
/// Locates .NET Runtimes.
/// </summary>
internal static class Runtime
internal partial class Runtime
{
private const string netCoreApp = "Microsoft.NETCore.App";
private const string aspNetCoreApp = "Microsoft.AspNetCore.App";
private readonly IDotNet dotNet;
private static string ExecutingRuntime => RuntimeEnvironment.GetRuntimeDirectory();
/// <summary>
/// Locates .NET Core Runtimes.
/// </summary>
private static IEnumerable<string> CoreRuntimes
{
get
{
var dotnetPath = FileUtils.FindProgramOnPath(Win32.IsWindows() ? "dotnet.exe" : "dotnet");
var dotnetDirs = dotnetPath is not null
? new[] { dotnetPath }
: new[] { "/usr/share/dotnet", @"C:\Program Files\dotnet" };
var coreDirs = dotnetDirs.Select(d => Path.Combine(d, "shared", "Microsoft.NETCore.App"));
public Runtime(IDotNet dotNet) => this.dotNet = dotNet;
var dir = coreDirs.FirstOrDefault(Directory.Exists);
if (dir is not null)
internal sealed class RuntimeVersion : IComparable<RuntimeVersion>
{
private readonly string dir;
private readonly Version version;
private readonly Version? preReleaseVersion;
private readonly string? preReleaseVersionType;
private bool IsPreRelease => preReleaseVersionType is not null && preReleaseVersion is not null;
public string FullPath
{
get
{
return Directory.EnumerateDirectories(dir).OrderByDescending(Path.GetFileName);
var preRelease = IsPreRelease ? $"-{preReleaseVersionType}.{preReleaseVersion}" : "";
var version = this.version + preRelease;
return Path.Combine(dir, version);
}
}
public RuntimeVersion(string dir, string version, string preReleaseVersionType, string preReleaseVersion)
{
this.dir = dir;
this.version = Version.Parse(version);
if (!string.IsNullOrEmpty(preReleaseVersion) && !string.IsNullOrEmpty(preReleaseVersionType))
{
this.preReleaseVersionType = preReleaseVersionType;
this.preReleaseVersion = Version.Parse(preReleaseVersion);
}
}
public int CompareTo(RuntimeVersion? other)
{
var c = version.CompareTo(other?.version);
if (c == 0 && IsPreRelease)
{
if (!other!.IsPreRelease)
{
return -1;
}
// Both are pre-release like runtime versions.
// The pre-release version types are sorted alphabetically (e.g. alpha, beta, preview, rc)
// and the pre-release version types are more important that the pre-release version numbers.
return preReleaseVersionType != other!.preReleaseVersionType
? preReleaseVersionType!.CompareTo(other!.preReleaseVersionType)
: preReleaseVersion!.CompareTo(other!.preReleaseVersion);
}
return Enumerable.Empty<string>();
return c;
}
public override bool Equals(object? obj) =>
obj is not null && obj is RuntimeVersion other && other.FullPath == FullPath;
public override int GetHashCode() => FullPath.GetHashCode();
public override string ToString() => FullPath;
}
[GeneratedRegex(@"^(\S+)\s(\d+\.\d+\.\d+)(-([a-z]+)\.(\d+\.\d+\.\d+))?\s\[(\S+)\]$")]
private static partial Regex RuntimeRegex();
/// <summary>
/// Parses the output of `dotnet --list-runtimes` to get a map from a runtime to the location of
/// the newest version of the runtime.
/// It is assume that the format of a listed runtime is something like:
/// Microsoft.NETCore.App 7.0.2 [/usr/share/dotnet/shared/Microsoft.NETCore.App]
/// </summary>
private static Dictionary<string, RuntimeVersion> ParseRuntimes(IList<string> listed)
{
// Parse listed runtimes.
var runtimes = new Dictionary<string, RuntimeVersion>();
listed.ForEach(r =>
{
var match = RuntimeRegex().Match(r);
if (match.Success)
{
runtimes.AddOrUpdate(match.Groups[1].Value, new RuntimeVersion(match.Groups[6].Value, match.Groups[2].Value, match.Groups[4].Value, match.Groups[5].Value));
}
});
return runtimes;
}
/// <summary>
/// Returns a dictionary mapping runtimes to their newest version.
/// </summary>
internal Dictionary<string, RuntimeVersion> GetNewestRuntimes()
{
var listed = dotNet.GetListedRuntimes();
return ParseRuntimes(listed);
}
/// <summary>
@@ -69,24 +145,33 @@ namespace Semmle.Extraction.CSharp.Standalone
}
}
private IEnumerable<string> GetRuntimes()
{
// Gets the newest version of the installed runtimes.
var newestRuntimes = GetNewestRuntimes();
// Location of the newest .NET Core Runtime.
if (newestRuntimes.TryGetValue(netCoreApp, out var netCoreVersion))
{
yield return netCoreVersion.FullPath;
}
// Location of the newest ASP.NET Core Runtime.
if (newestRuntimes.TryGetValue(aspNetCoreApp, out var aspNetCoreVersion))
{
yield return aspNetCoreVersion.FullPath;
}
foreach (var r in DesktopRuntimes)
yield return r;
// A bad choice if it's the self-contained runtime distributed in codeql dist.
yield return ExecutingRuntime;
}
/// <summary>
/// Gets the .NET runtime location to use for extraction
/// </summary>
public static string GetRuntime(bool useSelfContained) => useSelfContained ? ExecutingRuntime : Runtimes.First();
private static IEnumerable<string> Runtimes
{
get
{
foreach (var r in CoreRuntimes)
yield return r;
foreach (var r in DesktopRuntimes)
yield return r;
// A bad choice if it's the self-contained runtime distributed in codeql dist.
yield return ExecutingRuntime;
}
}
public string GetRuntime(bool useSelfContained) => useSelfContained ? ExecutingRuntime : GetRuntimes().First();
}
}

View File

@@ -100,7 +100,7 @@ namespace Semmle.Extraction.CSharp
// Figure out if it's dotnet core
var netCoreProjectFile = root.GetAttribute("Sdk") == "Microsoft.NET.Sdk";
var netCoreProjectFile = root.GetAttribute("Sdk").StartsWith("Microsoft.NET.Sdk");
if (netCoreProjectFile)
{

View File

@@ -0,0 +1,104 @@
using Xunit;
using System.Collections.Generic;
using Semmle.BuildAnalyser;
using Semmle.Extraction.CSharp.Standalone;
namespace Semmle.Extraction.Tests
{
internal class DotNetStub : IDotNet
{
private readonly IList<string> runtimes;
public DotNetStub(IList<string> runtimes) => this.runtimes = runtimes;
public bool AddPackage(string folder, string package) => true;
public bool New(string folder) => true;
public bool RestoreToDirectory(string project, string directory, string? pathToNugetConfig = null) => true;
public IList<string> GetListedRuntimes() => runtimes;
}
public class RuntimeTests
{
private static string FixExpectedPathOnWindows(string path) => path.Replace('\\', '/');
[Fact]
public void TestRuntime1()
{
// Setup
var listedRuntimes = new List<string> {
"Microsoft.AspNetCore.App 5.0.12 [/path/dotnet/shared/Microsoft.AspNetCore.App]",
"Microsoft.AspNetCore.App 6.0.4 [/path/dotnet/shared/Microsoft.AspNetCore.App]",
"Microsoft.AspNetCore.App 7.0.0 [/path/dotnet/shared/Microsoft.AspNetCore.App]",
"Microsoft.AspNetCore.App 7.0.2 [/path/dotnet/shared/Microsoft.AspNetCore.App]",
"Microsoft.NETCore.App 5.0.12 [/path/dotnet/shared/Microsoft.NETCore.App]",
"Microsoft.NETCore.App 6.0.4 [/path/dotnet/shared/Microsoft.NETCore.App]",
"Microsoft.NETCore.App 7.0.0 [/path/dotnet/shared/Microsoft.NETCore.App]",
"Microsoft.NETCore.App 7.0.2 [/path/dotnet/shared/Microsoft.NETCore.App]"
};
var dotnet = new DotNetStub(listedRuntimes);
var runtime = new Runtime(dotnet);
// Execute
var runtimes = runtime.GetNewestRuntimes();
// Verify
Assert.Equal(2, runtimes.Count);
Assert.True(runtimes.TryGetValue("Microsoft.AspNetCore.App", out var aspNetCoreApp));
Assert.Equal("/path/dotnet/shared/Microsoft.AspNetCore.App/7.0.2", FixExpectedPathOnWindows(aspNetCoreApp.FullPath));
Assert.True(runtimes.TryGetValue("Microsoft.NETCore.App", out var netCoreApp));
Assert.Equal("/path/dotnet/shared/Microsoft.NETCore.App/7.0.2", FixExpectedPathOnWindows(netCoreApp.FullPath));
}
[Fact]
public void TestRuntime2()
{
// Setup
var listedRuntimes = new List<string>
{
"Microsoft.NETCore.App 7.0.2 [/path/dotnet/shared/Microsoft.NETCore.App]",
"Microsoft.NETCore.App 8.0.0-preview.5.43280.8 [/path/dotnet/shared/Microsoft.NETCore.App]",
"Microsoft.NETCore.App 8.0.0-preview.5.23280.8 [/path/dotnet/shared/Microsoft.NETCore.App]"
};
var dotnet = new DotNetStub(listedRuntimes);
var runtime = new Runtime(dotnet);
// Execute
var runtimes = runtime.GetNewestRuntimes();
// Verify
Assert.Single(runtimes);
Assert.True(runtimes.TryGetValue("Microsoft.NETCore.App", out var netCoreApp));
Assert.Equal("/path/dotnet/shared/Microsoft.NETCore.App/8.0.0-preview.5.43280.8", FixExpectedPathOnWindows(netCoreApp.FullPath));
}
[Fact]
public void TestRuntime3()
{
// Setup
var listedRuntimes = new List<string>
{
"Microsoft.NETCore.App 7.0.2 [/path/dotnet/shared/Microsoft.NETCore.App]",
"Microsoft.NETCore.App 8.0.0-rc.4.43280.8 [/path/dotnet/shared/Microsoft.NETCore.App]",
"Microsoft.NETCore.App 8.0.0-preview.5.23280.8 [/path/dotnet/shared/Microsoft.NETCore.App]"
};
var dotnet = new DotNetStub(listedRuntimes);
var runtime = new Runtime(dotnet);
// Execute
var runtimes = runtime.GetNewestRuntimes();
// Verify
Assert.Single(runtimes);
Assert.True(runtimes.TryGetValue("Microsoft.NETCore.App", out var netCoreApp));
Assert.Equal("/path/dotnet/shared/Microsoft.NETCore.App/8.0.0-rc.4.43280.8", FixExpectedPathOnWindows(netCoreApp.FullPath));
}
}
}

View File

@@ -1,4 +1,5 @@
using System.Collections.Generic;
using System;
using System.Collections.Generic;
namespace Semmle.Util
{
@@ -18,5 +19,17 @@ namespace Semmle.Util
}
list.Add(element);
}
/// <summary>
/// Adds a new value or replaces the existing value (if the new value is greater than the existing)
/// in dictionary for the given key.
/// </summary>
public static void AddOrUpdate<T1, T2>(this Dictionary<T1, T2> dict, T1 key, T2 value) where T1 : notnull where T2 : IComparable<T2>
{
if (!dict.TryGetValue(key, out var existing) || existing.CompareTo(value) < 0)
{
dict[key] = value;
}
}
}
}

View File

@@ -1,3 +1,11 @@
## 1.6.0
No user-facing changes.
## 1.5.4
No user-facing changes.
## 1.5.3
No user-facing changes.

View File

@@ -0,0 +1,3 @@
## 1.5.4
No user-facing changes.

View File

@@ -0,0 +1,3 @@
## 1.6.0
No user-facing changes.

View File

@@ -1,2 +1,2 @@
---
lastReleaseVersion: 1.5.3
lastReleaseVersion: 1.6.0

View File

@@ -1,5 +1,5 @@
name: codeql/csharp-solorigate-all
version: 1.5.4-dev
version: 1.6.1-dev
groups:
- csharp
- solorigate

View File

@@ -1,3 +1,11 @@
## 1.6.0
No user-facing changes.
## 1.5.4
No user-facing changes.
## 1.5.3
No user-facing changes.

View File

@@ -0,0 +1,3 @@
## 1.5.4
No user-facing changes.

View File

@@ -0,0 +1,3 @@
## 1.6.0
No user-facing changes.

View File

@@ -1,2 +1,2 @@
---
lastReleaseVersion: 1.5.3
lastReleaseVersion: 1.6.0

View File

@@ -1,5 +1,5 @@
name: codeql/csharp-solorigate-queries
version: 1.5.4-dev
version: 1.6.1-dev
groups:
- csharp
- solorigate

View File

@@ -1,3 +1,17 @@
## 0.7.0
### Major Analysis Improvements
* The data flow library now performs type strengthening. This increases precision for all data flow queries by excluding paths that can be inferred to be impossible due to incompatible types.
### Minor Analysis Improvements
* Additional support for `command-injection`, `ldap-injection`, `log-injection`, and `url-redirection` sink kinds for Models as Data.
## 0.6.4
No user-facing changes.
## 0.6.3
### Major Analysis Improvements

View File

@@ -1,4 +0,0 @@
---
category: minorAnalysis
---
* Additional support for `command-injection`, `ldap-injection`, `log-injection`, and `url-redirection` sink kinds for Models as Data.

View File

@@ -1,4 +0,0 @@
---
category: majorAnalysis
---
* The data flow library now performs type strengthening. This increases precision for all data flow queries by excluding paths that can be inferred to be impossible due to incompatible types.

View File

@@ -0,0 +1,6 @@
---
category: feature
---
* The `DataFlow::StateConfigSig` signature module has gained default implementations for `isBarrier/2` and `isAdditionalFlowStep/4`.
Hence it is no longer needed to provide `none()` implementations of these predicates if they are not needed.

View File

@@ -0,0 +1,3 @@
## 0.6.4
No user-facing changes.

View File

@@ -0,0 +1,9 @@
## 0.7.0
### Major Analysis Improvements
* The data flow library now performs type strengthening. This increases precision for all data flow queries by excluding paths that can be inferred to be impossible due to incompatible types.
### Minor Analysis Improvements
* Additional support for `command-injection`, `ldap-injection`, `log-injection`, and `url-redirection` sink kinds for Models as Data.

View File

@@ -1,2 +1,2 @@
---
lastReleaseVersion: 0.6.3
lastReleaseVersion: 0.7.0

View File

@@ -1,5 +1,5 @@
name: codeql/csharp-all
version: 0.6.4-dev
version: 0.7.1-dev
groups: csharp
dbscheme: semmlecode.csharp.dbscheme
extractor: csharp

View File

@@ -110,8 +110,12 @@ module SummaryComponentStack {
result = singleton(SummaryComponent::syntheticGlobal(synthetic))
}
/** Gets a textual representation of this stack used for flow summaries. */
string getComponentStack(SummaryComponentStack s) { result = Impl::Public::getComponentStack(s) }
/**
* DEPRECATED: Use the member predicate `getMadRepresentation` instead.
*
* Gets a textual representation of this stack used for flow summaries.
*/
deprecated string getComponentStack(SummaryComponentStack s) { result = s.getMadRepresentation() }
}
class SummarizedCallable = Impl::Public::SummarizedCallable;

View File

@@ -107,8 +107,6 @@ module Global<ConfigSig ContentConfig> {
predicate isBarrier = ContentConfig::isBarrier/1;
predicate isBarrier(DataFlow::Node node, FlowState state) { none() }
DataFlow::FlowFeature getAFeature() { result = ContentConfig::getAFeature() }
// needed to record reads/stores inside summarized callables

View File

@@ -114,7 +114,7 @@ signature module StateConfigSig {
* Holds if data flow through `node` is prohibited when the flow state is
* `state`.
*/
predicate isBarrier(Node node, FlowState state);
default predicate isBarrier(Node node, FlowState state) { none() }
/** Holds if data flow into `node` is prohibited. */
default predicate isBarrierIn(Node node) { none() }
@@ -131,7 +131,9 @@ signature module StateConfigSig {
* Holds if data may flow from `node1` to `node2` in addition to the normal data-flow steps.
* This step is only applicable in `state1` and updates the flow state to `state2`.
*/
predicate isAdditionalFlowStep(Node node1, FlowState state1, Node node2, FlowState state2);
default predicate isAdditionalFlowStep(Node node1, FlowState state1, Node node2, FlowState state2) {
none()
}
/**
* Holds if an arbitrary number of implicit read steps of content `c` may be

View File

@@ -254,6 +254,11 @@ module Impl<FullStateConfigSig Config> {
not fullBarrier(node2)
}
pragma[nomagic]
private predicate isUnreachableInCall1(NodeEx n, LocalCallContextSpecificCall cc) {
isUnreachableInCallCached(n.asNode(), cc.getCall())
}
/**
* Holds if data can flow in one local step from `node1` to `node2`.
*/
@@ -460,7 +465,6 @@ module Impl<FullStateConfigSig Config> {
* The Boolean `cc` records whether the node is reached through an
* argument in a call.
*/
pragma[assume_small_delta]
private predicate fwdFlow(NodeEx node, Cc cc) {
sourceNode(node, _) and
if hasSourceCallCtx() then cc = true else cc = false
@@ -570,7 +574,6 @@ module Impl<FullStateConfigSig Config> {
/**
* Holds if `c` is the target of a store in the flow covered by `fwdFlow`.
*/
pragma[assume_small_delta]
pragma[nomagic]
private predicate fwdFlowConsCand(Content c) {
exists(NodeEx mid, NodeEx node |
@@ -1216,7 +1219,6 @@ module Impl<FullStateConfigSig Config> {
fwdFlow1(_, _, _, _, _, _, t0, t, ap, _) and t0 != t
}
pragma[assume_small_delta]
pragma[nomagic]
private predicate fwdFlow0(
NodeEx node, FlowState state, Cc cc, ParamNodeOption summaryCtx, TypOption argT,
@@ -2111,7 +2113,7 @@ module Impl<FullStateConfigSig Config> {
NodeEx node1, FlowState state, NodeEx node2, boolean preservesValue, DataFlowType t,
LocalCallContext cc
) {
not isUnreachableInCallCached(node2.asNode(), cc.(LocalCallContextSpecificCall).getCall()) and
not isUnreachableInCall1(node2, cc) and
(
localFlowEntry(node1, pragma[only_bind_into](state)) and
(
@@ -2126,7 +2128,7 @@ module Impl<FullStateConfigSig Config> {
) and
node1 != node2 and
cc.relevantFor(node1.getEnclosingCallable()) and
not isUnreachableInCallCached(node1.asNode(), cc.(LocalCallContextSpecificCall).getCall())
not isUnreachableInCall1(node1, cc)
or
exists(NodeEx mid |
localFlowStepPlus(node1, pragma[only_bind_into](state), mid, preservesValue, t, cc) and
@@ -2163,10 +2165,8 @@ module Impl<FullStateConfigSig Config> {
preservesValue = false and
t = node2.getDataFlowType() and
callContext.relevantFor(node1.getEnclosingCallable()) and
not exists(DataFlowCall call | call = callContext.(LocalCallContextSpecificCall).getCall() |
isUnreachableInCallCached(node1.asNode(), call) or
isUnreachableInCallCached(node2.asNode(), call)
)
not isUnreachableInCall1(node1, callContext) and
not isUnreachableInCall1(node2, callContext)
}
}
@@ -2706,7 +2706,7 @@ module Impl<FullStateConfigSig Config> {
ParamNodeEx getParamNode() { result = p }
override string toString() { result = p + ": " + ap }
override string toString() { result = p + concat(" : " + ppReprType(t)) + " " + ap }
predicate hasLocationInfo(
string filepath, int startline, int startcolumn, int endline, int endcolumn
@@ -2758,12 +2758,21 @@ module Impl<FullStateConfigSig Config> {
)
}
private predicate forceUnfold(AccessPathApprox apa) {
forceHighPrecision(apa.getHead())
or
exists(Content c2 |
apa = TConsCons(_, _, c2, _) and
forceHighPrecision(c2)
)
}
/**
* Holds with `unfold = false` if a precise head-tail representation of `apa` is
* expected to be expensive. Holds with `unfold = true` otherwise.
*/
private predicate evalUnfold(AccessPathApprox apa, boolean unfold) {
if forceHighPrecision(apa.getHead())
if forceUnfold(apa)
then unfold = true
else
exists(int aps, int nodes, int apLimit, int tupleLimit |
@@ -2777,7 +2786,6 @@ module Impl<FullStateConfigSig Config> {
/**
* Gets the number of `AccessPath`s that correspond to `apa`.
*/
pragma[assume_small_delta]
private int countAps(AccessPathApprox apa) {
evalUnfold(apa, false) and
result = 1 and
@@ -2796,7 +2804,6 @@ module Impl<FullStateConfigSig Config> {
* that it is expanded to a precise head-tail representation.
*/
language[monotonicAggregates]
pragma[assume_small_delta]
private int countPotentialAps(AccessPathApprox apa) {
apa instanceof AccessPathApproxNil and result = 1
or
@@ -2833,7 +2840,6 @@ module Impl<FullStateConfigSig Config> {
}
private newtype TPathNode =
pragma[assume_small_delta]
TPathNodeMid(
NodeEx node, FlowState state, CallContext cc, SummaryCtx sc, DataFlowType t, AccessPath ap
) {
@@ -2918,7 +2924,6 @@ module Impl<FullStateConfigSig Config> {
override AccessPathFrontHead getFront() { result = TFrontHead(head_) }
pragma[assume_small_delta]
override AccessPathApproxCons getApprox() {
result = TConsNil(head_, t) and tail_ = TAccessPathNil()
or
@@ -2927,7 +2932,6 @@ module Impl<FullStateConfigSig Config> {
result = TCons1(head_, this.length())
}
pragma[assume_small_delta]
override int length() { result = 1 + tail_.length() }
private string toStringImpl(boolean needsSuffix) {
@@ -3097,6 +3101,12 @@ module Impl<FullStateConfigSig Config> {
result = " <" + this.(PathNodeMid).getCallContext().toString() + ">"
}
private string ppSummaryCtx() {
this instanceof PathNodeSink and result = ""
or
result = " <" + this.(PathNodeMid).getSummaryCtx().toString() + ">"
}
/** Gets a textual representation of this element. */
string toString() { result = this.getNodeEx().toString() + this.ppType() + this.ppAp() }
@@ -3105,7 +3115,9 @@ module Impl<FullStateConfigSig Config> {
* representation of the call context.
*/
string toStringWithContext() {
result = this.getNodeEx().toString() + this.ppType() + this.ppAp() + this.ppCtx()
result =
this.getNodeEx().toString() + this.ppType() + this.ppAp() + this.ppCtx() +
this.ppSummaryCtx()
}
/**
@@ -3379,7 +3391,6 @@ module Impl<FullStateConfigSig Config> {
* Holds if data may flow from `mid` to `node`. The last step in or out of
* a callable is recorded by `cc`.
*/
pragma[assume_small_delta]
pragma[nomagic]
private predicate pathStep0(
PathNodeMid mid, NodeEx node, FlowState state, CallContext cc, SummaryCtx sc, DataFlowType t,
@@ -3592,7 +3603,6 @@ module Impl<FullStateConfigSig Config> {
)
}
pragma[assume_small_delta]
pragma[nomagic]
private predicate pathThroughCallable0(
DataFlowCall call, PathNodeMid mid, ReturnKindExt kind, FlowState state, CallContext cc,

View File

@@ -187,7 +187,6 @@ private module LambdaFlow {
else any()
}
pragma[assume_small_delta]
pragma[nomagic]
predicate revLambdaFlow0(
DataFlowCall lambdaCall, LambdaCallKind kind, Node node, DataFlowType t, boolean toReturn,
@@ -274,7 +273,6 @@ private module LambdaFlow {
)
}
pragma[assume_small_delta]
pragma[nomagic]
predicate revLambdaFlowOut(
DataFlowCall lambdaCall, LambdaCallKind kind, TReturnPositionSimple pos, DataFlowType t,

View File

@@ -23,29 +23,30 @@ module Public {
* content type, or a return kind.
*/
class SummaryComponent extends TSummaryComponent {
/** Gets a textual representation of this summary component. */
string toString() {
exists(ContentSet c | this = TContentSummaryComponent(c) and result = c.toString())
or
exists(ContentSet c | this = TWithoutContentSummaryComponent(c) and result = "without " + c)
or
exists(ContentSet c | this = TWithContentSummaryComponent(c) and result = "with " + c)
/** Gets a textual representation of this component used for MaD models. */
string getMadRepresentation() {
result = getMadRepresentationSpecific(this)
or
exists(ArgumentPosition pos |
this = TParameterSummaryComponent(pos) and result = "parameter " + pos
this = TParameterSummaryComponent(pos) and
result = "Parameter[" + getArgumentPosition(pos) + "]"
)
or
exists(ParameterPosition pos |
this = TArgumentSummaryComponent(pos) and result = "argument " + pos
this = TArgumentSummaryComponent(pos) and
result = "Argument[" + getParameterPosition(pos) + "]"
)
or
exists(ReturnKind rk | this = TReturnSummaryComponent(rk) and result = "return (" + rk + ")")
or
exists(SummaryComponent::SyntheticGlobal sg |
this = TSyntheticGlobalSummaryComponent(sg) and
result = "synthetic global (" + sg + ")"
exists(string synthetic |
this = TSyntheticGlobalSummaryComponent(synthetic) and
result = "SyntheticGlobal[" + synthetic + "]"
)
or
this = TReturnSummaryComponent(getReturnValueKind()) and result = "ReturnValue"
}
/** Gets a textual representation of this summary component. */
string toString() { result = this.getMadRepresentation() }
}
/** Provides predicates for constructing summary components. */
@@ -110,7 +111,6 @@ module Public {
}
/** Gets the stack obtained by dropping the first `i` elements, if any. */
pragma[assume_small_delta]
SummaryComponentStack drop(int i) {
i = 0 and result = this
or
@@ -125,19 +125,22 @@ module Public {
this = TSingletonSummaryComponentStack(result) or result = this.tail().bottom()
}
/** Gets a textual representation of this stack. */
string toString() {
/** Gets a textual representation of this stack used for MaD models. */
string getMadRepresentation() {
exists(SummaryComponent head, SummaryComponentStack tail |
head = this.head() and
tail = this.tail() and
result = tail + "." + head
result = tail.getMadRepresentation() + "." + head.getMadRepresentation()
)
or
exists(SummaryComponent c |
this = TSingletonSummaryComponentStack(c) and
result = c.toString()
result = c.getMadRepresentation()
)
}
/** Gets a textual representation of this stack. */
string toString() { result = this.getMadRepresentation() }
}
/** Provides predicates for constructing stacks of summary components. */
@@ -166,42 +169,6 @@ module Public {
SummaryComponentStack return(ReturnKind rk) { result = singleton(SummaryComponent::return(rk)) }
}
/** Gets a textual representation of this component used for flow summaries. */
private string getComponent(SummaryComponent sc) {
result = getComponentSpecific(sc)
or
exists(ArgumentPosition pos |
sc = TParameterSummaryComponent(pos) and
result = "Parameter[" + getArgumentPosition(pos) + "]"
)
or
exists(ParameterPosition pos |
sc = TArgumentSummaryComponent(pos) and
result = "Argument[" + getParameterPosition(pos) + "]"
)
or
exists(string synthetic |
sc = TSyntheticGlobalSummaryComponent(synthetic) and
result = "SyntheticGlobal[" + synthetic + "]"
)
or
sc = TReturnSummaryComponent(getReturnValueKind()) and result = "ReturnValue"
}
/** Gets a textual representation of this stack used for flow summaries. */
string getComponentStack(SummaryComponentStack stack) {
exists(SummaryComponent head, SummaryComponentStack tail |
head = stack.head() and
tail = stack.tail() and
result = getComponentStack(tail) + "." + getComponent(head)
)
or
exists(SummaryComponent c |
stack = TSingletonSummaryComponentStack(c) and
result = getComponent(c)
)
}
/**
* A class that exists for QL technical reasons only (the IPA type used
* to represent component stacks needs to be bounded).
@@ -1382,8 +1349,8 @@ module Private {
c.relevantSummary(input, output, preservesValue) and
csv =
c.getCallableCsv() // Callable information
+ getComponentStack(input) + ";" // input
+ getComponentStack(output) + ";" // output
+ input.getMadRepresentation() + ";" // input
+ output.getMadRepresentation() + ";" // output
+ renderKind(preservesValue) + ";" // kind
+ renderProvenance(c) // provenance
)

View File

@@ -186,7 +186,7 @@ SummaryComponent interpretComponentSpecific(AccessPathToken c) {
)
}
/** Gets the textual representation of the content in the format used for flow summaries. */
/** Gets the textual representation of the content in the format used for MaD models. */
private string getContentSpecific(Content c) {
c = TElementContent() and result = "Element"
or
@@ -197,8 +197,8 @@ private string getContentSpecific(Content c) {
exists(SyntheticField f | c = TSyntheticFieldContent(f) and result = "SyntheticField[" + f + "]")
}
/** Gets the textual representation of a summary component in the format used for flow summaries. */
string getComponentSpecific(SummaryComponent sc) {
/** Gets the textual representation of a summary component in the format used for MaD models. */
string getMadRepresentationSpecific(SummaryComponent sc) {
exists(Content c | sc = TContentSummaryComponent(c) and result = getContentSpecific(c))
or
sc = TWithoutContentSummaryComponent(_) and result = "WithoutElement"

View File

@@ -410,7 +410,7 @@ module EntityFramework {
) {
this = dbSet.getDbContextClass() and
this.output(output, mapped, dbSet) and
result = dbSet.getFullName() + "#" + SummaryComponentStack::getComponentStack(output)
result = dbSet.getFullName() + "#" + output.getMadRepresentation()
}
}

View File

@@ -0,0 +1,183 @@
/** Definitions for the missing function level access control query */
import csharp
import semmle.code.csharp.frameworks.microsoft.AspNetCore
import semmle.code.csharp.frameworks.system.web.UI
import semmle.code.asp.WebConfig
/** A method representing an action for a web endpoint. */
abstract class ActionMethod extends Method {
/**
* Gets a string that can indicate what this method does to determine if it should have an auth check;
* such as its method name, class name, or file path.
*/
string getADescription() {
result =
[
this.getName(), this.getDeclaringType().getBaseClass*().getName(),
this.getDeclaringType().getFile().getRelativePath()
]
}
/** Holds if this method may need an authorization check. */
predicate needsAuth() {
this.getADescription()
.regexpReplaceAll("([a-z])([A-Z])", "$1_$2")
// separate camelCase words
.toLowerCase()
.regexpMatch(".*(edit|delete|modify|admin|superuser).*")
}
/** Gets a callable for which if it contains an auth check, this method should be considered authenticated. */
Callable getAnAuthorizingCallable() { result = this }
/**
* Gets a possible url route that could refer to this action,
* which would be covered by `<location>` configurations specifying a prefix of it.
*/
string getARoute() { result = this.getDeclaringType().getFile().getRelativePath() }
}
/** An action method in the MVC framework. */
private class MvcActionMethod extends ActionMethod {
MvcActionMethod() { this = any(MicrosoftAspNetCoreMvcController c).getAnActionMethod() }
}
/** An action method on a subclass of `System.Web.UI.Page`. */
private class WebFormActionMethod extends ActionMethod {
WebFormActionMethod() {
this.getDeclaringType().getBaseClass+() instanceof SystemWebUIPageClass and
this.getAParameter().getType().getName().matches("%EventArgs")
}
override Callable getAnAuthorizingCallable() {
result = super.getAnAuthorizingCallable()
or
result.getDeclaringType() = this.getDeclaringType() and
result.getName() = "Page_Load"
}
override string getARoute() {
exists(string physicalRoute | physicalRoute = super.getARoute() |
result = physicalRoute
or
exists(string absolutePhysical |
virtualRouteMapping(result, absolutePhysical) and
physicalRouteMatches(absolutePhysical, physicalRoute)
)
)
}
}
/**
* Holds if `virtualRoute` is a URL path
* that can map to the corresponding `physicalRoute` filepath
* through a call to `MapPageRoute`
*/
private predicate virtualRouteMapping(string virtualRoute, string physicalRoute) {
exists(MethodCall mapPageRouteCall, StringLiteral virtualLit, StringLiteral physicalLit |
mapPageRouteCall
.getTarget()
.hasQualifiedName("System.Web.Routing", "RouteCollection", "MapPageRoute") and
virtualLit = mapPageRouteCall.getArgument(1) and
physicalLit = mapPageRouteCall.getArgument(2) and
virtualLit.getValue() = virtualRoute and
physicalLit.getValue() = physicalRoute
)
}
/** Holds if the filepath `route` can refer to `actual` after expanding a '~". */
bindingset[route, actual]
private predicate physicalRouteMatches(string route, string actual) {
route = actual
or
route.charAt(0) = "~" and
exists(string dir | actual = dir + route.suffix(1) + ".cs")
}
/** An expression that indicates that some authorization/authentication check is being performed. */
class AuthExpr extends Expr {
AuthExpr() {
this.(MethodCall)
.getTarget()
.hasQualifiedName("System.Security.Principal", "IPrincipal", "IsInRole")
or
this.(PropertyAccess)
.getTarget()
.hasQualifiedName("System.Security.Principal", "IIdentity", ["IsAuthenticated", "Name"])
or
this.(MethodCall).getTarget().getName().toLowerCase().matches("%auth%")
or
this.(PropertyAccess).getTarget().getName().toLowerCase().matches("%auth%")
}
}
/** Holds if `m` is a method that should have an auth check, and does indeed have one. */
predicate hasAuthViaCode(ActionMethod m) {
m.needsAuth() and
exists(Callable caller, AuthExpr auth |
m.getAnAuthorizingCallable().calls*(caller) and
auth.getEnclosingCallable() = caller
)
}
/** An `<authorization>` XML element. */
class AuthorizationXmlElement extends XmlElement {
AuthorizationXmlElement() {
this.getParent() instanceof SystemWebXmlElement and
this.getName().toLowerCase() = "authorization"
}
/** Holds if this element has a `<deny>` element to deny access to a resource. */
predicate hasDenyElement() { this.getAChild().getName().toLowerCase() = "deny" }
/** Gets the physical filepath of this element. */
string getPhysicalPath() { result = this.getFile().getParentContainer().getRelativePath() }
/** Gets the path specified by a `<location>` tag containing this element, if any. */
string getLocationTagPath() {
exists(LocationXmlElement loc, XmlAttribute path |
loc = this.getParent().(SystemWebXmlElement).getParent() and
path = loc.getAnAttribute() and
path.getName().toLowerCase() = "path" and
result = path.getValue()
)
}
/** Gets a route prefix that this configuration can refer to. */
string getARoute() {
result = this.getLocationTagPath()
or
result = this.getPhysicalPath() + "/" + this.getLocationTagPath()
or
not exists(this.getLocationTagPath()) and
result = this.getPhysicalPath()
}
}
/**
* Holds if the given action has an xml `authorization` tag that refers to it.
*/
predicate hasAuthViaXml(ActionMethod m) {
exists(AuthorizationXmlElement el, string rest |
el.hasDenyElement() and
m.getARoute() = el.getARoute() + rest
)
}
/** Holds if the given action has an attribute that indications authorization. */
predicate hasAuthViaAttribute(ActionMethod m) {
exists(Attribute attr | attr.getType().getName().toLowerCase().matches("%auth%") |
attr = m.getAnAttribute() or
attr = m.getDeclaringType().getABaseType*().getAnAttribute()
)
}
/** Holds if `m` is a method that should have an auth check, but is missing it. */
predicate missingAuth(ActionMethod m) {
m.needsAuth() and
not hasAuthViaCode(m) and
not hasAuthViaXml(m) and
not hasAuthViaAttribute(m) and
exists(m.getBody().getAChildStmt()) // exclude empty methods
}

View File

@@ -1,3 +1,17 @@
## 0.7.0
### New Queries
* Added a new query, `cs/web/missing-function-level-access-control`, to find instances of missing authorization checks.
### Bug Fixes
* The query "Arbitrary file write during zip extraction ("Zip Slip")" (`cs/zipslip`) has been renamed to "Arbitrary file access during archive extraction ("Zip Slip")."
## 0.6.4
No user-facing changes.
## 0.6.3
No user-facing changes.

View File

@@ -0,0 +1,13 @@
public class ProfileController : Controller {
// BAD: No authorization is used.
public ActionResult Edit(int id) {
...
}
// GOOD: The `Authorize` attribute is used.
[Authorize]
public ActionResult Delete(int id) {
...
}
}

View File

@@ -0,0 +1,54 @@
<!DOCTYPE qhelp PUBLIC
"-//Semmle//qhelp//EN"
"qhelp.dtd">
<qhelp>
<overview>
<p>
Sensitive actions, such as editing or deleting content, or accessing admin pages, should have authorization checks
to ensure that they cannot be used by malicious actors.
</p>
</overview>
<recommendation>
<p>
Ensure that proper authorization checks are made for sensitive actions.
For WebForms applications, the <code>authorization</code> tag in <code>Web.config</code> XML files
can be used to implement access control. The <code>System.Web.UI.Page.User</code> property can also be
used to verify a user's role.
For MVC applications, the <code>Authorize</code> attribute can be used to require authorization on specific
action methods.
</p>
</recommendation>
<example>
<p>
In the following WebForms example, the case marked BAD has no authorization checks whereas the
case marked GOOD uses <code>User.IsInRole</code> to check for the user's role.
</p>
<sample src="WebForms.cs" />
<p>
The following <code>Web.config</code> file uses the <code>authorization</code> tag to deny access to anonymous users,
in a <code>location</code> tag to have that configuration apply to a specific path.
</p>
<sample src="Web.config" />
<p>
In the following MVC example, the case marked BAD has no authorization
checks whereas the case marked GOOD uses the <code>Authorize</code> attribute.
</p>
<sample src="MVC.cs" />
</example>
<references>
<li><code>Page.User</code> Property - <a href="https://learn.microsoft.com/en-us/dotnet/api/system.web.ui.page.user?view=netframework-4.8.1#system-web-ui-page-user">Microsoft Learn</a>.</li>
<li>Control authorization permissions in an ASP.NET application - <a href="https://learn.microsoft.com/en-us/troubleshoot/developer/webapps/aspnet/www-authentication-authorization/authorization-permissions">Microsoft Learn</a>.</li>
<li>Simple authorization in ASP.NET Core - <a href="https://learn.microsoft.com/en-us/aspnet/core/security/authorization/simple?view=aspnetcore-7.0">Microsoft Learn</a>.</li>
</references>
</qhelp>

View File

@@ -0,0 +1,20 @@
/**
* @name Missing function level access control
* @description Sensitive actions should have authorization checks to prevent them from being used by malicious actors.
* @kind problem
* @problem.severity warning
* @security-severity 7.5
* @precision medium
* @id cs/web/missing-function-level-access-control
* @tags security
* external/cwe/cwe-285
* external/cwe/cwe-284
* external/cwe/cwe-862
*/
import csharp
import semmle.code.csharp.security.auth.MissingFunctionLevelAccessControlQuery
from Method m
where missingAuth(m)
select m, "This action is missing an authorization check."

View File

@@ -0,0 +1,11 @@
<?xml version="1.0"?>
<configuration xmlns:xdt="http://schemas.microsoft.com/XML-Document-Transform">
<location path="User/Profile">
<system.web>
<authorization>
<deny users="?" />
</authorization>
</system.web>
</location>
</configuration>

View File

@@ -0,0 +1,14 @@
class ProfilePage : System.Web.UI.Page {
// BAD: No authorization is used
protected void btn1_Edit_Click(object sender, EventArgs e) {
...
}
// GOOD: `User.IsInRole` checks the current user's role.
protected void btn2_Delete_Click(object sender, EventArgs e) {
if (!User.IsInRole("admin")) {
return;
}
...
}
}

View File

@@ -136,7 +136,7 @@ private string nestedName(Declaration declaration) {
/**
* Gets the limit for the number of results produced by a telemetry query.
*/
int resultLimit() { result = 1000 }
int resultLimit() { result = 100 }
/**
* Holds if it is relevant to count usages of `api`.

View File

@@ -0,0 +1,3 @@
## 0.6.4
No user-facing changes.

View File

@@ -1,4 +1,9 @@
---
category: fix
---
## 0.7.0
### New Queries
* Added a new query, `cs/web/missing-function-level-access-control`, to find instances of missing authorization checks.
### Bug Fixes
* The query "Arbitrary file write during zip extraction ("Zip Slip")" (`cs/zipslip`) has been renamed to "Arbitrary file access during archive extraction ("Zip Slip")."

View File

@@ -1,2 +1,2 @@
---
lastReleaseVersion: 0.6.3
lastReleaseVersion: 0.7.0

View File

@@ -176,7 +176,6 @@ private predicate binaryValueNumber0(
)
}
pragma[assume_small_delta]
private predicate binaryValueNumber(
BinaryInstruction instr, IRFunction irFunc, Opcode opcode, TValueNumber leftOperand,
TValueNumber rightOperand
@@ -202,7 +201,6 @@ private predicate pointerArithmeticValueNumber0(
)
}
pragma[assume_small_delta]
private predicate pointerArithmeticValueNumber(
PointerArithmeticInstruction instr, IRFunction irFunc, Opcode opcode, int elementSize,
TValueNumber leftOperand, TValueNumber rightOperand
@@ -249,7 +247,6 @@ private predicate loadTotalOverlapValueNumber0(
)
}
pragma[assume_small_delta]
private predicate loadTotalOverlapValueNumber(
LoadTotalOverlapInstruction instr, IRFunction irFunc, IRType type, TValueNumber memOperand,
TValueNumber operand

View File

@@ -176,7 +176,6 @@ private predicate binaryValueNumber0(
)
}
pragma[assume_small_delta]
private predicate binaryValueNumber(
BinaryInstruction instr, IRFunction irFunc, Opcode opcode, TValueNumber leftOperand,
TValueNumber rightOperand
@@ -202,7 +201,6 @@ private predicate pointerArithmeticValueNumber0(
)
}
pragma[assume_small_delta]
private predicate pointerArithmeticValueNumber(
PointerArithmeticInstruction instr, IRFunction irFunc, Opcode opcode, int elementSize,
TValueNumber leftOperand, TValueNumber rightOperand
@@ -249,7 +247,6 @@ private predicate loadTotalOverlapValueNumber0(
)
}
pragma[assume_small_delta]
private predicate loadTotalOverlapValueNumber(
LoadTotalOverlapInstruction instr, IRFunction irFunc, IRType type, TValueNumber memOperand,
TValueNumber operand

View File

@@ -1,5 +1,5 @@
name: codeql/csharp-queries
version: 0.6.4-dev
version: 0.7.1-dev
groups:
- csharp
- queries

Some files were not shown because too many files have changed in this diff Show More