mirror of
https://github.com/github/codeql.git
synced 2026-04-30 11:15:13 +02:00
Merge branch 'main' into amammad-python-WebAppsConstatntSecretKeys
This commit is contained in:
@@ -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`.
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -1,4 +0,0 @@
|
||||
---
|
||||
category: minorAnalysis
|
||||
---
|
||||
* Deleted the deprecated `getURL` predicate from the `Container`, `Folder`, and `File` classes. Use the `getLocation` predicate instead.
|
||||
@@ -0,0 +1,4 @@
|
||||
---
|
||||
category: minorAnalysis
|
||||
---
|
||||
* The `IRGuards` library has improved handling of pointer addition and subtraction operations.
|
||||
@@ -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.
|
||||
|
||||
3
cpp/ql/lib/change-notes/released/0.7.4.md
Normal file
3
cpp/ql/lib/change-notes/released/0.7.4.md
Normal file
@@ -0,0 +1,3 @@
|
||||
## 0.7.4
|
||||
|
||||
No user-facing changes.
|
||||
9
cpp/ql/lib/change-notes/released/0.8.0.md
Normal file
9
cpp/ql/lib/change-notes/released/0.8.0.md
Normal 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.
|
||||
@@ -1,2 +1,2 @@
|
||||
---
|
||||
lastReleaseVersion: 0.7.3
|
||||
lastReleaseVersion: 0.8.0
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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. */
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -588,7 +588,6 @@ private module Cached {
|
||||
)
|
||||
}
|
||||
|
||||
pragma[assume_small_delta]
|
||||
private predicate convertsIntoArgumentRev(Instruction instr) {
|
||||
convertsIntoArgumentFwd(instr) and
|
||||
(
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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()
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -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())
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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
|
||||
) {
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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.
|
||||
3
cpp/ql/src/change-notes/released/0.6.4.md
Normal file
3
cpp/ql/src/change-notes/released/0.6.4.md
Normal file
@@ -0,0 +1,3 @@
|
||||
## 0.6.4
|
||||
|
||||
No user-facing changes.
|
||||
5
cpp/ql/src/change-notes/released/0.7.0.md
Normal file
5
cpp/ql/src/change-notes/released/0.7.0.md
Normal 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.
|
||||
@@ -1,2 +1,2 @@
|
||||
---
|
||||
lastReleaseVersion: 0.6.3
|
||||
lastReleaseVersion: 0.7.0
|
||||
|
||||
@@ -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, _) }
|
||||
|
||||
@@ -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>;
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
name: codeql/cpp-queries
|
||||
version: 0.6.4-dev
|
||||
version: 0.7.1-dev
|
||||
groups:
|
||||
- cpp
|
||||
- queries
|
||||
|
||||
@@ -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 |
|
||||
|
||||
@@ -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];
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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 |
|
||||
|
||||
@@ -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>:
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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 |
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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 |
|
||||
|
||||
@@ -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 |
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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 |
|
||||
|
||||
@@ -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
|
||||
}
|
||||
@@ -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;
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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.");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
{
|
||||
|
||||
104
csharp/extractor/Semmle.Extraction.Tests/Runtime.cs
Normal file
104
csharp/extractor/Semmle.Extraction.Tests/Runtime.cs
Normal 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));
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -0,0 +1,3 @@
|
||||
## 1.5.4
|
||||
|
||||
No user-facing changes.
|
||||
@@ -0,0 +1,3 @@
|
||||
## 1.6.0
|
||||
|
||||
No user-facing changes.
|
||||
@@ -1,2 +1,2 @@
|
||||
---
|
||||
lastReleaseVersion: 1.5.3
|
||||
lastReleaseVersion: 1.6.0
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
name: codeql/csharp-solorigate-all
|
||||
version: 1.5.4-dev
|
||||
version: 1.6.1-dev
|
||||
groups:
|
||||
- csharp
|
||||
- solorigate
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -0,0 +1,3 @@
|
||||
## 1.5.4
|
||||
|
||||
No user-facing changes.
|
||||
@@ -0,0 +1,3 @@
|
||||
## 1.6.0
|
||||
|
||||
No user-facing changes.
|
||||
@@ -1,2 +1,2 @@
|
||||
---
|
||||
lastReleaseVersion: 1.5.3
|
||||
lastReleaseVersion: 1.6.0
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
name: codeql/csharp-solorigate-queries
|
||||
version: 1.5.4-dev
|
||||
version: 1.6.1-dev
|
||||
groups:
|
||||
- csharp
|
||||
- solorigate
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -1,4 +0,0 @@
|
||||
---
|
||||
category: minorAnalysis
|
||||
---
|
||||
* Additional support for `command-injection`, `ldap-injection`, `log-injection`, and `url-redirection` sink kinds for Models as Data.
|
||||
@@ -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.
|
||||
@@ -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.
|
||||
|
||||
3
csharp/ql/lib/change-notes/released/0.6.4.md
Normal file
3
csharp/ql/lib/change-notes/released/0.6.4.md
Normal file
@@ -0,0 +1,3 @@
|
||||
## 0.6.4
|
||||
|
||||
No user-facing changes.
|
||||
9
csharp/ql/lib/change-notes/released/0.7.0.md
Normal file
9
csharp/ql/lib/change-notes/released/0.7.0.md
Normal 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.
|
||||
@@ -1,2 +1,2 @@
|
||||
---
|
||||
lastReleaseVersion: 0.6.3
|
||||
lastReleaseVersion: 0.7.0
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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
|
||||
)
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
}
|
||||
@@ -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.
|
||||
|
||||
13
csharp/ql/src/Security Features/CWE-285/MVC.cs
Normal file
13
csharp/ql/src/Security Features/CWE-285/MVC.cs
Normal 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) {
|
||||
...
|
||||
}
|
||||
}
|
||||
@@ -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>
|
||||
@@ -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."
|
||||
11
csharp/ql/src/Security Features/CWE-285/Web.config
Normal file
11
csharp/ql/src/Security Features/CWE-285/Web.config
Normal 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>
|
||||
14
csharp/ql/src/Security Features/CWE-285/WebForms.cs
Normal file
14
csharp/ql/src/Security Features/CWE-285/WebForms.cs
Normal 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;
|
||||
}
|
||||
...
|
||||
}
|
||||
}
|
||||
@@ -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`.
|
||||
|
||||
3
csharp/ql/src/change-notes/released/0.6.4.md
Normal file
3
csharp/ql/src/change-notes/released/0.6.4.md
Normal file
@@ -0,0 +1,3 @@
|
||||
## 0.6.4
|
||||
|
||||
No user-facing changes.
|
||||
@@ -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")."
|
||||
@@ -1,2 +1,2 @@
|
||||
---
|
||||
lastReleaseVersion: 0.6.3
|
||||
lastReleaseVersion: 0.7.0
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
Reference in New Issue
Block a user