mirror of
https://github.com/github/codeql.git
synced 2025-12-22 19:56:32 +01:00
C++: merge main and accept test changes
This commit is contained in:
@@ -1,3 +1,16 @@
|
||||
## 0.2.1
|
||||
|
||||
## 0.2.0
|
||||
|
||||
### Breaking Changes
|
||||
|
||||
* The signature of `allowImplicitRead` on `DataFlow::Configuration` and `TaintTracking::Configuration` has changed from `allowImplicitRead(DataFlow::Node node, DataFlow::Content c)` to `allowImplicitRead(DataFlow::Node node, DataFlow::ContentSet c)`.
|
||||
|
||||
### Minor Analysis Improvements
|
||||
|
||||
* More Windows pool allocation functions are now detected as `AllocationFunction`s.
|
||||
* The `semmle.code.cpp.commons.Buffer` library has been enhanced to handle array members of classes that do not specify a size.
|
||||
|
||||
## 0.1.0
|
||||
|
||||
### Breaking Changes
|
||||
|
||||
@@ -0,0 +1,4 @@
|
||||
---
|
||||
category: feature
|
||||
---
|
||||
* A `getInitialization` predicate was added to the `ConstexprIfStmt`, `IfStmt`, and `SwitchStmt` classes that yields the C++17-style initializer of the `if` or `switch` statement when it exists.
|
||||
@@ -1,4 +0,0 @@
|
||||
---
|
||||
category: minorAnalysis
|
||||
---
|
||||
* The `semmle.code.cpp.commons.Buffer` library has been enhanced to handle array members of classes that do not specify a size.
|
||||
@@ -1,4 +0,0 @@
|
||||
---
|
||||
category: breaking
|
||||
---
|
||||
The signature of `allowImplicitRead` on `DataFlow::Configuration` and `TaintTracking::Configuration` has changed from `allowImplicitRead(DataFlow::Node node, DataFlow::Content c)` to `allowImplicitRead(DataFlow::Node node, DataFlow::ContentSet c)`.
|
||||
@@ -1,4 +0,0 @@
|
||||
---
|
||||
category: minorAnalysis
|
||||
---
|
||||
* More Windows pool allocation functions are now detected as `AllocationFunction`s.
|
||||
@@ -0,0 +1,4 @@
|
||||
---
|
||||
category: deprecated
|
||||
---
|
||||
* The `AnalysedString` class in the `StringAnalysis` module has been replaced with `AnalyzedString`, to follow our style guide. The old name still exists as a deprecated alias.
|
||||
10
cpp/ql/lib/change-notes/released/0.2.0.md
Normal file
10
cpp/ql/lib/change-notes/released/0.2.0.md
Normal file
@@ -0,0 +1,10 @@
|
||||
## 0.2.0
|
||||
|
||||
### Breaking Changes
|
||||
|
||||
* The signature of `allowImplicitRead` on `DataFlow::Configuration` and `TaintTracking::Configuration` has changed from `allowImplicitRead(DataFlow::Node node, DataFlow::Content c)` to `allowImplicitRead(DataFlow::Node node, DataFlow::ContentSet c)`.
|
||||
|
||||
### Minor Analysis Improvements
|
||||
|
||||
* More Windows pool allocation functions are now detected as `AllocationFunction`s.
|
||||
* The `semmle.code.cpp.commons.Buffer` library has been enhanced to handle array members of classes that do not specify a size.
|
||||
1
cpp/ql/lib/change-notes/released/0.2.1.md
Normal file
1
cpp/ql/lib/change-notes/released/0.2.1.md
Normal file
@@ -0,0 +1 @@
|
||||
## 0.2.1
|
||||
@@ -1,2 +1,2 @@
|
||||
---
|
||||
lastReleaseVersion: 0.1.0
|
||||
lastReleaseVersion: 0.2.1
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
name: codeql/cpp-all
|
||||
version: 0.1.1-dev
|
||||
version: 0.2.2-dev
|
||||
groups: cpp
|
||||
dbscheme: semmlecode.cpp.dbscheme
|
||||
extractor: cpp
|
||||
|
||||
@@ -31,7 +31,7 @@ class Field extends MemberVariable {
|
||||
int getByteOffset() { fieldoffsets(underlyingElement(this), result, _) }
|
||||
|
||||
/**
|
||||
* Gets the byte offset within `mostDerivedClass` of each occurence of this
|
||||
* Gets the byte offset within `mostDerivedClass` of each occurrence of this
|
||||
* field within `mostDerivedClass` itself or a base class subobject of
|
||||
* `mostDerivedClass`.
|
||||
* Note that for fields of virtual base classes, and non-virtual base classes
|
||||
|
||||
@@ -38,8 +38,8 @@ class Function extends Declaration, ControlFlowNode, AccessHolder, @function {
|
||||
* int z = min(5, 7);
|
||||
* ```
|
||||
* The full signature of the function called on the last line would be
|
||||
* "min<int>(int, int) -> int", and the full signature of the uninstantiated
|
||||
* template on the first line would be "min<T>(T, T) -> T".
|
||||
* `min<int>(int, int) -> int`, and the full signature of the uninstantiated
|
||||
* template on the first line would be `min<T>(T, T) -> T`.
|
||||
*/
|
||||
string getFullSignature() {
|
||||
exists(string name, string templateArgs, string args |
|
||||
|
||||
@@ -663,18 +663,24 @@ private predicate namedStmtChildPredicates(Locatable s, Element e, string pred)
|
||||
or
|
||||
s.(ComputedGotoStmt).getExpr() = e and pred = "getExpr()"
|
||||
or
|
||||
s.(ConstexprIfStmt).getInitialization() = e and pred = "getInitialization()"
|
||||
or
|
||||
s.(ConstexprIfStmt).getCondition() = e and pred = "getCondition()"
|
||||
or
|
||||
s.(ConstexprIfStmt).getThen() = e and pred = "getThen()"
|
||||
or
|
||||
s.(ConstexprIfStmt).getElse() = e and pred = "getElse()"
|
||||
or
|
||||
s.(IfStmt).getInitialization() = e and pred = "getInitialization()"
|
||||
or
|
||||
s.(IfStmt).getCondition() = e and pred = "getCondition()"
|
||||
or
|
||||
s.(IfStmt).getThen() = e and pred = "getThen()"
|
||||
or
|
||||
s.(IfStmt).getElse() = e and pred = "getElse()"
|
||||
or
|
||||
s.(SwitchStmt).getInitialization() = e and pred = "getInitialization()"
|
||||
or
|
||||
s.(SwitchStmt).getExpr() = e and pred = "getExpr()"
|
||||
or
|
||||
s.(SwitchStmt).getStmt() = e and pred = "getStmt()"
|
||||
|
||||
@@ -872,7 +872,7 @@ class FormatLiteral extends Literal {
|
||||
|
||||
private Type getConversionType1(int n) {
|
||||
exists(string cnv | cnv = this.getConversionChar(n) |
|
||||
cnv.regexpMatch("d|i") and
|
||||
cnv = ["d", "i"] and
|
||||
result = this.getIntegralConversion(n) and
|
||||
not result.getUnderlyingType().(IntegralType).isExplicitlySigned() and
|
||||
not result.getUnderlyingType().(IntegralType).isExplicitlyUnsigned()
|
||||
@@ -912,7 +912,7 @@ class FormatLiteral extends Literal {
|
||||
|
||||
private Type getConversionType2(int n) {
|
||||
exists(string cnv | cnv = this.getConversionChar(n) |
|
||||
cnv.regexpMatch("o|u|x|X") and
|
||||
cnv = ["o", "u", "x", "X"] and
|
||||
result = this.getIntegralConversion(n) and
|
||||
result.getUnderlyingType().(IntegralType).isUnsigned()
|
||||
)
|
||||
@@ -920,7 +920,7 @@ class FormatLiteral extends Literal {
|
||||
|
||||
private Type getConversionType3(int n) {
|
||||
exists(string cnv | cnv = this.getConversionChar(n) |
|
||||
cnv.regexpMatch("a|A|e|E|f|F|g|G") and result = this.getFloatingPointConversion(n)
|
||||
cnv = ["a", "A", "e", "E", "f", "F", "g", "G"] and result = this.getFloatingPointConversion(n)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1312,7 +1312,7 @@ class FormatLiteral extends Literal {
|
||||
len =
|
||||
min(int v |
|
||||
v = this.getPrecision(n) or
|
||||
v = this.getUse().getFormatArgument(n).(AnalysedString).getMaxLength() - 1 // (don't count null terminator)
|
||||
v = this.getUse().getFormatArgument(n).(AnalyzedString).getMaxLength() - 1 // (don't count null terminator)
|
||||
) and
|
||||
reason = TValueFlowAnalysis()
|
||||
)
|
||||
|
||||
@@ -27,11 +27,14 @@ predicate canValueFlow(Expr fromExpr, Expr toExpr) {
|
||||
fromExpr = toExpr.(ConditionalExpr).getElse()
|
||||
}
|
||||
|
||||
/** DEPRECATED: Alias for AnalyzedString */
|
||||
deprecated class AnalysedString = AnalyzedString;
|
||||
|
||||
/**
|
||||
* An analysed null terminated string.
|
||||
* An analyzed null terminated string.
|
||||
*/
|
||||
class AnalysedString extends Expr {
|
||||
AnalysedString() {
|
||||
class AnalyzedString extends Expr {
|
||||
AnalyzedString() {
|
||||
this.getUnspecifiedType() instanceof ArrayType or
|
||||
this.getUnspecifiedType() instanceof PointerType
|
||||
}
|
||||
@@ -41,15 +44,15 @@ class AnalysedString extends Expr {
|
||||
* can be calculated.
|
||||
*/
|
||||
int getMaxLength() {
|
||||
// take the longest AnalysedString it's value could 'flow' from; however if even one doesn't
|
||||
// take the longest AnalyzedString its value could 'flow' from; however if even one doesn't
|
||||
// return a value (this essentially means 'infinity') we can't return a value either.
|
||||
result =
|
||||
max(AnalysedString expr, int toMax |
|
||||
max(AnalyzedString expr, int toMax |
|
||||
canValueFlow*(expr, this) and toMax = expr.(StringLiteral).getOriginalLength()
|
||||
|
|
||||
toMax
|
||||
) and // maximum length
|
||||
forall(AnalysedString expr | canValueFlow(expr, this) | exists(expr.getMaxLength())) // all sources return a value (recursive)
|
||||
forall(AnalyzedString expr | canValueFlow(expr, this) | exists(expr.getMaxLength())) // all sources return a value (recursive)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -708,30 +708,33 @@ private predicate straightLineSparse(Node scope, int i, Node ni, Spec spec) {
|
||||
or
|
||||
scope =
|
||||
any(SwitchStmt s |
|
||||
// SwitchStmt [-> init] -> expr
|
||||
i = -1 and ni = s and spec.isAt()
|
||||
or
|
||||
i = 0 and ni = s.getExpr() and spec.isAround()
|
||||
i = 0 and ni = s.getInitialization() and spec.isAround()
|
||||
or
|
||||
i = 1 and ni = s.getExpr() and spec.isAround()
|
||||
or
|
||||
// If the switch body is not a block then this step is skipped, and the
|
||||
// expression jumps directly to the cases.
|
||||
i = 1 and ni = s.getStmt().(BlockStmt) and spec.isAt()
|
||||
i = 2 and ni = s.getStmt().(BlockStmt) and spec.isAt()
|
||||
or
|
||||
i = 2 and ni = s.getASwitchCase() and spec.isBefore()
|
||||
i = 3 and ni = s.getASwitchCase() and spec.isBefore()
|
||||
or
|
||||
// If there is no default case, we can jump to after the block. Note: `i`
|
||||
// is same value as above.
|
||||
not s.getASwitchCase() instanceof DefaultCase and
|
||||
i = 2 and
|
||||
i = 3 and
|
||||
ni = s.getStmt() and
|
||||
spec.isAfter()
|
||||
or
|
||||
i = 3 and /* BARRIER */ ni = s and spec.isBarrier()
|
||||
i = 4 and /* BARRIER */ ni = s and spec.isBarrier()
|
||||
or
|
||||
i = 4 and ni = s.getStmt() and spec.isAfter()
|
||||
i = 5 and ni = s.getStmt() and spec.isAfter()
|
||||
or
|
||||
i = 5 and ni = s and spec.isAroundDestructors()
|
||||
i = 6 and ni = s and spec.isAroundDestructors()
|
||||
or
|
||||
i = 6 and ni = s and spec.isAfter()
|
||||
i = 7 and ni = s and spec.isAfter()
|
||||
)
|
||||
or
|
||||
scope =
|
||||
@@ -836,8 +839,15 @@ private predicate subEdge(Pos p1, Node n1, Node n2, Pos p2) {
|
||||
p2.nodeAt(n2, f)
|
||||
)
|
||||
or
|
||||
// IfStmt -> condition ; { then, else } ->
|
||||
// IfStmt -> [ init -> ] condition ; { then, else } ->
|
||||
exists(IfStmt s |
|
||||
p1.nodeAt(n1, s) and
|
||||
p2.nodeBefore(n2, s.getInitialization())
|
||||
or
|
||||
p1.nodeAfter(n1, s.getInitialization()) and
|
||||
p2.nodeBefore(n2, s.getCondition())
|
||||
or
|
||||
not exists(s.getInitialization()) and
|
||||
p1.nodeAt(n1, s) and
|
||||
p2.nodeBefore(n2, s.getCondition())
|
||||
or
|
||||
@@ -851,8 +861,15 @@ private predicate subEdge(Pos p1, Node n1, Node n2, Pos p2) {
|
||||
p2.nodeAfter(n2, s)
|
||||
)
|
||||
or
|
||||
// ConstexprIfStmt -> condition ; { then, else } -> // same as IfStmt
|
||||
// ConstexprIfStmt -> [ init -> ] condition ; { then, else } -> // same as IfStmt
|
||||
exists(ConstexprIfStmt s |
|
||||
p1.nodeAt(n1, s) and
|
||||
p2.nodeBefore(n2, s.getInitialization())
|
||||
or
|
||||
p1.nodeAfter(n1, s.getInitialization()) and
|
||||
p2.nodeBefore(n2, s.getCondition())
|
||||
or
|
||||
not exists(s.getInitialization()) and
|
||||
p1.nodeAt(n1, s) and
|
||||
p2.nodeBefore(n2, s.getCondition())
|
||||
or
|
||||
@@ -953,7 +970,7 @@ private predicate subEdge(Pos p1, Node n1, Node n2, Pos p2) {
|
||||
private predicate subEdgeIncludingDestructors(Pos p1, Node n1, Node n2, Pos p2) {
|
||||
subEdge(p1, n1, n2, p2)
|
||||
or
|
||||
// If `n1` has sub-nodes to accomodate destructors, but there are none to be
|
||||
// If `n1` has sub-nodes to accommodate destructors, but there are none to be
|
||||
// called, connect the "before destructors" node directly to the "after
|
||||
// destructors" node. For performance, only do this when the nodes exist.
|
||||
exists(Pos afterDtors | afterDtors.isAfterDestructors() | subEdge(afterDtors, n1, _, _)) and
|
||||
|
||||
@@ -170,6 +170,14 @@ abstract class Configuration extends string {
|
||||
*/
|
||||
int explorationLimit() { none() }
|
||||
|
||||
/**
|
||||
* Holds if hidden nodes should be included in the data flow graph.
|
||||
*
|
||||
* This feature should only be used for debugging or when the data flow graph
|
||||
* is not visualized (for example in a `path-problem` query).
|
||||
*/
|
||||
predicate includeHiddenNodes() { none() }
|
||||
|
||||
/**
|
||||
* Holds if there is a partial data flow path from `source` to `node`. The
|
||||
* approximate distance between `node` and the closest source is `dist` and
|
||||
@@ -498,23 +506,35 @@ private predicate readSet(NodeEx node1, ContentSet c, NodeEx node2, Configuratio
|
||||
}
|
||||
|
||||
// inline to reduce fan-out via `getAReadContent`
|
||||
pragma[inline]
|
||||
bindingset[c]
|
||||
private predicate read(NodeEx node1, Content c, NodeEx node2, Configuration config) {
|
||||
exists(ContentSet cs |
|
||||
readSet(node1, cs, node2, config) and
|
||||
c = cs.getAReadContent()
|
||||
pragma[only_bind_out](c) = pragma[only_bind_into](cs).getAReadContent()
|
||||
)
|
||||
}
|
||||
|
||||
// inline to reduce fan-out via `getAReadContent`
|
||||
pragma[inline]
|
||||
bindingset[c]
|
||||
private predicate clearsContentEx(NodeEx n, Content c) {
|
||||
exists(ContentSet cs |
|
||||
clearsContentCached(n.asNode(), cs) and
|
||||
c = cs.getAReadContent()
|
||||
pragma[only_bind_out](c) = pragma[only_bind_into](cs).getAReadContent()
|
||||
)
|
||||
}
|
||||
|
||||
// inline to reduce fan-out via `getAReadContent`
|
||||
bindingset[c]
|
||||
private predicate expectsContentEx(NodeEx n, Content c) {
|
||||
exists(ContentSet cs |
|
||||
expectsContentCached(n.asNode(), cs) and
|
||||
pragma[only_bind_out](c) = pragma[only_bind_into](cs).getAReadContent()
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate notExpectsContent(NodeEx n) { not expectsContentCached(n.asNode(), _) }
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate store(
|
||||
NodeEx node1, TypedContent tc, NodeEx node2, DataFlowType contentType, Configuration config
|
||||
@@ -793,7 +813,7 @@ private module Stage1 {
|
||||
* by `revFlow`.
|
||||
*/
|
||||
pragma[nomagic]
|
||||
private predicate revFlowIsReadAndStored(Content c, Configuration conf) {
|
||||
predicate revFlowIsReadAndStored(Content c, Configuration conf) {
|
||||
revFlowConsCand(c, conf) and
|
||||
revFlowStore(c, _, _, conf)
|
||||
}
|
||||
@@ -891,7 +911,7 @@ private module Stage1 {
|
||||
|
||||
pragma[nomagic]
|
||||
predicate readStepCand(NodeEx n1, Content c, NodeEx n2, Configuration config) {
|
||||
revFlowIsReadAndStored(pragma[only_bind_into](c), pragma[only_bind_into](config)) and
|
||||
revFlowIsReadAndStored(c, pragma[only_bind_into](config)) and
|
||||
read(n1, c, n2, pragma[only_bind_into](config)) and
|
||||
revFlow(n2, pragma[only_bind_into](config))
|
||||
}
|
||||
@@ -1181,11 +1201,26 @@ private module Stage2 {
|
||||
|
||||
private predicate flowIntoCall = flowIntoCallNodeCand1/5;
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate expectsContentCand(NodeEx node, Configuration config) {
|
||||
exists(Content c |
|
||||
PrevStage::revFlow(node, pragma[only_bind_into](config)) and
|
||||
PrevStage::revFlowIsReadAndStored(c, pragma[only_bind_into](config)) and
|
||||
expectsContentEx(node, c)
|
||||
)
|
||||
}
|
||||
|
||||
bindingset[node, state, ap, config]
|
||||
private predicate filter(NodeEx node, FlowState state, Ap ap, Configuration config) {
|
||||
PrevStage::revFlowState(state, pragma[only_bind_into](config)) and
|
||||
exists(ap) and
|
||||
not stateBarrier(node, state, config)
|
||||
not stateBarrier(node, state, config) and
|
||||
(
|
||||
notExpectsContent(node)
|
||||
or
|
||||
ap = true and
|
||||
expectsContentCand(node, config)
|
||||
)
|
||||
}
|
||||
|
||||
bindingset[ap, contentType]
|
||||
@@ -1646,10 +1681,24 @@ private module Stage2 {
|
||||
storeStepFwd(_, ap, tc, _, _, config)
|
||||
}
|
||||
|
||||
predicate consCand(TypedContent tc, Ap ap, Configuration config) {
|
||||
private predicate revConsCand(TypedContent tc, Ap ap, Configuration config) {
|
||||
storeStepCand(_, ap, tc, _, _, config)
|
||||
}
|
||||
|
||||
private predicate validAp(Ap ap, Configuration config) {
|
||||
revFlow(_, _, _, _, ap, config) and ap instanceof ApNil
|
||||
or
|
||||
exists(TypedContent head, Ap tail |
|
||||
consCand(head, tail, config) and
|
||||
ap = apCons(head, tail)
|
||||
)
|
||||
}
|
||||
|
||||
predicate consCand(TypedContent tc, Ap ap, Configuration config) {
|
||||
revConsCand(tc, ap, config) and
|
||||
validAp(ap, config)
|
||||
}
|
||||
|
||||
pragma[noinline]
|
||||
private predicate parameterFlow(
|
||||
ParamNodeEx p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config
|
||||
@@ -1740,7 +1789,8 @@ private module LocalFlowBigStep {
|
||||
private class FlowCheckNode extends NodeEx {
|
||||
FlowCheckNode() {
|
||||
castNode(this.asNode()) or
|
||||
clearsContentCached(this.asNode(), _)
|
||||
clearsContentCached(this.asNode(), _) or
|
||||
expectsContentCached(this.asNode(), _)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1979,6 +2029,16 @@ private module Stage3 {
|
||||
clearContent(node, ap.getHead().getContent(), config)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate expectsContentCand(NodeEx node, Ap ap, Configuration config) {
|
||||
exists(Content c |
|
||||
PrevStage::revFlow(node, pragma[only_bind_into](config)) and
|
||||
PrevStage::readStepCand(_, c, _, pragma[only_bind_into](config)) and
|
||||
expectsContentEx(node, c) and
|
||||
c = ap.getHead().getContent()
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate castingNodeEx(NodeEx node) { node.asNode() instanceof CastingNode }
|
||||
|
||||
@@ -1987,7 +2047,12 @@ private module Stage3 {
|
||||
exists(state) and
|
||||
exists(config) and
|
||||
not clear(node, ap, config) and
|
||||
if castingNodeEx(node) then compatibleTypes(node.getDataFlowType(), ap.getType()) else any()
|
||||
(if castingNodeEx(node) then compatibleTypes(node.getDataFlowType(), ap.getType()) else any()) and
|
||||
(
|
||||
notExpectsContent(node)
|
||||
or
|
||||
expectsContentCand(node, ap, config)
|
||||
)
|
||||
}
|
||||
|
||||
bindingset[ap, contentType]
|
||||
@@ -2452,10 +2517,24 @@ private module Stage3 {
|
||||
storeStepFwd(_, ap, tc, _, _, config)
|
||||
}
|
||||
|
||||
predicate consCand(TypedContent tc, Ap ap, Configuration config) {
|
||||
private predicate revConsCand(TypedContent tc, Ap ap, Configuration config) {
|
||||
storeStepCand(_, ap, tc, _, _, config)
|
||||
}
|
||||
|
||||
private predicate validAp(Ap ap, Configuration config) {
|
||||
revFlow(_, _, _, _, ap, config) and ap instanceof ApNil
|
||||
or
|
||||
exists(TypedContent head, Ap tail |
|
||||
consCand(head, tail, config) and
|
||||
ap = apCons(head, tail)
|
||||
)
|
||||
}
|
||||
|
||||
predicate consCand(TypedContent tc, Ap ap, Configuration config) {
|
||||
revConsCand(tc, ap, config) and
|
||||
validAp(ap, config)
|
||||
}
|
||||
|
||||
pragma[noinline]
|
||||
private predicate parameterFlow(
|
||||
ParamNodeEx p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config
|
||||
@@ -3279,10 +3358,24 @@ private module Stage4 {
|
||||
storeStepFwd(_, ap, tc, _, _, config)
|
||||
}
|
||||
|
||||
predicate consCand(TypedContent tc, Ap ap, Configuration config) {
|
||||
private predicate revConsCand(TypedContent tc, Ap ap, Configuration config) {
|
||||
storeStepCand(_, ap, tc, _, _, config)
|
||||
}
|
||||
|
||||
private predicate validAp(Ap ap, Configuration config) {
|
||||
revFlow(_, _, _, _, ap, config) and ap instanceof ApNil
|
||||
or
|
||||
exists(TypedContent head, Ap tail |
|
||||
consCand(head, tail, config) and
|
||||
ap = apCons(head, tail)
|
||||
)
|
||||
}
|
||||
|
||||
predicate consCand(TypedContent tc, Ap ap, Configuration config) {
|
||||
revConsCand(tc, ap, config) and
|
||||
validAp(ap, config)
|
||||
}
|
||||
|
||||
pragma[noinline]
|
||||
private predicate parameterFlow(
|
||||
ParamNodeEx p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config
|
||||
@@ -3351,17 +3444,28 @@ private Configuration unbindConf(Configuration conf) {
|
||||
exists(Configuration c | result = pragma[only_bind_into](c) and conf = pragma[only_bind_into](c))
|
||||
}
|
||||
|
||||
private predicate nodeMayUseSummary(
|
||||
NodeEx n, FlowState state, AccessPathApprox apa, Configuration config
|
||||
pragma[nomagic]
|
||||
private predicate nodeMayUseSummary0(
|
||||
NodeEx n, DataFlowCallable c, FlowState state, AccessPathApprox apa, Configuration config
|
||||
) {
|
||||
exists(DataFlowCallable c, AccessPathApprox apa0 |
|
||||
Stage4::parameterMayFlowThrough(_, c, apa, _) and
|
||||
exists(AccessPathApprox apa0 |
|
||||
Stage4::parameterMayFlowThrough(_, c, _, _) and
|
||||
Stage4::revFlow(n, state, true, _, apa0, config) and
|
||||
Stage4::fwdFlow(n, state, any(CallContextCall ccc), TAccessPathApproxSome(apa), apa0, config) and
|
||||
n.getEnclosingCallable() = c
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate nodeMayUseSummary(
|
||||
NodeEx n, FlowState state, AccessPathApprox apa, Configuration config
|
||||
) {
|
||||
exists(DataFlowCallable c |
|
||||
Stage4::parameterMayFlowThrough(_, c, apa, config) and
|
||||
nodeMayUseSummary0(n, c, state, apa, config)
|
||||
)
|
||||
}
|
||||
|
||||
private newtype TSummaryCtx =
|
||||
TSummaryCtxNone() or
|
||||
TSummaryCtxSome(ParamNodeEx p, FlowState state, AccessPath ap) {
|
||||
@@ -3557,7 +3661,7 @@ private newtype TPathNode =
|
||||
* of dereference operations needed to get from the value in the node to the
|
||||
* tracked object. The final type indicates the type of the tracked object.
|
||||
*/
|
||||
abstract private class AccessPath extends TAccessPath {
|
||||
private class AccessPath extends TAccessPath {
|
||||
/** Gets the head of this access path, if any. */
|
||||
abstract TypedContent getHead();
|
||||
|
||||
@@ -3772,11 +3876,14 @@ abstract private class PathNodeImpl extends PathNode {
|
||||
abstract NodeEx getNodeEx();
|
||||
|
||||
predicate isHidden() {
|
||||
hiddenNode(this.getNodeEx().asNode()) and
|
||||
not this.isSource() and
|
||||
not this instanceof PathNodeSink
|
||||
or
|
||||
this.getNodeEx() instanceof TNodeImplicitRead
|
||||
not this.getConfiguration().includeHiddenNodes() and
|
||||
(
|
||||
hiddenNode(this.getNodeEx().asNode()) and
|
||||
not this.isSource() and
|
||||
not this instanceof PathNodeSink
|
||||
or
|
||||
this.getNodeEx() instanceof TNodeImplicitRead
|
||||
)
|
||||
}
|
||||
|
||||
private string ppAp() {
|
||||
@@ -4257,6 +4364,12 @@ private module Subpaths {
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate hasSuccessor(PathNode pred, PathNodeMid succ, NodeEx succNode) {
|
||||
succ = pred.getASuccessor() and
|
||||
succNode = succ.getNodeEx()
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `(arg, par, ret, out)` forms a subpath-tuple, that is, flow through
|
||||
* a subpath between `par` and `ret` with the connecting edges `arg -> par` and
|
||||
@@ -4264,15 +4377,13 @@ private module Subpaths {
|
||||
*/
|
||||
predicate subpaths(PathNode arg, PathNodeImpl par, PathNodeImpl ret, PathNode out) {
|
||||
exists(ParamNodeEx p, NodeEx o, FlowState sout, AccessPath apout, PathNodeMid out0 |
|
||||
pragma[only_bind_into](arg).getASuccessor() = par and
|
||||
pragma[only_bind_into](arg).getASuccessor() = out0 and
|
||||
subpaths03(arg, p, localStepToHidden*(ret), o, sout, apout) and
|
||||
pragma[only_bind_into](arg).getASuccessor() = pragma[only_bind_into](out0) and
|
||||
subpaths03(pragma[only_bind_into](arg), p, localStepToHidden*(ret), o, sout, apout) and
|
||||
hasSuccessor(pragma[only_bind_into](arg), par, p) and
|
||||
not ret.isHidden() and
|
||||
par.getNodeEx() = p and
|
||||
out0.getNodeEx() = o and
|
||||
out0.getState() = sout and
|
||||
out0.getAp() = apout and
|
||||
(out = out0 or out = out0.projectToSink())
|
||||
pathNode(out0, o, sout, _, _, apout, _, _)
|
||||
|
|
||||
out = out0 or out = out0.projectToSink()
|
||||
)
|
||||
}
|
||||
|
||||
@@ -4609,6 +4720,10 @@ private module FlowExploration {
|
||||
exists(PartialPathNodeRev mid |
|
||||
revPartialPathStep(mid, node, state, sc1, sc2, sc3, ap, config) and
|
||||
not clearsContentEx(node, ap.getHead()) and
|
||||
(
|
||||
notExpectsContent(node) or
|
||||
expectsContentEx(node, ap.getHead())
|
||||
) and
|
||||
not fullBarrier(node, config) and
|
||||
not stateBarrier(node, state, config) and
|
||||
distSink(node.getEnclosingCallable(), config) <= config.explorationLimit()
|
||||
@@ -4625,6 +4740,10 @@ private module FlowExploration {
|
||||
not fullBarrier(node, config) and
|
||||
not stateBarrier(node, state, config) and
|
||||
not clearsContentEx(node, ap.getHead().getContent()) and
|
||||
(
|
||||
notExpectsContent(node) or
|
||||
expectsContentEx(node, ap.getHead().getContent())
|
||||
) and
|
||||
if node.asNode() instanceof CastingNode
|
||||
then compatibleTypes(node.getDataFlowType(), ap.getType())
|
||||
else any()
|
||||
|
||||
@@ -170,6 +170,14 @@ abstract class Configuration extends string {
|
||||
*/
|
||||
int explorationLimit() { none() }
|
||||
|
||||
/**
|
||||
* Holds if hidden nodes should be included in the data flow graph.
|
||||
*
|
||||
* This feature should only be used for debugging or when the data flow graph
|
||||
* is not visualized (for example in a `path-problem` query).
|
||||
*/
|
||||
predicate includeHiddenNodes() { none() }
|
||||
|
||||
/**
|
||||
* Holds if there is a partial data flow path from `source` to `node`. The
|
||||
* approximate distance between `node` and the closest source is `dist` and
|
||||
@@ -498,23 +506,35 @@ private predicate readSet(NodeEx node1, ContentSet c, NodeEx node2, Configuratio
|
||||
}
|
||||
|
||||
// inline to reduce fan-out via `getAReadContent`
|
||||
pragma[inline]
|
||||
bindingset[c]
|
||||
private predicate read(NodeEx node1, Content c, NodeEx node2, Configuration config) {
|
||||
exists(ContentSet cs |
|
||||
readSet(node1, cs, node2, config) and
|
||||
c = cs.getAReadContent()
|
||||
pragma[only_bind_out](c) = pragma[only_bind_into](cs).getAReadContent()
|
||||
)
|
||||
}
|
||||
|
||||
// inline to reduce fan-out via `getAReadContent`
|
||||
pragma[inline]
|
||||
bindingset[c]
|
||||
private predicate clearsContentEx(NodeEx n, Content c) {
|
||||
exists(ContentSet cs |
|
||||
clearsContentCached(n.asNode(), cs) and
|
||||
c = cs.getAReadContent()
|
||||
pragma[only_bind_out](c) = pragma[only_bind_into](cs).getAReadContent()
|
||||
)
|
||||
}
|
||||
|
||||
// inline to reduce fan-out via `getAReadContent`
|
||||
bindingset[c]
|
||||
private predicate expectsContentEx(NodeEx n, Content c) {
|
||||
exists(ContentSet cs |
|
||||
expectsContentCached(n.asNode(), cs) and
|
||||
pragma[only_bind_out](c) = pragma[only_bind_into](cs).getAReadContent()
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate notExpectsContent(NodeEx n) { not expectsContentCached(n.asNode(), _) }
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate store(
|
||||
NodeEx node1, TypedContent tc, NodeEx node2, DataFlowType contentType, Configuration config
|
||||
@@ -793,7 +813,7 @@ private module Stage1 {
|
||||
* by `revFlow`.
|
||||
*/
|
||||
pragma[nomagic]
|
||||
private predicate revFlowIsReadAndStored(Content c, Configuration conf) {
|
||||
predicate revFlowIsReadAndStored(Content c, Configuration conf) {
|
||||
revFlowConsCand(c, conf) and
|
||||
revFlowStore(c, _, _, conf)
|
||||
}
|
||||
@@ -891,7 +911,7 @@ private module Stage1 {
|
||||
|
||||
pragma[nomagic]
|
||||
predicate readStepCand(NodeEx n1, Content c, NodeEx n2, Configuration config) {
|
||||
revFlowIsReadAndStored(pragma[only_bind_into](c), pragma[only_bind_into](config)) and
|
||||
revFlowIsReadAndStored(c, pragma[only_bind_into](config)) and
|
||||
read(n1, c, n2, pragma[only_bind_into](config)) and
|
||||
revFlow(n2, pragma[only_bind_into](config))
|
||||
}
|
||||
@@ -1181,11 +1201,26 @@ private module Stage2 {
|
||||
|
||||
private predicate flowIntoCall = flowIntoCallNodeCand1/5;
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate expectsContentCand(NodeEx node, Configuration config) {
|
||||
exists(Content c |
|
||||
PrevStage::revFlow(node, pragma[only_bind_into](config)) and
|
||||
PrevStage::revFlowIsReadAndStored(c, pragma[only_bind_into](config)) and
|
||||
expectsContentEx(node, c)
|
||||
)
|
||||
}
|
||||
|
||||
bindingset[node, state, ap, config]
|
||||
private predicate filter(NodeEx node, FlowState state, Ap ap, Configuration config) {
|
||||
PrevStage::revFlowState(state, pragma[only_bind_into](config)) and
|
||||
exists(ap) and
|
||||
not stateBarrier(node, state, config)
|
||||
not stateBarrier(node, state, config) and
|
||||
(
|
||||
notExpectsContent(node)
|
||||
or
|
||||
ap = true and
|
||||
expectsContentCand(node, config)
|
||||
)
|
||||
}
|
||||
|
||||
bindingset[ap, contentType]
|
||||
@@ -1646,10 +1681,24 @@ private module Stage2 {
|
||||
storeStepFwd(_, ap, tc, _, _, config)
|
||||
}
|
||||
|
||||
predicate consCand(TypedContent tc, Ap ap, Configuration config) {
|
||||
private predicate revConsCand(TypedContent tc, Ap ap, Configuration config) {
|
||||
storeStepCand(_, ap, tc, _, _, config)
|
||||
}
|
||||
|
||||
private predicate validAp(Ap ap, Configuration config) {
|
||||
revFlow(_, _, _, _, ap, config) and ap instanceof ApNil
|
||||
or
|
||||
exists(TypedContent head, Ap tail |
|
||||
consCand(head, tail, config) and
|
||||
ap = apCons(head, tail)
|
||||
)
|
||||
}
|
||||
|
||||
predicate consCand(TypedContent tc, Ap ap, Configuration config) {
|
||||
revConsCand(tc, ap, config) and
|
||||
validAp(ap, config)
|
||||
}
|
||||
|
||||
pragma[noinline]
|
||||
private predicate parameterFlow(
|
||||
ParamNodeEx p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config
|
||||
@@ -1740,7 +1789,8 @@ private module LocalFlowBigStep {
|
||||
private class FlowCheckNode extends NodeEx {
|
||||
FlowCheckNode() {
|
||||
castNode(this.asNode()) or
|
||||
clearsContentCached(this.asNode(), _)
|
||||
clearsContentCached(this.asNode(), _) or
|
||||
expectsContentCached(this.asNode(), _)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1979,6 +2029,16 @@ private module Stage3 {
|
||||
clearContent(node, ap.getHead().getContent(), config)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate expectsContentCand(NodeEx node, Ap ap, Configuration config) {
|
||||
exists(Content c |
|
||||
PrevStage::revFlow(node, pragma[only_bind_into](config)) and
|
||||
PrevStage::readStepCand(_, c, _, pragma[only_bind_into](config)) and
|
||||
expectsContentEx(node, c) and
|
||||
c = ap.getHead().getContent()
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate castingNodeEx(NodeEx node) { node.asNode() instanceof CastingNode }
|
||||
|
||||
@@ -1987,7 +2047,12 @@ private module Stage3 {
|
||||
exists(state) and
|
||||
exists(config) and
|
||||
not clear(node, ap, config) and
|
||||
if castingNodeEx(node) then compatibleTypes(node.getDataFlowType(), ap.getType()) else any()
|
||||
(if castingNodeEx(node) then compatibleTypes(node.getDataFlowType(), ap.getType()) else any()) and
|
||||
(
|
||||
notExpectsContent(node)
|
||||
or
|
||||
expectsContentCand(node, ap, config)
|
||||
)
|
||||
}
|
||||
|
||||
bindingset[ap, contentType]
|
||||
@@ -2452,10 +2517,24 @@ private module Stage3 {
|
||||
storeStepFwd(_, ap, tc, _, _, config)
|
||||
}
|
||||
|
||||
predicate consCand(TypedContent tc, Ap ap, Configuration config) {
|
||||
private predicate revConsCand(TypedContent tc, Ap ap, Configuration config) {
|
||||
storeStepCand(_, ap, tc, _, _, config)
|
||||
}
|
||||
|
||||
private predicate validAp(Ap ap, Configuration config) {
|
||||
revFlow(_, _, _, _, ap, config) and ap instanceof ApNil
|
||||
or
|
||||
exists(TypedContent head, Ap tail |
|
||||
consCand(head, tail, config) and
|
||||
ap = apCons(head, tail)
|
||||
)
|
||||
}
|
||||
|
||||
predicate consCand(TypedContent tc, Ap ap, Configuration config) {
|
||||
revConsCand(tc, ap, config) and
|
||||
validAp(ap, config)
|
||||
}
|
||||
|
||||
pragma[noinline]
|
||||
private predicate parameterFlow(
|
||||
ParamNodeEx p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config
|
||||
@@ -3279,10 +3358,24 @@ private module Stage4 {
|
||||
storeStepFwd(_, ap, tc, _, _, config)
|
||||
}
|
||||
|
||||
predicate consCand(TypedContent tc, Ap ap, Configuration config) {
|
||||
private predicate revConsCand(TypedContent tc, Ap ap, Configuration config) {
|
||||
storeStepCand(_, ap, tc, _, _, config)
|
||||
}
|
||||
|
||||
private predicate validAp(Ap ap, Configuration config) {
|
||||
revFlow(_, _, _, _, ap, config) and ap instanceof ApNil
|
||||
or
|
||||
exists(TypedContent head, Ap tail |
|
||||
consCand(head, tail, config) and
|
||||
ap = apCons(head, tail)
|
||||
)
|
||||
}
|
||||
|
||||
predicate consCand(TypedContent tc, Ap ap, Configuration config) {
|
||||
revConsCand(tc, ap, config) and
|
||||
validAp(ap, config)
|
||||
}
|
||||
|
||||
pragma[noinline]
|
||||
private predicate parameterFlow(
|
||||
ParamNodeEx p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config
|
||||
@@ -3351,17 +3444,28 @@ private Configuration unbindConf(Configuration conf) {
|
||||
exists(Configuration c | result = pragma[only_bind_into](c) and conf = pragma[only_bind_into](c))
|
||||
}
|
||||
|
||||
private predicate nodeMayUseSummary(
|
||||
NodeEx n, FlowState state, AccessPathApprox apa, Configuration config
|
||||
pragma[nomagic]
|
||||
private predicate nodeMayUseSummary0(
|
||||
NodeEx n, DataFlowCallable c, FlowState state, AccessPathApprox apa, Configuration config
|
||||
) {
|
||||
exists(DataFlowCallable c, AccessPathApprox apa0 |
|
||||
Stage4::parameterMayFlowThrough(_, c, apa, _) and
|
||||
exists(AccessPathApprox apa0 |
|
||||
Stage4::parameterMayFlowThrough(_, c, _, _) and
|
||||
Stage4::revFlow(n, state, true, _, apa0, config) and
|
||||
Stage4::fwdFlow(n, state, any(CallContextCall ccc), TAccessPathApproxSome(apa), apa0, config) and
|
||||
n.getEnclosingCallable() = c
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate nodeMayUseSummary(
|
||||
NodeEx n, FlowState state, AccessPathApprox apa, Configuration config
|
||||
) {
|
||||
exists(DataFlowCallable c |
|
||||
Stage4::parameterMayFlowThrough(_, c, apa, config) and
|
||||
nodeMayUseSummary0(n, c, state, apa, config)
|
||||
)
|
||||
}
|
||||
|
||||
private newtype TSummaryCtx =
|
||||
TSummaryCtxNone() or
|
||||
TSummaryCtxSome(ParamNodeEx p, FlowState state, AccessPath ap) {
|
||||
@@ -3557,7 +3661,7 @@ private newtype TPathNode =
|
||||
* of dereference operations needed to get from the value in the node to the
|
||||
* tracked object. The final type indicates the type of the tracked object.
|
||||
*/
|
||||
abstract private class AccessPath extends TAccessPath {
|
||||
private class AccessPath extends TAccessPath {
|
||||
/** Gets the head of this access path, if any. */
|
||||
abstract TypedContent getHead();
|
||||
|
||||
@@ -3772,11 +3876,14 @@ abstract private class PathNodeImpl extends PathNode {
|
||||
abstract NodeEx getNodeEx();
|
||||
|
||||
predicate isHidden() {
|
||||
hiddenNode(this.getNodeEx().asNode()) and
|
||||
not this.isSource() and
|
||||
not this instanceof PathNodeSink
|
||||
or
|
||||
this.getNodeEx() instanceof TNodeImplicitRead
|
||||
not this.getConfiguration().includeHiddenNodes() and
|
||||
(
|
||||
hiddenNode(this.getNodeEx().asNode()) and
|
||||
not this.isSource() and
|
||||
not this instanceof PathNodeSink
|
||||
or
|
||||
this.getNodeEx() instanceof TNodeImplicitRead
|
||||
)
|
||||
}
|
||||
|
||||
private string ppAp() {
|
||||
@@ -4257,6 +4364,12 @@ private module Subpaths {
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate hasSuccessor(PathNode pred, PathNodeMid succ, NodeEx succNode) {
|
||||
succ = pred.getASuccessor() and
|
||||
succNode = succ.getNodeEx()
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `(arg, par, ret, out)` forms a subpath-tuple, that is, flow through
|
||||
* a subpath between `par` and `ret` with the connecting edges `arg -> par` and
|
||||
@@ -4264,15 +4377,13 @@ private module Subpaths {
|
||||
*/
|
||||
predicate subpaths(PathNode arg, PathNodeImpl par, PathNodeImpl ret, PathNode out) {
|
||||
exists(ParamNodeEx p, NodeEx o, FlowState sout, AccessPath apout, PathNodeMid out0 |
|
||||
pragma[only_bind_into](arg).getASuccessor() = par and
|
||||
pragma[only_bind_into](arg).getASuccessor() = out0 and
|
||||
subpaths03(arg, p, localStepToHidden*(ret), o, sout, apout) and
|
||||
pragma[only_bind_into](arg).getASuccessor() = pragma[only_bind_into](out0) and
|
||||
subpaths03(pragma[only_bind_into](arg), p, localStepToHidden*(ret), o, sout, apout) and
|
||||
hasSuccessor(pragma[only_bind_into](arg), par, p) and
|
||||
not ret.isHidden() and
|
||||
par.getNodeEx() = p and
|
||||
out0.getNodeEx() = o and
|
||||
out0.getState() = sout and
|
||||
out0.getAp() = apout and
|
||||
(out = out0 or out = out0.projectToSink())
|
||||
pathNode(out0, o, sout, _, _, apout, _, _)
|
||||
|
|
||||
out = out0 or out = out0.projectToSink()
|
||||
)
|
||||
}
|
||||
|
||||
@@ -4609,6 +4720,10 @@ private module FlowExploration {
|
||||
exists(PartialPathNodeRev mid |
|
||||
revPartialPathStep(mid, node, state, sc1, sc2, sc3, ap, config) and
|
||||
not clearsContentEx(node, ap.getHead()) and
|
||||
(
|
||||
notExpectsContent(node) or
|
||||
expectsContentEx(node, ap.getHead())
|
||||
) and
|
||||
not fullBarrier(node, config) and
|
||||
not stateBarrier(node, state, config) and
|
||||
distSink(node.getEnclosingCallable(), config) <= config.explorationLimit()
|
||||
@@ -4625,6 +4740,10 @@ private module FlowExploration {
|
||||
not fullBarrier(node, config) and
|
||||
not stateBarrier(node, state, config) and
|
||||
not clearsContentEx(node, ap.getHead().getContent()) and
|
||||
(
|
||||
notExpectsContent(node) or
|
||||
expectsContentEx(node, ap.getHead().getContent())
|
||||
) and
|
||||
if node.asNode() instanceof CastingNode
|
||||
then compatibleTypes(node.getDataFlowType(), ap.getType())
|
||||
else any()
|
||||
|
||||
@@ -170,6 +170,14 @@ abstract class Configuration extends string {
|
||||
*/
|
||||
int explorationLimit() { none() }
|
||||
|
||||
/**
|
||||
* Holds if hidden nodes should be included in the data flow graph.
|
||||
*
|
||||
* This feature should only be used for debugging or when the data flow graph
|
||||
* is not visualized (for example in a `path-problem` query).
|
||||
*/
|
||||
predicate includeHiddenNodes() { none() }
|
||||
|
||||
/**
|
||||
* Holds if there is a partial data flow path from `source` to `node`. The
|
||||
* approximate distance between `node` and the closest source is `dist` and
|
||||
@@ -498,23 +506,35 @@ private predicate readSet(NodeEx node1, ContentSet c, NodeEx node2, Configuratio
|
||||
}
|
||||
|
||||
// inline to reduce fan-out via `getAReadContent`
|
||||
pragma[inline]
|
||||
bindingset[c]
|
||||
private predicate read(NodeEx node1, Content c, NodeEx node2, Configuration config) {
|
||||
exists(ContentSet cs |
|
||||
readSet(node1, cs, node2, config) and
|
||||
c = cs.getAReadContent()
|
||||
pragma[only_bind_out](c) = pragma[only_bind_into](cs).getAReadContent()
|
||||
)
|
||||
}
|
||||
|
||||
// inline to reduce fan-out via `getAReadContent`
|
||||
pragma[inline]
|
||||
bindingset[c]
|
||||
private predicate clearsContentEx(NodeEx n, Content c) {
|
||||
exists(ContentSet cs |
|
||||
clearsContentCached(n.asNode(), cs) and
|
||||
c = cs.getAReadContent()
|
||||
pragma[only_bind_out](c) = pragma[only_bind_into](cs).getAReadContent()
|
||||
)
|
||||
}
|
||||
|
||||
// inline to reduce fan-out via `getAReadContent`
|
||||
bindingset[c]
|
||||
private predicate expectsContentEx(NodeEx n, Content c) {
|
||||
exists(ContentSet cs |
|
||||
expectsContentCached(n.asNode(), cs) and
|
||||
pragma[only_bind_out](c) = pragma[only_bind_into](cs).getAReadContent()
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate notExpectsContent(NodeEx n) { not expectsContentCached(n.asNode(), _) }
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate store(
|
||||
NodeEx node1, TypedContent tc, NodeEx node2, DataFlowType contentType, Configuration config
|
||||
@@ -793,7 +813,7 @@ private module Stage1 {
|
||||
* by `revFlow`.
|
||||
*/
|
||||
pragma[nomagic]
|
||||
private predicate revFlowIsReadAndStored(Content c, Configuration conf) {
|
||||
predicate revFlowIsReadAndStored(Content c, Configuration conf) {
|
||||
revFlowConsCand(c, conf) and
|
||||
revFlowStore(c, _, _, conf)
|
||||
}
|
||||
@@ -891,7 +911,7 @@ private module Stage1 {
|
||||
|
||||
pragma[nomagic]
|
||||
predicate readStepCand(NodeEx n1, Content c, NodeEx n2, Configuration config) {
|
||||
revFlowIsReadAndStored(pragma[only_bind_into](c), pragma[only_bind_into](config)) and
|
||||
revFlowIsReadAndStored(c, pragma[only_bind_into](config)) and
|
||||
read(n1, c, n2, pragma[only_bind_into](config)) and
|
||||
revFlow(n2, pragma[only_bind_into](config))
|
||||
}
|
||||
@@ -1181,11 +1201,26 @@ private module Stage2 {
|
||||
|
||||
private predicate flowIntoCall = flowIntoCallNodeCand1/5;
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate expectsContentCand(NodeEx node, Configuration config) {
|
||||
exists(Content c |
|
||||
PrevStage::revFlow(node, pragma[only_bind_into](config)) and
|
||||
PrevStage::revFlowIsReadAndStored(c, pragma[only_bind_into](config)) and
|
||||
expectsContentEx(node, c)
|
||||
)
|
||||
}
|
||||
|
||||
bindingset[node, state, ap, config]
|
||||
private predicate filter(NodeEx node, FlowState state, Ap ap, Configuration config) {
|
||||
PrevStage::revFlowState(state, pragma[only_bind_into](config)) and
|
||||
exists(ap) and
|
||||
not stateBarrier(node, state, config)
|
||||
not stateBarrier(node, state, config) and
|
||||
(
|
||||
notExpectsContent(node)
|
||||
or
|
||||
ap = true and
|
||||
expectsContentCand(node, config)
|
||||
)
|
||||
}
|
||||
|
||||
bindingset[ap, contentType]
|
||||
@@ -1646,10 +1681,24 @@ private module Stage2 {
|
||||
storeStepFwd(_, ap, tc, _, _, config)
|
||||
}
|
||||
|
||||
predicate consCand(TypedContent tc, Ap ap, Configuration config) {
|
||||
private predicate revConsCand(TypedContent tc, Ap ap, Configuration config) {
|
||||
storeStepCand(_, ap, tc, _, _, config)
|
||||
}
|
||||
|
||||
private predicate validAp(Ap ap, Configuration config) {
|
||||
revFlow(_, _, _, _, ap, config) and ap instanceof ApNil
|
||||
or
|
||||
exists(TypedContent head, Ap tail |
|
||||
consCand(head, tail, config) and
|
||||
ap = apCons(head, tail)
|
||||
)
|
||||
}
|
||||
|
||||
predicate consCand(TypedContent tc, Ap ap, Configuration config) {
|
||||
revConsCand(tc, ap, config) and
|
||||
validAp(ap, config)
|
||||
}
|
||||
|
||||
pragma[noinline]
|
||||
private predicate parameterFlow(
|
||||
ParamNodeEx p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config
|
||||
@@ -1740,7 +1789,8 @@ private module LocalFlowBigStep {
|
||||
private class FlowCheckNode extends NodeEx {
|
||||
FlowCheckNode() {
|
||||
castNode(this.asNode()) or
|
||||
clearsContentCached(this.asNode(), _)
|
||||
clearsContentCached(this.asNode(), _) or
|
||||
expectsContentCached(this.asNode(), _)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1979,6 +2029,16 @@ private module Stage3 {
|
||||
clearContent(node, ap.getHead().getContent(), config)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate expectsContentCand(NodeEx node, Ap ap, Configuration config) {
|
||||
exists(Content c |
|
||||
PrevStage::revFlow(node, pragma[only_bind_into](config)) and
|
||||
PrevStage::readStepCand(_, c, _, pragma[only_bind_into](config)) and
|
||||
expectsContentEx(node, c) and
|
||||
c = ap.getHead().getContent()
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate castingNodeEx(NodeEx node) { node.asNode() instanceof CastingNode }
|
||||
|
||||
@@ -1987,7 +2047,12 @@ private module Stage3 {
|
||||
exists(state) and
|
||||
exists(config) and
|
||||
not clear(node, ap, config) and
|
||||
if castingNodeEx(node) then compatibleTypes(node.getDataFlowType(), ap.getType()) else any()
|
||||
(if castingNodeEx(node) then compatibleTypes(node.getDataFlowType(), ap.getType()) else any()) and
|
||||
(
|
||||
notExpectsContent(node)
|
||||
or
|
||||
expectsContentCand(node, ap, config)
|
||||
)
|
||||
}
|
||||
|
||||
bindingset[ap, contentType]
|
||||
@@ -2452,10 +2517,24 @@ private module Stage3 {
|
||||
storeStepFwd(_, ap, tc, _, _, config)
|
||||
}
|
||||
|
||||
predicate consCand(TypedContent tc, Ap ap, Configuration config) {
|
||||
private predicate revConsCand(TypedContent tc, Ap ap, Configuration config) {
|
||||
storeStepCand(_, ap, tc, _, _, config)
|
||||
}
|
||||
|
||||
private predicate validAp(Ap ap, Configuration config) {
|
||||
revFlow(_, _, _, _, ap, config) and ap instanceof ApNil
|
||||
or
|
||||
exists(TypedContent head, Ap tail |
|
||||
consCand(head, tail, config) and
|
||||
ap = apCons(head, tail)
|
||||
)
|
||||
}
|
||||
|
||||
predicate consCand(TypedContent tc, Ap ap, Configuration config) {
|
||||
revConsCand(tc, ap, config) and
|
||||
validAp(ap, config)
|
||||
}
|
||||
|
||||
pragma[noinline]
|
||||
private predicate parameterFlow(
|
||||
ParamNodeEx p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config
|
||||
@@ -3279,10 +3358,24 @@ private module Stage4 {
|
||||
storeStepFwd(_, ap, tc, _, _, config)
|
||||
}
|
||||
|
||||
predicate consCand(TypedContent tc, Ap ap, Configuration config) {
|
||||
private predicate revConsCand(TypedContent tc, Ap ap, Configuration config) {
|
||||
storeStepCand(_, ap, tc, _, _, config)
|
||||
}
|
||||
|
||||
private predicate validAp(Ap ap, Configuration config) {
|
||||
revFlow(_, _, _, _, ap, config) and ap instanceof ApNil
|
||||
or
|
||||
exists(TypedContent head, Ap tail |
|
||||
consCand(head, tail, config) and
|
||||
ap = apCons(head, tail)
|
||||
)
|
||||
}
|
||||
|
||||
predicate consCand(TypedContent tc, Ap ap, Configuration config) {
|
||||
revConsCand(tc, ap, config) and
|
||||
validAp(ap, config)
|
||||
}
|
||||
|
||||
pragma[noinline]
|
||||
private predicate parameterFlow(
|
||||
ParamNodeEx p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config
|
||||
@@ -3351,17 +3444,28 @@ private Configuration unbindConf(Configuration conf) {
|
||||
exists(Configuration c | result = pragma[only_bind_into](c) and conf = pragma[only_bind_into](c))
|
||||
}
|
||||
|
||||
private predicate nodeMayUseSummary(
|
||||
NodeEx n, FlowState state, AccessPathApprox apa, Configuration config
|
||||
pragma[nomagic]
|
||||
private predicate nodeMayUseSummary0(
|
||||
NodeEx n, DataFlowCallable c, FlowState state, AccessPathApprox apa, Configuration config
|
||||
) {
|
||||
exists(DataFlowCallable c, AccessPathApprox apa0 |
|
||||
Stage4::parameterMayFlowThrough(_, c, apa, _) and
|
||||
exists(AccessPathApprox apa0 |
|
||||
Stage4::parameterMayFlowThrough(_, c, _, _) and
|
||||
Stage4::revFlow(n, state, true, _, apa0, config) and
|
||||
Stage4::fwdFlow(n, state, any(CallContextCall ccc), TAccessPathApproxSome(apa), apa0, config) and
|
||||
n.getEnclosingCallable() = c
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate nodeMayUseSummary(
|
||||
NodeEx n, FlowState state, AccessPathApprox apa, Configuration config
|
||||
) {
|
||||
exists(DataFlowCallable c |
|
||||
Stage4::parameterMayFlowThrough(_, c, apa, config) and
|
||||
nodeMayUseSummary0(n, c, state, apa, config)
|
||||
)
|
||||
}
|
||||
|
||||
private newtype TSummaryCtx =
|
||||
TSummaryCtxNone() or
|
||||
TSummaryCtxSome(ParamNodeEx p, FlowState state, AccessPath ap) {
|
||||
@@ -3557,7 +3661,7 @@ private newtype TPathNode =
|
||||
* of dereference operations needed to get from the value in the node to the
|
||||
* tracked object. The final type indicates the type of the tracked object.
|
||||
*/
|
||||
abstract private class AccessPath extends TAccessPath {
|
||||
private class AccessPath extends TAccessPath {
|
||||
/** Gets the head of this access path, if any. */
|
||||
abstract TypedContent getHead();
|
||||
|
||||
@@ -3772,11 +3876,14 @@ abstract private class PathNodeImpl extends PathNode {
|
||||
abstract NodeEx getNodeEx();
|
||||
|
||||
predicate isHidden() {
|
||||
hiddenNode(this.getNodeEx().asNode()) and
|
||||
not this.isSource() and
|
||||
not this instanceof PathNodeSink
|
||||
or
|
||||
this.getNodeEx() instanceof TNodeImplicitRead
|
||||
not this.getConfiguration().includeHiddenNodes() and
|
||||
(
|
||||
hiddenNode(this.getNodeEx().asNode()) and
|
||||
not this.isSource() and
|
||||
not this instanceof PathNodeSink
|
||||
or
|
||||
this.getNodeEx() instanceof TNodeImplicitRead
|
||||
)
|
||||
}
|
||||
|
||||
private string ppAp() {
|
||||
@@ -4257,6 +4364,12 @@ private module Subpaths {
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate hasSuccessor(PathNode pred, PathNodeMid succ, NodeEx succNode) {
|
||||
succ = pred.getASuccessor() and
|
||||
succNode = succ.getNodeEx()
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `(arg, par, ret, out)` forms a subpath-tuple, that is, flow through
|
||||
* a subpath between `par` and `ret` with the connecting edges `arg -> par` and
|
||||
@@ -4264,15 +4377,13 @@ private module Subpaths {
|
||||
*/
|
||||
predicate subpaths(PathNode arg, PathNodeImpl par, PathNodeImpl ret, PathNode out) {
|
||||
exists(ParamNodeEx p, NodeEx o, FlowState sout, AccessPath apout, PathNodeMid out0 |
|
||||
pragma[only_bind_into](arg).getASuccessor() = par and
|
||||
pragma[only_bind_into](arg).getASuccessor() = out0 and
|
||||
subpaths03(arg, p, localStepToHidden*(ret), o, sout, apout) and
|
||||
pragma[only_bind_into](arg).getASuccessor() = pragma[only_bind_into](out0) and
|
||||
subpaths03(pragma[only_bind_into](arg), p, localStepToHidden*(ret), o, sout, apout) and
|
||||
hasSuccessor(pragma[only_bind_into](arg), par, p) and
|
||||
not ret.isHidden() and
|
||||
par.getNodeEx() = p and
|
||||
out0.getNodeEx() = o and
|
||||
out0.getState() = sout and
|
||||
out0.getAp() = apout and
|
||||
(out = out0 or out = out0.projectToSink())
|
||||
pathNode(out0, o, sout, _, _, apout, _, _)
|
||||
|
|
||||
out = out0 or out = out0.projectToSink()
|
||||
)
|
||||
}
|
||||
|
||||
@@ -4609,6 +4720,10 @@ private module FlowExploration {
|
||||
exists(PartialPathNodeRev mid |
|
||||
revPartialPathStep(mid, node, state, sc1, sc2, sc3, ap, config) and
|
||||
not clearsContentEx(node, ap.getHead()) and
|
||||
(
|
||||
notExpectsContent(node) or
|
||||
expectsContentEx(node, ap.getHead())
|
||||
) and
|
||||
not fullBarrier(node, config) and
|
||||
not stateBarrier(node, state, config) and
|
||||
distSink(node.getEnclosingCallable(), config) <= config.explorationLimit()
|
||||
@@ -4625,6 +4740,10 @@ private module FlowExploration {
|
||||
not fullBarrier(node, config) and
|
||||
not stateBarrier(node, state, config) and
|
||||
not clearsContentEx(node, ap.getHead().getContent()) and
|
||||
(
|
||||
notExpectsContent(node) or
|
||||
expectsContentEx(node, ap.getHead().getContent())
|
||||
) and
|
||||
if node.asNode() instanceof CastingNode
|
||||
then compatibleTypes(node.getDataFlowType(), ap.getType())
|
||||
else any()
|
||||
|
||||
@@ -170,6 +170,14 @@ abstract class Configuration extends string {
|
||||
*/
|
||||
int explorationLimit() { none() }
|
||||
|
||||
/**
|
||||
* Holds if hidden nodes should be included in the data flow graph.
|
||||
*
|
||||
* This feature should only be used for debugging or when the data flow graph
|
||||
* is not visualized (for example in a `path-problem` query).
|
||||
*/
|
||||
predicate includeHiddenNodes() { none() }
|
||||
|
||||
/**
|
||||
* Holds if there is a partial data flow path from `source` to `node`. The
|
||||
* approximate distance between `node` and the closest source is `dist` and
|
||||
@@ -498,23 +506,35 @@ private predicate readSet(NodeEx node1, ContentSet c, NodeEx node2, Configuratio
|
||||
}
|
||||
|
||||
// inline to reduce fan-out via `getAReadContent`
|
||||
pragma[inline]
|
||||
bindingset[c]
|
||||
private predicate read(NodeEx node1, Content c, NodeEx node2, Configuration config) {
|
||||
exists(ContentSet cs |
|
||||
readSet(node1, cs, node2, config) and
|
||||
c = cs.getAReadContent()
|
||||
pragma[only_bind_out](c) = pragma[only_bind_into](cs).getAReadContent()
|
||||
)
|
||||
}
|
||||
|
||||
// inline to reduce fan-out via `getAReadContent`
|
||||
pragma[inline]
|
||||
bindingset[c]
|
||||
private predicate clearsContentEx(NodeEx n, Content c) {
|
||||
exists(ContentSet cs |
|
||||
clearsContentCached(n.asNode(), cs) and
|
||||
c = cs.getAReadContent()
|
||||
pragma[only_bind_out](c) = pragma[only_bind_into](cs).getAReadContent()
|
||||
)
|
||||
}
|
||||
|
||||
// inline to reduce fan-out via `getAReadContent`
|
||||
bindingset[c]
|
||||
private predicate expectsContentEx(NodeEx n, Content c) {
|
||||
exists(ContentSet cs |
|
||||
expectsContentCached(n.asNode(), cs) and
|
||||
pragma[only_bind_out](c) = pragma[only_bind_into](cs).getAReadContent()
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate notExpectsContent(NodeEx n) { not expectsContentCached(n.asNode(), _) }
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate store(
|
||||
NodeEx node1, TypedContent tc, NodeEx node2, DataFlowType contentType, Configuration config
|
||||
@@ -793,7 +813,7 @@ private module Stage1 {
|
||||
* by `revFlow`.
|
||||
*/
|
||||
pragma[nomagic]
|
||||
private predicate revFlowIsReadAndStored(Content c, Configuration conf) {
|
||||
predicate revFlowIsReadAndStored(Content c, Configuration conf) {
|
||||
revFlowConsCand(c, conf) and
|
||||
revFlowStore(c, _, _, conf)
|
||||
}
|
||||
@@ -891,7 +911,7 @@ private module Stage1 {
|
||||
|
||||
pragma[nomagic]
|
||||
predicate readStepCand(NodeEx n1, Content c, NodeEx n2, Configuration config) {
|
||||
revFlowIsReadAndStored(pragma[only_bind_into](c), pragma[only_bind_into](config)) and
|
||||
revFlowIsReadAndStored(c, pragma[only_bind_into](config)) and
|
||||
read(n1, c, n2, pragma[only_bind_into](config)) and
|
||||
revFlow(n2, pragma[only_bind_into](config))
|
||||
}
|
||||
@@ -1181,11 +1201,26 @@ private module Stage2 {
|
||||
|
||||
private predicate flowIntoCall = flowIntoCallNodeCand1/5;
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate expectsContentCand(NodeEx node, Configuration config) {
|
||||
exists(Content c |
|
||||
PrevStage::revFlow(node, pragma[only_bind_into](config)) and
|
||||
PrevStage::revFlowIsReadAndStored(c, pragma[only_bind_into](config)) and
|
||||
expectsContentEx(node, c)
|
||||
)
|
||||
}
|
||||
|
||||
bindingset[node, state, ap, config]
|
||||
private predicate filter(NodeEx node, FlowState state, Ap ap, Configuration config) {
|
||||
PrevStage::revFlowState(state, pragma[only_bind_into](config)) and
|
||||
exists(ap) and
|
||||
not stateBarrier(node, state, config)
|
||||
not stateBarrier(node, state, config) and
|
||||
(
|
||||
notExpectsContent(node)
|
||||
or
|
||||
ap = true and
|
||||
expectsContentCand(node, config)
|
||||
)
|
||||
}
|
||||
|
||||
bindingset[ap, contentType]
|
||||
@@ -1646,10 +1681,24 @@ private module Stage2 {
|
||||
storeStepFwd(_, ap, tc, _, _, config)
|
||||
}
|
||||
|
||||
predicate consCand(TypedContent tc, Ap ap, Configuration config) {
|
||||
private predicate revConsCand(TypedContent tc, Ap ap, Configuration config) {
|
||||
storeStepCand(_, ap, tc, _, _, config)
|
||||
}
|
||||
|
||||
private predicate validAp(Ap ap, Configuration config) {
|
||||
revFlow(_, _, _, _, ap, config) and ap instanceof ApNil
|
||||
or
|
||||
exists(TypedContent head, Ap tail |
|
||||
consCand(head, tail, config) and
|
||||
ap = apCons(head, tail)
|
||||
)
|
||||
}
|
||||
|
||||
predicate consCand(TypedContent tc, Ap ap, Configuration config) {
|
||||
revConsCand(tc, ap, config) and
|
||||
validAp(ap, config)
|
||||
}
|
||||
|
||||
pragma[noinline]
|
||||
private predicate parameterFlow(
|
||||
ParamNodeEx p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config
|
||||
@@ -1740,7 +1789,8 @@ private module LocalFlowBigStep {
|
||||
private class FlowCheckNode extends NodeEx {
|
||||
FlowCheckNode() {
|
||||
castNode(this.asNode()) or
|
||||
clearsContentCached(this.asNode(), _)
|
||||
clearsContentCached(this.asNode(), _) or
|
||||
expectsContentCached(this.asNode(), _)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1979,6 +2029,16 @@ private module Stage3 {
|
||||
clearContent(node, ap.getHead().getContent(), config)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate expectsContentCand(NodeEx node, Ap ap, Configuration config) {
|
||||
exists(Content c |
|
||||
PrevStage::revFlow(node, pragma[only_bind_into](config)) and
|
||||
PrevStage::readStepCand(_, c, _, pragma[only_bind_into](config)) and
|
||||
expectsContentEx(node, c) and
|
||||
c = ap.getHead().getContent()
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate castingNodeEx(NodeEx node) { node.asNode() instanceof CastingNode }
|
||||
|
||||
@@ -1987,7 +2047,12 @@ private module Stage3 {
|
||||
exists(state) and
|
||||
exists(config) and
|
||||
not clear(node, ap, config) and
|
||||
if castingNodeEx(node) then compatibleTypes(node.getDataFlowType(), ap.getType()) else any()
|
||||
(if castingNodeEx(node) then compatibleTypes(node.getDataFlowType(), ap.getType()) else any()) and
|
||||
(
|
||||
notExpectsContent(node)
|
||||
or
|
||||
expectsContentCand(node, ap, config)
|
||||
)
|
||||
}
|
||||
|
||||
bindingset[ap, contentType]
|
||||
@@ -2452,10 +2517,24 @@ private module Stage3 {
|
||||
storeStepFwd(_, ap, tc, _, _, config)
|
||||
}
|
||||
|
||||
predicate consCand(TypedContent tc, Ap ap, Configuration config) {
|
||||
private predicate revConsCand(TypedContent tc, Ap ap, Configuration config) {
|
||||
storeStepCand(_, ap, tc, _, _, config)
|
||||
}
|
||||
|
||||
private predicate validAp(Ap ap, Configuration config) {
|
||||
revFlow(_, _, _, _, ap, config) and ap instanceof ApNil
|
||||
or
|
||||
exists(TypedContent head, Ap tail |
|
||||
consCand(head, tail, config) and
|
||||
ap = apCons(head, tail)
|
||||
)
|
||||
}
|
||||
|
||||
predicate consCand(TypedContent tc, Ap ap, Configuration config) {
|
||||
revConsCand(tc, ap, config) and
|
||||
validAp(ap, config)
|
||||
}
|
||||
|
||||
pragma[noinline]
|
||||
private predicate parameterFlow(
|
||||
ParamNodeEx p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config
|
||||
@@ -3279,10 +3358,24 @@ private module Stage4 {
|
||||
storeStepFwd(_, ap, tc, _, _, config)
|
||||
}
|
||||
|
||||
predicate consCand(TypedContent tc, Ap ap, Configuration config) {
|
||||
private predicate revConsCand(TypedContent tc, Ap ap, Configuration config) {
|
||||
storeStepCand(_, ap, tc, _, _, config)
|
||||
}
|
||||
|
||||
private predicate validAp(Ap ap, Configuration config) {
|
||||
revFlow(_, _, _, _, ap, config) and ap instanceof ApNil
|
||||
or
|
||||
exists(TypedContent head, Ap tail |
|
||||
consCand(head, tail, config) and
|
||||
ap = apCons(head, tail)
|
||||
)
|
||||
}
|
||||
|
||||
predicate consCand(TypedContent tc, Ap ap, Configuration config) {
|
||||
revConsCand(tc, ap, config) and
|
||||
validAp(ap, config)
|
||||
}
|
||||
|
||||
pragma[noinline]
|
||||
private predicate parameterFlow(
|
||||
ParamNodeEx p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config
|
||||
@@ -3351,17 +3444,28 @@ private Configuration unbindConf(Configuration conf) {
|
||||
exists(Configuration c | result = pragma[only_bind_into](c) and conf = pragma[only_bind_into](c))
|
||||
}
|
||||
|
||||
private predicate nodeMayUseSummary(
|
||||
NodeEx n, FlowState state, AccessPathApprox apa, Configuration config
|
||||
pragma[nomagic]
|
||||
private predicate nodeMayUseSummary0(
|
||||
NodeEx n, DataFlowCallable c, FlowState state, AccessPathApprox apa, Configuration config
|
||||
) {
|
||||
exists(DataFlowCallable c, AccessPathApprox apa0 |
|
||||
Stage4::parameterMayFlowThrough(_, c, apa, _) and
|
||||
exists(AccessPathApprox apa0 |
|
||||
Stage4::parameterMayFlowThrough(_, c, _, _) and
|
||||
Stage4::revFlow(n, state, true, _, apa0, config) and
|
||||
Stage4::fwdFlow(n, state, any(CallContextCall ccc), TAccessPathApproxSome(apa), apa0, config) and
|
||||
n.getEnclosingCallable() = c
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate nodeMayUseSummary(
|
||||
NodeEx n, FlowState state, AccessPathApprox apa, Configuration config
|
||||
) {
|
||||
exists(DataFlowCallable c |
|
||||
Stage4::parameterMayFlowThrough(_, c, apa, config) and
|
||||
nodeMayUseSummary0(n, c, state, apa, config)
|
||||
)
|
||||
}
|
||||
|
||||
private newtype TSummaryCtx =
|
||||
TSummaryCtxNone() or
|
||||
TSummaryCtxSome(ParamNodeEx p, FlowState state, AccessPath ap) {
|
||||
@@ -3557,7 +3661,7 @@ private newtype TPathNode =
|
||||
* of dereference operations needed to get from the value in the node to the
|
||||
* tracked object. The final type indicates the type of the tracked object.
|
||||
*/
|
||||
abstract private class AccessPath extends TAccessPath {
|
||||
private class AccessPath extends TAccessPath {
|
||||
/** Gets the head of this access path, if any. */
|
||||
abstract TypedContent getHead();
|
||||
|
||||
@@ -3772,11 +3876,14 @@ abstract private class PathNodeImpl extends PathNode {
|
||||
abstract NodeEx getNodeEx();
|
||||
|
||||
predicate isHidden() {
|
||||
hiddenNode(this.getNodeEx().asNode()) and
|
||||
not this.isSource() and
|
||||
not this instanceof PathNodeSink
|
||||
or
|
||||
this.getNodeEx() instanceof TNodeImplicitRead
|
||||
not this.getConfiguration().includeHiddenNodes() and
|
||||
(
|
||||
hiddenNode(this.getNodeEx().asNode()) and
|
||||
not this.isSource() and
|
||||
not this instanceof PathNodeSink
|
||||
or
|
||||
this.getNodeEx() instanceof TNodeImplicitRead
|
||||
)
|
||||
}
|
||||
|
||||
private string ppAp() {
|
||||
@@ -4257,6 +4364,12 @@ private module Subpaths {
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate hasSuccessor(PathNode pred, PathNodeMid succ, NodeEx succNode) {
|
||||
succ = pred.getASuccessor() and
|
||||
succNode = succ.getNodeEx()
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `(arg, par, ret, out)` forms a subpath-tuple, that is, flow through
|
||||
* a subpath between `par` and `ret` with the connecting edges `arg -> par` and
|
||||
@@ -4264,15 +4377,13 @@ private module Subpaths {
|
||||
*/
|
||||
predicate subpaths(PathNode arg, PathNodeImpl par, PathNodeImpl ret, PathNode out) {
|
||||
exists(ParamNodeEx p, NodeEx o, FlowState sout, AccessPath apout, PathNodeMid out0 |
|
||||
pragma[only_bind_into](arg).getASuccessor() = par and
|
||||
pragma[only_bind_into](arg).getASuccessor() = out0 and
|
||||
subpaths03(arg, p, localStepToHidden*(ret), o, sout, apout) and
|
||||
pragma[only_bind_into](arg).getASuccessor() = pragma[only_bind_into](out0) and
|
||||
subpaths03(pragma[only_bind_into](arg), p, localStepToHidden*(ret), o, sout, apout) and
|
||||
hasSuccessor(pragma[only_bind_into](arg), par, p) and
|
||||
not ret.isHidden() and
|
||||
par.getNodeEx() = p and
|
||||
out0.getNodeEx() = o and
|
||||
out0.getState() = sout and
|
||||
out0.getAp() = apout and
|
||||
(out = out0 or out = out0.projectToSink())
|
||||
pathNode(out0, o, sout, _, _, apout, _, _)
|
||||
|
|
||||
out = out0 or out = out0.projectToSink()
|
||||
)
|
||||
}
|
||||
|
||||
@@ -4609,6 +4720,10 @@ private module FlowExploration {
|
||||
exists(PartialPathNodeRev mid |
|
||||
revPartialPathStep(mid, node, state, sc1, sc2, sc3, ap, config) and
|
||||
not clearsContentEx(node, ap.getHead()) and
|
||||
(
|
||||
notExpectsContent(node) or
|
||||
expectsContentEx(node, ap.getHead())
|
||||
) and
|
||||
not fullBarrier(node, config) and
|
||||
not stateBarrier(node, state, config) and
|
||||
distSink(node.getEnclosingCallable(), config) <= config.explorationLimit()
|
||||
@@ -4625,6 +4740,10 @@ private module FlowExploration {
|
||||
not fullBarrier(node, config) and
|
||||
not stateBarrier(node, state, config) and
|
||||
not clearsContentEx(node, ap.getHead().getContent()) and
|
||||
(
|
||||
notExpectsContent(node) or
|
||||
expectsContentEx(node, ap.getHead().getContent())
|
||||
) and
|
||||
if node.asNode() instanceof CastingNode
|
||||
then compatibleTypes(node.getDataFlowType(), ap.getType())
|
||||
else any()
|
||||
|
||||
@@ -305,7 +305,7 @@ cached
|
||||
private module Cached {
|
||||
/**
|
||||
* If needed, call this predicate from `DataFlowImplSpecific.qll` in order to
|
||||
* force a stage-dependency on the `DataFlowImplCommon.qll` stage and therby
|
||||
* force a stage-dependency on the `DataFlowImplCommon.qll` stage and thereby
|
||||
* collapsing the two stages.
|
||||
*/
|
||||
cached
|
||||
@@ -328,6 +328,9 @@ private module Cached {
|
||||
cached
|
||||
predicate clearsContentCached(Node n, ContentSet c) { clearsContent(n, c) }
|
||||
|
||||
cached
|
||||
predicate expectsContentCached(Node n, ContentSet c) { expectsContent(n, c) }
|
||||
|
||||
cached
|
||||
predicate isUnreachableInCallCached(Node n, DataFlowCall call) { isUnreachableInCall(n, call) }
|
||||
|
||||
|
||||
@@ -170,6 +170,14 @@ abstract class Configuration extends string {
|
||||
*/
|
||||
int explorationLimit() { none() }
|
||||
|
||||
/**
|
||||
* Holds if hidden nodes should be included in the data flow graph.
|
||||
*
|
||||
* This feature should only be used for debugging or when the data flow graph
|
||||
* is not visualized (for example in a `path-problem` query).
|
||||
*/
|
||||
predicate includeHiddenNodes() { none() }
|
||||
|
||||
/**
|
||||
* Holds if there is a partial data flow path from `source` to `node`. The
|
||||
* approximate distance between `node` and the closest source is `dist` and
|
||||
@@ -498,23 +506,35 @@ private predicate readSet(NodeEx node1, ContentSet c, NodeEx node2, Configuratio
|
||||
}
|
||||
|
||||
// inline to reduce fan-out via `getAReadContent`
|
||||
pragma[inline]
|
||||
bindingset[c]
|
||||
private predicate read(NodeEx node1, Content c, NodeEx node2, Configuration config) {
|
||||
exists(ContentSet cs |
|
||||
readSet(node1, cs, node2, config) and
|
||||
c = cs.getAReadContent()
|
||||
pragma[only_bind_out](c) = pragma[only_bind_into](cs).getAReadContent()
|
||||
)
|
||||
}
|
||||
|
||||
// inline to reduce fan-out via `getAReadContent`
|
||||
pragma[inline]
|
||||
bindingset[c]
|
||||
private predicate clearsContentEx(NodeEx n, Content c) {
|
||||
exists(ContentSet cs |
|
||||
clearsContentCached(n.asNode(), cs) and
|
||||
c = cs.getAReadContent()
|
||||
pragma[only_bind_out](c) = pragma[only_bind_into](cs).getAReadContent()
|
||||
)
|
||||
}
|
||||
|
||||
// inline to reduce fan-out via `getAReadContent`
|
||||
bindingset[c]
|
||||
private predicate expectsContentEx(NodeEx n, Content c) {
|
||||
exists(ContentSet cs |
|
||||
expectsContentCached(n.asNode(), cs) and
|
||||
pragma[only_bind_out](c) = pragma[only_bind_into](cs).getAReadContent()
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate notExpectsContent(NodeEx n) { not expectsContentCached(n.asNode(), _) }
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate store(
|
||||
NodeEx node1, TypedContent tc, NodeEx node2, DataFlowType contentType, Configuration config
|
||||
@@ -793,7 +813,7 @@ private module Stage1 {
|
||||
* by `revFlow`.
|
||||
*/
|
||||
pragma[nomagic]
|
||||
private predicate revFlowIsReadAndStored(Content c, Configuration conf) {
|
||||
predicate revFlowIsReadAndStored(Content c, Configuration conf) {
|
||||
revFlowConsCand(c, conf) and
|
||||
revFlowStore(c, _, _, conf)
|
||||
}
|
||||
@@ -891,7 +911,7 @@ private module Stage1 {
|
||||
|
||||
pragma[nomagic]
|
||||
predicate readStepCand(NodeEx n1, Content c, NodeEx n2, Configuration config) {
|
||||
revFlowIsReadAndStored(pragma[only_bind_into](c), pragma[only_bind_into](config)) and
|
||||
revFlowIsReadAndStored(c, pragma[only_bind_into](config)) and
|
||||
read(n1, c, n2, pragma[only_bind_into](config)) and
|
||||
revFlow(n2, pragma[only_bind_into](config))
|
||||
}
|
||||
@@ -1181,11 +1201,26 @@ private module Stage2 {
|
||||
|
||||
private predicate flowIntoCall = flowIntoCallNodeCand1/5;
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate expectsContentCand(NodeEx node, Configuration config) {
|
||||
exists(Content c |
|
||||
PrevStage::revFlow(node, pragma[only_bind_into](config)) and
|
||||
PrevStage::revFlowIsReadAndStored(c, pragma[only_bind_into](config)) and
|
||||
expectsContentEx(node, c)
|
||||
)
|
||||
}
|
||||
|
||||
bindingset[node, state, ap, config]
|
||||
private predicate filter(NodeEx node, FlowState state, Ap ap, Configuration config) {
|
||||
PrevStage::revFlowState(state, pragma[only_bind_into](config)) and
|
||||
exists(ap) and
|
||||
not stateBarrier(node, state, config)
|
||||
not stateBarrier(node, state, config) and
|
||||
(
|
||||
notExpectsContent(node)
|
||||
or
|
||||
ap = true and
|
||||
expectsContentCand(node, config)
|
||||
)
|
||||
}
|
||||
|
||||
bindingset[ap, contentType]
|
||||
@@ -1646,10 +1681,24 @@ private module Stage2 {
|
||||
storeStepFwd(_, ap, tc, _, _, config)
|
||||
}
|
||||
|
||||
predicate consCand(TypedContent tc, Ap ap, Configuration config) {
|
||||
private predicate revConsCand(TypedContent tc, Ap ap, Configuration config) {
|
||||
storeStepCand(_, ap, tc, _, _, config)
|
||||
}
|
||||
|
||||
private predicate validAp(Ap ap, Configuration config) {
|
||||
revFlow(_, _, _, _, ap, config) and ap instanceof ApNil
|
||||
or
|
||||
exists(TypedContent head, Ap tail |
|
||||
consCand(head, tail, config) and
|
||||
ap = apCons(head, tail)
|
||||
)
|
||||
}
|
||||
|
||||
predicate consCand(TypedContent tc, Ap ap, Configuration config) {
|
||||
revConsCand(tc, ap, config) and
|
||||
validAp(ap, config)
|
||||
}
|
||||
|
||||
pragma[noinline]
|
||||
private predicate parameterFlow(
|
||||
ParamNodeEx p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config
|
||||
@@ -1740,7 +1789,8 @@ private module LocalFlowBigStep {
|
||||
private class FlowCheckNode extends NodeEx {
|
||||
FlowCheckNode() {
|
||||
castNode(this.asNode()) or
|
||||
clearsContentCached(this.asNode(), _)
|
||||
clearsContentCached(this.asNode(), _) or
|
||||
expectsContentCached(this.asNode(), _)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1979,6 +2029,16 @@ private module Stage3 {
|
||||
clearContent(node, ap.getHead().getContent(), config)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate expectsContentCand(NodeEx node, Ap ap, Configuration config) {
|
||||
exists(Content c |
|
||||
PrevStage::revFlow(node, pragma[only_bind_into](config)) and
|
||||
PrevStage::readStepCand(_, c, _, pragma[only_bind_into](config)) and
|
||||
expectsContentEx(node, c) and
|
||||
c = ap.getHead().getContent()
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate castingNodeEx(NodeEx node) { node.asNode() instanceof CastingNode }
|
||||
|
||||
@@ -1987,7 +2047,12 @@ private module Stage3 {
|
||||
exists(state) and
|
||||
exists(config) and
|
||||
not clear(node, ap, config) and
|
||||
if castingNodeEx(node) then compatibleTypes(node.getDataFlowType(), ap.getType()) else any()
|
||||
(if castingNodeEx(node) then compatibleTypes(node.getDataFlowType(), ap.getType()) else any()) and
|
||||
(
|
||||
notExpectsContent(node)
|
||||
or
|
||||
expectsContentCand(node, ap, config)
|
||||
)
|
||||
}
|
||||
|
||||
bindingset[ap, contentType]
|
||||
@@ -2452,10 +2517,24 @@ private module Stage3 {
|
||||
storeStepFwd(_, ap, tc, _, _, config)
|
||||
}
|
||||
|
||||
predicate consCand(TypedContent tc, Ap ap, Configuration config) {
|
||||
private predicate revConsCand(TypedContent tc, Ap ap, Configuration config) {
|
||||
storeStepCand(_, ap, tc, _, _, config)
|
||||
}
|
||||
|
||||
private predicate validAp(Ap ap, Configuration config) {
|
||||
revFlow(_, _, _, _, ap, config) and ap instanceof ApNil
|
||||
or
|
||||
exists(TypedContent head, Ap tail |
|
||||
consCand(head, tail, config) and
|
||||
ap = apCons(head, tail)
|
||||
)
|
||||
}
|
||||
|
||||
predicate consCand(TypedContent tc, Ap ap, Configuration config) {
|
||||
revConsCand(tc, ap, config) and
|
||||
validAp(ap, config)
|
||||
}
|
||||
|
||||
pragma[noinline]
|
||||
private predicate parameterFlow(
|
||||
ParamNodeEx p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config
|
||||
@@ -3279,10 +3358,24 @@ private module Stage4 {
|
||||
storeStepFwd(_, ap, tc, _, _, config)
|
||||
}
|
||||
|
||||
predicate consCand(TypedContent tc, Ap ap, Configuration config) {
|
||||
private predicate revConsCand(TypedContent tc, Ap ap, Configuration config) {
|
||||
storeStepCand(_, ap, tc, _, _, config)
|
||||
}
|
||||
|
||||
private predicate validAp(Ap ap, Configuration config) {
|
||||
revFlow(_, _, _, _, ap, config) and ap instanceof ApNil
|
||||
or
|
||||
exists(TypedContent head, Ap tail |
|
||||
consCand(head, tail, config) and
|
||||
ap = apCons(head, tail)
|
||||
)
|
||||
}
|
||||
|
||||
predicate consCand(TypedContent tc, Ap ap, Configuration config) {
|
||||
revConsCand(tc, ap, config) and
|
||||
validAp(ap, config)
|
||||
}
|
||||
|
||||
pragma[noinline]
|
||||
private predicate parameterFlow(
|
||||
ParamNodeEx p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config
|
||||
@@ -3351,17 +3444,28 @@ private Configuration unbindConf(Configuration conf) {
|
||||
exists(Configuration c | result = pragma[only_bind_into](c) and conf = pragma[only_bind_into](c))
|
||||
}
|
||||
|
||||
private predicate nodeMayUseSummary(
|
||||
NodeEx n, FlowState state, AccessPathApprox apa, Configuration config
|
||||
pragma[nomagic]
|
||||
private predicate nodeMayUseSummary0(
|
||||
NodeEx n, DataFlowCallable c, FlowState state, AccessPathApprox apa, Configuration config
|
||||
) {
|
||||
exists(DataFlowCallable c, AccessPathApprox apa0 |
|
||||
Stage4::parameterMayFlowThrough(_, c, apa, _) and
|
||||
exists(AccessPathApprox apa0 |
|
||||
Stage4::parameterMayFlowThrough(_, c, _, _) and
|
||||
Stage4::revFlow(n, state, true, _, apa0, config) and
|
||||
Stage4::fwdFlow(n, state, any(CallContextCall ccc), TAccessPathApproxSome(apa), apa0, config) and
|
||||
n.getEnclosingCallable() = c
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate nodeMayUseSummary(
|
||||
NodeEx n, FlowState state, AccessPathApprox apa, Configuration config
|
||||
) {
|
||||
exists(DataFlowCallable c |
|
||||
Stage4::parameterMayFlowThrough(_, c, apa, config) and
|
||||
nodeMayUseSummary0(n, c, state, apa, config)
|
||||
)
|
||||
}
|
||||
|
||||
private newtype TSummaryCtx =
|
||||
TSummaryCtxNone() or
|
||||
TSummaryCtxSome(ParamNodeEx p, FlowState state, AccessPath ap) {
|
||||
@@ -3557,7 +3661,7 @@ private newtype TPathNode =
|
||||
* of dereference operations needed to get from the value in the node to the
|
||||
* tracked object. The final type indicates the type of the tracked object.
|
||||
*/
|
||||
abstract private class AccessPath extends TAccessPath {
|
||||
private class AccessPath extends TAccessPath {
|
||||
/** Gets the head of this access path, if any. */
|
||||
abstract TypedContent getHead();
|
||||
|
||||
@@ -3772,11 +3876,14 @@ abstract private class PathNodeImpl extends PathNode {
|
||||
abstract NodeEx getNodeEx();
|
||||
|
||||
predicate isHidden() {
|
||||
hiddenNode(this.getNodeEx().asNode()) and
|
||||
not this.isSource() and
|
||||
not this instanceof PathNodeSink
|
||||
or
|
||||
this.getNodeEx() instanceof TNodeImplicitRead
|
||||
not this.getConfiguration().includeHiddenNodes() and
|
||||
(
|
||||
hiddenNode(this.getNodeEx().asNode()) and
|
||||
not this.isSource() and
|
||||
not this instanceof PathNodeSink
|
||||
or
|
||||
this.getNodeEx() instanceof TNodeImplicitRead
|
||||
)
|
||||
}
|
||||
|
||||
private string ppAp() {
|
||||
@@ -4257,6 +4364,12 @@ private module Subpaths {
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate hasSuccessor(PathNode pred, PathNodeMid succ, NodeEx succNode) {
|
||||
succ = pred.getASuccessor() and
|
||||
succNode = succ.getNodeEx()
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `(arg, par, ret, out)` forms a subpath-tuple, that is, flow through
|
||||
* a subpath between `par` and `ret` with the connecting edges `arg -> par` and
|
||||
@@ -4264,15 +4377,13 @@ private module Subpaths {
|
||||
*/
|
||||
predicate subpaths(PathNode arg, PathNodeImpl par, PathNodeImpl ret, PathNode out) {
|
||||
exists(ParamNodeEx p, NodeEx o, FlowState sout, AccessPath apout, PathNodeMid out0 |
|
||||
pragma[only_bind_into](arg).getASuccessor() = par and
|
||||
pragma[only_bind_into](arg).getASuccessor() = out0 and
|
||||
subpaths03(arg, p, localStepToHidden*(ret), o, sout, apout) and
|
||||
pragma[only_bind_into](arg).getASuccessor() = pragma[only_bind_into](out0) and
|
||||
subpaths03(pragma[only_bind_into](arg), p, localStepToHidden*(ret), o, sout, apout) and
|
||||
hasSuccessor(pragma[only_bind_into](arg), par, p) and
|
||||
not ret.isHidden() and
|
||||
par.getNodeEx() = p and
|
||||
out0.getNodeEx() = o and
|
||||
out0.getState() = sout and
|
||||
out0.getAp() = apout and
|
||||
(out = out0 or out = out0.projectToSink())
|
||||
pathNode(out0, o, sout, _, _, apout, _, _)
|
||||
|
|
||||
out = out0 or out = out0.projectToSink()
|
||||
)
|
||||
}
|
||||
|
||||
@@ -4609,6 +4720,10 @@ private module FlowExploration {
|
||||
exists(PartialPathNodeRev mid |
|
||||
revPartialPathStep(mid, node, state, sc1, sc2, sc3, ap, config) and
|
||||
not clearsContentEx(node, ap.getHead()) and
|
||||
(
|
||||
notExpectsContent(node) or
|
||||
expectsContentEx(node, ap.getHead())
|
||||
) and
|
||||
not fullBarrier(node, config) and
|
||||
not stateBarrier(node, state, config) and
|
||||
distSink(node.getEnclosingCallable(), config) <= config.explorationLimit()
|
||||
@@ -4625,6 +4740,10 @@ private module FlowExploration {
|
||||
not fullBarrier(node, config) and
|
||||
not stateBarrier(node, state, config) and
|
||||
not clearsContentEx(node, ap.getHead().getContent()) and
|
||||
(
|
||||
notExpectsContent(node) or
|
||||
expectsContentEx(node, ap.getHead().getContent())
|
||||
) and
|
||||
if node.asNode() instanceof CastingNode
|
||||
then compatibleTypes(node.getDataFlowType(), ap.getType())
|
||||
else any()
|
||||
|
||||
@@ -198,6 +198,12 @@ predicate clearsContent(Node n, Content c) {
|
||||
none() // stub implementation
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if the value that is being tracked is expected to be stored inside content `c`
|
||||
* at node `n`.
|
||||
*/
|
||||
predicate expectsContent(Node n, ContentSet c) { none() }
|
||||
|
||||
/** Gets the type of `n` used for type pruning. */
|
||||
Type getNodeType(Node n) {
|
||||
suppressUnusedNode(n) and
|
||||
|
||||
@@ -170,6 +170,14 @@ abstract class Configuration extends string {
|
||||
*/
|
||||
int explorationLimit() { none() }
|
||||
|
||||
/**
|
||||
* Holds if hidden nodes should be included in the data flow graph.
|
||||
*
|
||||
* This feature should only be used for debugging or when the data flow graph
|
||||
* is not visualized (for example in a `path-problem` query).
|
||||
*/
|
||||
predicate includeHiddenNodes() { none() }
|
||||
|
||||
/**
|
||||
* Holds if there is a partial data flow path from `source` to `node`. The
|
||||
* approximate distance between `node` and the closest source is `dist` and
|
||||
@@ -498,23 +506,35 @@ private predicate readSet(NodeEx node1, ContentSet c, NodeEx node2, Configuratio
|
||||
}
|
||||
|
||||
// inline to reduce fan-out via `getAReadContent`
|
||||
pragma[inline]
|
||||
bindingset[c]
|
||||
private predicate read(NodeEx node1, Content c, NodeEx node2, Configuration config) {
|
||||
exists(ContentSet cs |
|
||||
readSet(node1, cs, node2, config) and
|
||||
c = cs.getAReadContent()
|
||||
pragma[only_bind_out](c) = pragma[only_bind_into](cs).getAReadContent()
|
||||
)
|
||||
}
|
||||
|
||||
// inline to reduce fan-out via `getAReadContent`
|
||||
pragma[inline]
|
||||
bindingset[c]
|
||||
private predicate clearsContentEx(NodeEx n, Content c) {
|
||||
exists(ContentSet cs |
|
||||
clearsContentCached(n.asNode(), cs) and
|
||||
c = cs.getAReadContent()
|
||||
pragma[only_bind_out](c) = pragma[only_bind_into](cs).getAReadContent()
|
||||
)
|
||||
}
|
||||
|
||||
// inline to reduce fan-out via `getAReadContent`
|
||||
bindingset[c]
|
||||
private predicate expectsContentEx(NodeEx n, Content c) {
|
||||
exists(ContentSet cs |
|
||||
expectsContentCached(n.asNode(), cs) and
|
||||
pragma[only_bind_out](c) = pragma[only_bind_into](cs).getAReadContent()
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate notExpectsContent(NodeEx n) { not expectsContentCached(n.asNode(), _) }
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate store(
|
||||
NodeEx node1, TypedContent tc, NodeEx node2, DataFlowType contentType, Configuration config
|
||||
@@ -793,7 +813,7 @@ private module Stage1 {
|
||||
* by `revFlow`.
|
||||
*/
|
||||
pragma[nomagic]
|
||||
private predicate revFlowIsReadAndStored(Content c, Configuration conf) {
|
||||
predicate revFlowIsReadAndStored(Content c, Configuration conf) {
|
||||
revFlowConsCand(c, conf) and
|
||||
revFlowStore(c, _, _, conf)
|
||||
}
|
||||
@@ -891,7 +911,7 @@ private module Stage1 {
|
||||
|
||||
pragma[nomagic]
|
||||
predicate readStepCand(NodeEx n1, Content c, NodeEx n2, Configuration config) {
|
||||
revFlowIsReadAndStored(pragma[only_bind_into](c), pragma[only_bind_into](config)) and
|
||||
revFlowIsReadAndStored(c, pragma[only_bind_into](config)) and
|
||||
read(n1, c, n2, pragma[only_bind_into](config)) and
|
||||
revFlow(n2, pragma[only_bind_into](config))
|
||||
}
|
||||
@@ -1181,11 +1201,26 @@ private module Stage2 {
|
||||
|
||||
private predicate flowIntoCall = flowIntoCallNodeCand1/5;
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate expectsContentCand(NodeEx node, Configuration config) {
|
||||
exists(Content c |
|
||||
PrevStage::revFlow(node, pragma[only_bind_into](config)) and
|
||||
PrevStage::revFlowIsReadAndStored(c, pragma[only_bind_into](config)) and
|
||||
expectsContentEx(node, c)
|
||||
)
|
||||
}
|
||||
|
||||
bindingset[node, state, ap, config]
|
||||
private predicate filter(NodeEx node, FlowState state, Ap ap, Configuration config) {
|
||||
PrevStage::revFlowState(state, pragma[only_bind_into](config)) and
|
||||
exists(ap) and
|
||||
not stateBarrier(node, state, config)
|
||||
not stateBarrier(node, state, config) and
|
||||
(
|
||||
notExpectsContent(node)
|
||||
or
|
||||
ap = true and
|
||||
expectsContentCand(node, config)
|
||||
)
|
||||
}
|
||||
|
||||
bindingset[ap, contentType]
|
||||
@@ -1646,10 +1681,24 @@ private module Stage2 {
|
||||
storeStepFwd(_, ap, tc, _, _, config)
|
||||
}
|
||||
|
||||
predicate consCand(TypedContent tc, Ap ap, Configuration config) {
|
||||
private predicate revConsCand(TypedContent tc, Ap ap, Configuration config) {
|
||||
storeStepCand(_, ap, tc, _, _, config)
|
||||
}
|
||||
|
||||
private predicate validAp(Ap ap, Configuration config) {
|
||||
revFlow(_, _, _, _, ap, config) and ap instanceof ApNil
|
||||
or
|
||||
exists(TypedContent head, Ap tail |
|
||||
consCand(head, tail, config) and
|
||||
ap = apCons(head, tail)
|
||||
)
|
||||
}
|
||||
|
||||
predicate consCand(TypedContent tc, Ap ap, Configuration config) {
|
||||
revConsCand(tc, ap, config) and
|
||||
validAp(ap, config)
|
||||
}
|
||||
|
||||
pragma[noinline]
|
||||
private predicate parameterFlow(
|
||||
ParamNodeEx p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config
|
||||
@@ -1740,7 +1789,8 @@ private module LocalFlowBigStep {
|
||||
private class FlowCheckNode extends NodeEx {
|
||||
FlowCheckNode() {
|
||||
castNode(this.asNode()) or
|
||||
clearsContentCached(this.asNode(), _)
|
||||
clearsContentCached(this.asNode(), _) or
|
||||
expectsContentCached(this.asNode(), _)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1979,6 +2029,16 @@ private module Stage3 {
|
||||
clearContent(node, ap.getHead().getContent(), config)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate expectsContentCand(NodeEx node, Ap ap, Configuration config) {
|
||||
exists(Content c |
|
||||
PrevStage::revFlow(node, pragma[only_bind_into](config)) and
|
||||
PrevStage::readStepCand(_, c, _, pragma[only_bind_into](config)) and
|
||||
expectsContentEx(node, c) and
|
||||
c = ap.getHead().getContent()
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate castingNodeEx(NodeEx node) { node.asNode() instanceof CastingNode }
|
||||
|
||||
@@ -1987,7 +2047,12 @@ private module Stage3 {
|
||||
exists(state) and
|
||||
exists(config) and
|
||||
not clear(node, ap, config) and
|
||||
if castingNodeEx(node) then compatibleTypes(node.getDataFlowType(), ap.getType()) else any()
|
||||
(if castingNodeEx(node) then compatibleTypes(node.getDataFlowType(), ap.getType()) else any()) and
|
||||
(
|
||||
notExpectsContent(node)
|
||||
or
|
||||
expectsContentCand(node, ap, config)
|
||||
)
|
||||
}
|
||||
|
||||
bindingset[ap, contentType]
|
||||
@@ -2452,10 +2517,24 @@ private module Stage3 {
|
||||
storeStepFwd(_, ap, tc, _, _, config)
|
||||
}
|
||||
|
||||
predicate consCand(TypedContent tc, Ap ap, Configuration config) {
|
||||
private predicate revConsCand(TypedContent tc, Ap ap, Configuration config) {
|
||||
storeStepCand(_, ap, tc, _, _, config)
|
||||
}
|
||||
|
||||
private predicate validAp(Ap ap, Configuration config) {
|
||||
revFlow(_, _, _, _, ap, config) and ap instanceof ApNil
|
||||
or
|
||||
exists(TypedContent head, Ap tail |
|
||||
consCand(head, tail, config) and
|
||||
ap = apCons(head, tail)
|
||||
)
|
||||
}
|
||||
|
||||
predicate consCand(TypedContent tc, Ap ap, Configuration config) {
|
||||
revConsCand(tc, ap, config) and
|
||||
validAp(ap, config)
|
||||
}
|
||||
|
||||
pragma[noinline]
|
||||
private predicate parameterFlow(
|
||||
ParamNodeEx p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config
|
||||
@@ -3279,10 +3358,24 @@ private module Stage4 {
|
||||
storeStepFwd(_, ap, tc, _, _, config)
|
||||
}
|
||||
|
||||
predicate consCand(TypedContent tc, Ap ap, Configuration config) {
|
||||
private predicate revConsCand(TypedContent tc, Ap ap, Configuration config) {
|
||||
storeStepCand(_, ap, tc, _, _, config)
|
||||
}
|
||||
|
||||
private predicate validAp(Ap ap, Configuration config) {
|
||||
revFlow(_, _, _, _, ap, config) and ap instanceof ApNil
|
||||
or
|
||||
exists(TypedContent head, Ap tail |
|
||||
consCand(head, tail, config) and
|
||||
ap = apCons(head, tail)
|
||||
)
|
||||
}
|
||||
|
||||
predicate consCand(TypedContent tc, Ap ap, Configuration config) {
|
||||
revConsCand(tc, ap, config) and
|
||||
validAp(ap, config)
|
||||
}
|
||||
|
||||
pragma[noinline]
|
||||
private predicate parameterFlow(
|
||||
ParamNodeEx p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config
|
||||
@@ -3351,17 +3444,28 @@ private Configuration unbindConf(Configuration conf) {
|
||||
exists(Configuration c | result = pragma[only_bind_into](c) and conf = pragma[only_bind_into](c))
|
||||
}
|
||||
|
||||
private predicate nodeMayUseSummary(
|
||||
NodeEx n, FlowState state, AccessPathApprox apa, Configuration config
|
||||
pragma[nomagic]
|
||||
private predicate nodeMayUseSummary0(
|
||||
NodeEx n, DataFlowCallable c, FlowState state, AccessPathApprox apa, Configuration config
|
||||
) {
|
||||
exists(DataFlowCallable c, AccessPathApprox apa0 |
|
||||
Stage4::parameterMayFlowThrough(_, c, apa, _) and
|
||||
exists(AccessPathApprox apa0 |
|
||||
Stage4::parameterMayFlowThrough(_, c, _, _) and
|
||||
Stage4::revFlow(n, state, true, _, apa0, config) and
|
||||
Stage4::fwdFlow(n, state, any(CallContextCall ccc), TAccessPathApproxSome(apa), apa0, config) and
|
||||
n.getEnclosingCallable() = c
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate nodeMayUseSummary(
|
||||
NodeEx n, FlowState state, AccessPathApprox apa, Configuration config
|
||||
) {
|
||||
exists(DataFlowCallable c |
|
||||
Stage4::parameterMayFlowThrough(_, c, apa, config) and
|
||||
nodeMayUseSummary0(n, c, state, apa, config)
|
||||
)
|
||||
}
|
||||
|
||||
private newtype TSummaryCtx =
|
||||
TSummaryCtxNone() or
|
||||
TSummaryCtxSome(ParamNodeEx p, FlowState state, AccessPath ap) {
|
||||
@@ -3557,7 +3661,7 @@ private newtype TPathNode =
|
||||
* of dereference operations needed to get from the value in the node to the
|
||||
* tracked object. The final type indicates the type of the tracked object.
|
||||
*/
|
||||
abstract private class AccessPath extends TAccessPath {
|
||||
private class AccessPath extends TAccessPath {
|
||||
/** Gets the head of this access path, if any. */
|
||||
abstract TypedContent getHead();
|
||||
|
||||
@@ -3772,11 +3876,14 @@ abstract private class PathNodeImpl extends PathNode {
|
||||
abstract NodeEx getNodeEx();
|
||||
|
||||
predicate isHidden() {
|
||||
hiddenNode(this.getNodeEx().asNode()) and
|
||||
not this.isSource() and
|
||||
not this instanceof PathNodeSink
|
||||
or
|
||||
this.getNodeEx() instanceof TNodeImplicitRead
|
||||
not this.getConfiguration().includeHiddenNodes() and
|
||||
(
|
||||
hiddenNode(this.getNodeEx().asNode()) and
|
||||
not this.isSource() and
|
||||
not this instanceof PathNodeSink
|
||||
or
|
||||
this.getNodeEx() instanceof TNodeImplicitRead
|
||||
)
|
||||
}
|
||||
|
||||
private string ppAp() {
|
||||
@@ -4257,6 +4364,12 @@ private module Subpaths {
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate hasSuccessor(PathNode pred, PathNodeMid succ, NodeEx succNode) {
|
||||
succ = pred.getASuccessor() and
|
||||
succNode = succ.getNodeEx()
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `(arg, par, ret, out)` forms a subpath-tuple, that is, flow through
|
||||
* a subpath between `par` and `ret` with the connecting edges `arg -> par` and
|
||||
@@ -4264,15 +4377,13 @@ private module Subpaths {
|
||||
*/
|
||||
predicate subpaths(PathNode arg, PathNodeImpl par, PathNodeImpl ret, PathNode out) {
|
||||
exists(ParamNodeEx p, NodeEx o, FlowState sout, AccessPath apout, PathNodeMid out0 |
|
||||
pragma[only_bind_into](arg).getASuccessor() = par and
|
||||
pragma[only_bind_into](arg).getASuccessor() = out0 and
|
||||
subpaths03(arg, p, localStepToHidden*(ret), o, sout, apout) and
|
||||
pragma[only_bind_into](arg).getASuccessor() = pragma[only_bind_into](out0) and
|
||||
subpaths03(pragma[only_bind_into](arg), p, localStepToHidden*(ret), o, sout, apout) and
|
||||
hasSuccessor(pragma[only_bind_into](arg), par, p) and
|
||||
not ret.isHidden() and
|
||||
par.getNodeEx() = p and
|
||||
out0.getNodeEx() = o and
|
||||
out0.getState() = sout and
|
||||
out0.getAp() = apout and
|
||||
(out = out0 or out = out0.projectToSink())
|
||||
pathNode(out0, o, sout, _, _, apout, _, _)
|
||||
|
|
||||
out = out0 or out = out0.projectToSink()
|
||||
)
|
||||
}
|
||||
|
||||
@@ -4609,6 +4720,10 @@ private module FlowExploration {
|
||||
exists(PartialPathNodeRev mid |
|
||||
revPartialPathStep(mid, node, state, sc1, sc2, sc3, ap, config) and
|
||||
not clearsContentEx(node, ap.getHead()) and
|
||||
(
|
||||
notExpectsContent(node) or
|
||||
expectsContentEx(node, ap.getHead())
|
||||
) and
|
||||
not fullBarrier(node, config) and
|
||||
not stateBarrier(node, state, config) and
|
||||
distSink(node.getEnclosingCallable(), config) <= config.explorationLimit()
|
||||
@@ -4625,6 +4740,10 @@ private module FlowExploration {
|
||||
not fullBarrier(node, config) and
|
||||
not stateBarrier(node, state, config) and
|
||||
not clearsContentEx(node, ap.getHead().getContent()) and
|
||||
(
|
||||
notExpectsContent(node) or
|
||||
expectsContentEx(node, ap.getHead().getContent())
|
||||
) and
|
||||
if node.asNode() instanceof CastingNode
|
||||
then compatibleTypes(node.getDataFlowType(), ap.getType())
|
||||
else any()
|
||||
|
||||
@@ -170,6 +170,14 @@ abstract class Configuration extends string {
|
||||
*/
|
||||
int explorationLimit() { none() }
|
||||
|
||||
/**
|
||||
* Holds if hidden nodes should be included in the data flow graph.
|
||||
*
|
||||
* This feature should only be used for debugging or when the data flow graph
|
||||
* is not visualized (for example in a `path-problem` query).
|
||||
*/
|
||||
predicate includeHiddenNodes() { none() }
|
||||
|
||||
/**
|
||||
* Holds if there is a partial data flow path from `source` to `node`. The
|
||||
* approximate distance between `node` and the closest source is `dist` and
|
||||
@@ -498,23 +506,35 @@ private predicate readSet(NodeEx node1, ContentSet c, NodeEx node2, Configuratio
|
||||
}
|
||||
|
||||
// inline to reduce fan-out via `getAReadContent`
|
||||
pragma[inline]
|
||||
bindingset[c]
|
||||
private predicate read(NodeEx node1, Content c, NodeEx node2, Configuration config) {
|
||||
exists(ContentSet cs |
|
||||
readSet(node1, cs, node2, config) and
|
||||
c = cs.getAReadContent()
|
||||
pragma[only_bind_out](c) = pragma[only_bind_into](cs).getAReadContent()
|
||||
)
|
||||
}
|
||||
|
||||
// inline to reduce fan-out via `getAReadContent`
|
||||
pragma[inline]
|
||||
bindingset[c]
|
||||
private predicate clearsContentEx(NodeEx n, Content c) {
|
||||
exists(ContentSet cs |
|
||||
clearsContentCached(n.asNode(), cs) and
|
||||
c = cs.getAReadContent()
|
||||
pragma[only_bind_out](c) = pragma[only_bind_into](cs).getAReadContent()
|
||||
)
|
||||
}
|
||||
|
||||
// inline to reduce fan-out via `getAReadContent`
|
||||
bindingset[c]
|
||||
private predicate expectsContentEx(NodeEx n, Content c) {
|
||||
exists(ContentSet cs |
|
||||
expectsContentCached(n.asNode(), cs) and
|
||||
pragma[only_bind_out](c) = pragma[only_bind_into](cs).getAReadContent()
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate notExpectsContent(NodeEx n) { not expectsContentCached(n.asNode(), _) }
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate store(
|
||||
NodeEx node1, TypedContent tc, NodeEx node2, DataFlowType contentType, Configuration config
|
||||
@@ -793,7 +813,7 @@ private module Stage1 {
|
||||
* by `revFlow`.
|
||||
*/
|
||||
pragma[nomagic]
|
||||
private predicate revFlowIsReadAndStored(Content c, Configuration conf) {
|
||||
predicate revFlowIsReadAndStored(Content c, Configuration conf) {
|
||||
revFlowConsCand(c, conf) and
|
||||
revFlowStore(c, _, _, conf)
|
||||
}
|
||||
@@ -891,7 +911,7 @@ private module Stage1 {
|
||||
|
||||
pragma[nomagic]
|
||||
predicate readStepCand(NodeEx n1, Content c, NodeEx n2, Configuration config) {
|
||||
revFlowIsReadAndStored(pragma[only_bind_into](c), pragma[only_bind_into](config)) and
|
||||
revFlowIsReadAndStored(c, pragma[only_bind_into](config)) and
|
||||
read(n1, c, n2, pragma[only_bind_into](config)) and
|
||||
revFlow(n2, pragma[only_bind_into](config))
|
||||
}
|
||||
@@ -1181,11 +1201,26 @@ private module Stage2 {
|
||||
|
||||
private predicate flowIntoCall = flowIntoCallNodeCand1/5;
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate expectsContentCand(NodeEx node, Configuration config) {
|
||||
exists(Content c |
|
||||
PrevStage::revFlow(node, pragma[only_bind_into](config)) and
|
||||
PrevStage::revFlowIsReadAndStored(c, pragma[only_bind_into](config)) and
|
||||
expectsContentEx(node, c)
|
||||
)
|
||||
}
|
||||
|
||||
bindingset[node, state, ap, config]
|
||||
private predicate filter(NodeEx node, FlowState state, Ap ap, Configuration config) {
|
||||
PrevStage::revFlowState(state, pragma[only_bind_into](config)) and
|
||||
exists(ap) and
|
||||
not stateBarrier(node, state, config)
|
||||
not stateBarrier(node, state, config) and
|
||||
(
|
||||
notExpectsContent(node)
|
||||
or
|
||||
ap = true and
|
||||
expectsContentCand(node, config)
|
||||
)
|
||||
}
|
||||
|
||||
bindingset[ap, contentType]
|
||||
@@ -1646,10 +1681,24 @@ private module Stage2 {
|
||||
storeStepFwd(_, ap, tc, _, _, config)
|
||||
}
|
||||
|
||||
predicate consCand(TypedContent tc, Ap ap, Configuration config) {
|
||||
private predicate revConsCand(TypedContent tc, Ap ap, Configuration config) {
|
||||
storeStepCand(_, ap, tc, _, _, config)
|
||||
}
|
||||
|
||||
private predicate validAp(Ap ap, Configuration config) {
|
||||
revFlow(_, _, _, _, ap, config) and ap instanceof ApNil
|
||||
or
|
||||
exists(TypedContent head, Ap tail |
|
||||
consCand(head, tail, config) and
|
||||
ap = apCons(head, tail)
|
||||
)
|
||||
}
|
||||
|
||||
predicate consCand(TypedContent tc, Ap ap, Configuration config) {
|
||||
revConsCand(tc, ap, config) and
|
||||
validAp(ap, config)
|
||||
}
|
||||
|
||||
pragma[noinline]
|
||||
private predicate parameterFlow(
|
||||
ParamNodeEx p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config
|
||||
@@ -1740,7 +1789,8 @@ private module LocalFlowBigStep {
|
||||
private class FlowCheckNode extends NodeEx {
|
||||
FlowCheckNode() {
|
||||
castNode(this.asNode()) or
|
||||
clearsContentCached(this.asNode(), _)
|
||||
clearsContentCached(this.asNode(), _) or
|
||||
expectsContentCached(this.asNode(), _)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1979,6 +2029,16 @@ private module Stage3 {
|
||||
clearContent(node, ap.getHead().getContent(), config)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate expectsContentCand(NodeEx node, Ap ap, Configuration config) {
|
||||
exists(Content c |
|
||||
PrevStage::revFlow(node, pragma[only_bind_into](config)) and
|
||||
PrevStage::readStepCand(_, c, _, pragma[only_bind_into](config)) and
|
||||
expectsContentEx(node, c) and
|
||||
c = ap.getHead().getContent()
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate castingNodeEx(NodeEx node) { node.asNode() instanceof CastingNode }
|
||||
|
||||
@@ -1987,7 +2047,12 @@ private module Stage3 {
|
||||
exists(state) and
|
||||
exists(config) and
|
||||
not clear(node, ap, config) and
|
||||
if castingNodeEx(node) then compatibleTypes(node.getDataFlowType(), ap.getType()) else any()
|
||||
(if castingNodeEx(node) then compatibleTypes(node.getDataFlowType(), ap.getType()) else any()) and
|
||||
(
|
||||
notExpectsContent(node)
|
||||
or
|
||||
expectsContentCand(node, ap, config)
|
||||
)
|
||||
}
|
||||
|
||||
bindingset[ap, contentType]
|
||||
@@ -2452,10 +2517,24 @@ private module Stage3 {
|
||||
storeStepFwd(_, ap, tc, _, _, config)
|
||||
}
|
||||
|
||||
predicate consCand(TypedContent tc, Ap ap, Configuration config) {
|
||||
private predicate revConsCand(TypedContent tc, Ap ap, Configuration config) {
|
||||
storeStepCand(_, ap, tc, _, _, config)
|
||||
}
|
||||
|
||||
private predicate validAp(Ap ap, Configuration config) {
|
||||
revFlow(_, _, _, _, ap, config) and ap instanceof ApNil
|
||||
or
|
||||
exists(TypedContent head, Ap tail |
|
||||
consCand(head, tail, config) and
|
||||
ap = apCons(head, tail)
|
||||
)
|
||||
}
|
||||
|
||||
predicate consCand(TypedContent tc, Ap ap, Configuration config) {
|
||||
revConsCand(tc, ap, config) and
|
||||
validAp(ap, config)
|
||||
}
|
||||
|
||||
pragma[noinline]
|
||||
private predicate parameterFlow(
|
||||
ParamNodeEx p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config
|
||||
@@ -3279,10 +3358,24 @@ private module Stage4 {
|
||||
storeStepFwd(_, ap, tc, _, _, config)
|
||||
}
|
||||
|
||||
predicate consCand(TypedContent tc, Ap ap, Configuration config) {
|
||||
private predicate revConsCand(TypedContent tc, Ap ap, Configuration config) {
|
||||
storeStepCand(_, ap, tc, _, _, config)
|
||||
}
|
||||
|
||||
private predicate validAp(Ap ap, Configuration config) {
|
||||
revFlow(_, _, _, _, ap, config) and ap instanceof ApNil
|
||||
or
|
||||
exists(TypedContent head, Ap tail |
|
||||
consCand(head, tail, config) and
|
||||
ap = apCons(head, tail)
|
||||
)
|
||||
}
|
||||
|
||||
predicate consCand(TypedContent tc, Ap ap, Configuration config) {
|
||||
revConsCand(tc, ap, config) and
|
||||
validAp(ap, config)
|
||||
}
|
||||
|
||||
pragma[noinline]
|
||||
private predicate parameterFlow(
|
||||
ParamNodeEx p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config
|
||||
@@ -3351,17 +3444,28 @@ private Configuration unbindConf(Configuration conf) {
|
||||
exists(Configuration c | result = pragma[only_bind_into](c) and conf = pragma[only_bind_into](c))
|
||||
}
|
||||
|
||||
private predicate nodeMayUseSummary(
|
||||
NodeEx n, FlowState state, AccessPathApprox apa, Configuration config
|
||||
pragma[nomagic]
|
||||
private predicate nodeMayUseSummary0(
|
||||
NodeEx n, DataFlowCallable c, FlowState state, AccessPathApprox apa, Configuration config
|
||||
) {
|
||||
exists(DataFlowCallable c, AccessPathApprox apa0 |
|
||||
Stage4::parameterMayFlowThrough(_, c, apa, _) and
|
||||
exists(AccessPathApprox apa0 |
|
||||
Stage4::parameterMayFlowThrough(_, c, _, _) and
|
||||
Stage4::revFlow(n, state, true, _, apa0, config) and
|
||||
Stage4::fwdFlow(n, state, any(CallContextCall ccc), TAccessPathApproxSome(apa), apa0, config) and
|
||||
n.getEnclosingCallable() = c
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate nodeMayUseSummary(
|
||||
NodeEx n, FlowState state, AccessPathApprox apa, Configuration config
|
||||
) {
|
||||
exists(DataFlowCallable c |
|
||||
Stage4::parameterMayFlowThrough(_, c, apa, config) and
|
||||
nodeMayUseSummary0(n, c, state, apa, config)
|
||||
)
|
||||
}
|
||||
|
||||
private newtype TSummaryCtx =
|
||||
TSummaryCtxNone() or
|
||||
TSummaryCtxSome(ParamNodeEx p, FlowState state, AccessPath ap) {
|
||||
@@ -3557,7 +3661,7 @@ private newtype TPathNode =
|
||||
* of dereference operations needed to get from the value in the node to the
|
||||
* tracked object. The final type indicates the type of the tracked object.
|
||||
*/
|
||||
abstract private class AccessPath extends TAccessPath {
|
||||
private class AccessPath extends TAccessPath {
|
||||
/** Gets the head of this access path, if any. */
|
||||
abstract TypedContent getHead();
|
||||
|
||||
@@ -3772,11 +3876,14 @@ abstract private class PathNodeImpl extends PathNode {
|
||||
abstract NodeEx getNodeEx();
|
||||
|
||||
predicate isHidden() {
|
||||
hiddenNode(this.getNodeEx().asNode()) and
|
||||
not this.isSource() and
|
||||
not this instanceof PathNodeSink
|
||||
or
|
||||
this.getNodeEx() instanceof TNodeImplicitRead
|
||||
not this.getConfiguration().includeHiddenNodes() and
|
||||
(
|
||||
hiddenNode(this.getNodeEx().asNode()) and
|
||||
not this.isSource() and
|
||||
not this instanceof PathNodeSink
|
||||
or
|
||||
this.getNodeEx() instanceof TNodeImplicitRead
|
||||
)
|
||||
}
|
||||
|
||||
private string ppAp() {
|
||||
@@ -4257,6 +4364,12 @@ private module Subpaths {
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate hasSuccessor(PathNode pred, PathNodeMid succ, NodeEx succNode) {
|
||||
succ = pred.getASuccessor() and
|
||||
succNode = succ.getNodeEx()
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `(arg, par, ret, out)` forms a subpath-tuple, that is, flow through
|
||||
* a subpath between `par` and `ret` with the connecting edges `arg -> par` and
|
||||
@@ -4264,15 +4377,13 @@ private module Subpaths {
|
||||
*/
|
||||
predicate subpaths(PathNode arg, PathNodeImpl par, PathNodeImpl ret, PathNode out) {
|
||||
exists(ParamNodeEx p, NodeEx o, FlowState sout, AccessPath apout, PathNodeMid out0 |
|
||||
pragma[only_bind_into](arg).getASuccessor() = par and
|
||||
pragma[only_bind_into](arg).getASuccessor() = out0 and
|
||||
subpaths03(arg, p, localStepToHidden*(ret), o, sout, apout) and
|
||||
pragma[only_bind_into](arg).getASuccessor() = pragma[only_bind_into](out0) and
|
||||
subpaths03(pragma[only_bind_into](arg), p, localStepToHidden*(ret), o, sout, apout) and
|
||||
hasSuccessor(pragma[only_bind_into](arg), par, p) and
|
||||
not ret.isHidden() and
|
||||
par.getNodeEx() = p and
|
||||
out0.getNodeEx() = o and
|
||||
out0.getState() = sout and
|
||||
out0.getAp() = apout and
|
||||
(out = out0 or out = out0.projectToSink())
|
||||
pathNode(out0, o, sout, _, _, apout, _, _)
|
||||
|
|
||||
out = out0 or out = out0.projectToSink()
|
||||
)
|
||||
}
|
||||
|
||||
@@ -4609,6 +4720,10 @@ private module FlowExploration {
|
||||
exists(PartialPathNodeRev mid |
|
||||
revPartialPathStep(mid, node, state, sc1, sc2, sc3, ap, config) and
|
||||
not clearsContentEx(node, ap.getHead()) and
|
||||
(
|
||||
notExpectsContent(node) or
|
||||
expectsContentEx(node, ap.getHead())
|
||||
) and
|
||||
not fullBarrier(node, config) and
|
||||
not stateBarrier(node, state, config) and
|
||||
distSink(node.getEnclosingCallable(), config) <= config.explorationLimit()
|
||||
@@ -4625,6 +4740,10 @@ private module FlowExploration {
|
||||
not fullBarrier(node, config) and
|
||||
not stateBarrier(node, state, config) and
|
||||
not clearsContentEx(node, ap.getHead().getContent()) and
|
||||
(
|
||||
notExpectsContent(node) or
|
||||
expectsContentEx(node, ap.getHead().getContent())
|
||||
) and
|
||||
if node.asNode() instanceof CastingNode
|
||||
then compatibleTypes(node.getDataFlowType(), ap.getType())
|
||||
else any()
|
||||
|
||||
@@ -170,6 +170,14 @@ abstract class Configuration extends string {
|
||||
*/
|
||||
int explorationLimit() { none() }
|
||||
|
||||
/**
|
||||
* Holds if hidden nodes should be included in the data flow graph.
|
||||
*
|
||||
* This feature should only be used for debugging or when the data flow graph
|
||||
* is not visualized (for example in a `path-problem` query).
|
||||
*/
|
||||
predicate includeHiddenNodes() { none() }
|
||||
|
||||
/**
|
||||
* Holds if there is a partial data flow path from `source` to `node`. The
|
||||
* approximate distance between `node` and the closest source is `dist` and
|
||||
@@ -498,23 +506,35 @@ private predicate readSet(NodeEx node1, ContentSet c, NodeEx node2, Configuratio
|
||||
}
|
||||
|
||||
// inline to reduce fan-out via `getAReadContent`
|
||||
pragma[inline]
|
||||
bindingset[c]
|
||||
private predicate read(NodeEx node1, Content c, NodeEx node2, Configuration config) {
|
||||
exists(ContentSet cs |
|
||||
readSet(node1, cs, node2, config) and
|
||||
c = cs.getAReadContent()
|
||||
pragma[only_bind_out](c) = pragma[only_bind_into](cs).getAReadContent()
|
||||
)
|
||||
}
|
||||
|
||||
// inline to reduce fan-out via `getAReadContent`
|
||||
pragma[inline]
|
||||
bindingset[c]
|
||||
private predicate clearsContentEx(NodeEx n, Content c) {
|
||||
exists(ContentSet cs |
|
||||
clearsContentCached(n.asNode(), cs) and
|
||||
c = cs.getAReadContent()
|
||||
pragma[only_bind_out](c) = pragma[only_bind_into](cs).getAReadContent()
|
||||
)
|
||||
}
|
||||
|
||||
// inline to reduce fan-out via `getAReadContent`
|
||||
bindingset[c]
|
||||
private predicate expectsContentEx(NodeEx n, Content c) {
|
||||
exists(ContentSet cs |
|
||||
expectsContentCached(n.asNode(), cs) and
|
||||
pragma[only_bind_out](c) = pragma[only_bind_into](cs).getAReadContent()
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate notExpectsContent(NodeEx n) { not expectsContentCached(n.asNode(), _) }
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate store(
|
||||
NodeEx node1, TypedContent tc, NodeEx node2, DataFlowType contentType, Configuration config
|
||||
@@ -793,7 +813,7 @@ private module Stage1 {
|
||||
* by `revFlow`.
|
||||
*/
|
||||
pragma[nomagic]
|
||||
private predicate revFlowIsReadAndStored(Content c, Configuration conf) {
|
||||
predicate revFlowIsReadAndStored(Content c, Configuration conf) {
|
||||
revFlowConsCand(c, conf) and
|
||||
revFlowStore(c, _, _, conf)
|
||||
}
|
||||
@@ -891,7 +911,7 @@ private module Stage1 {
|
||||
|
||||
pragma[nomagic]
|
||||
predicate readStepCand(NodeEx n1, Content c, NodeEx n2, Configuration config) {
|
||||
revFlowIsReadAndStored(pragma[only_bind_into](c), pragma[only_bind_into](config)) and
|
||||
revFlowIsReadAndStored(c, pragma[only_bind_into](config)) and
|
||||
read(n1, c, n2, pragma[only_bind_into](config)) and
|
||||
revFlow(n2, pragma[only_bind_into](config))
|
||||
}
|
||||
@@ -1181,11 +1201,26 @@ private module Stage2 {
|
||||
|
||||
private predicate flowIntoCall = flowIntoCallNodeCand1/5;
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate expectsContentCand(NodeEx node, Configuration config) {
|
||||
exists(Content c |
|
||||
PrevStage::revFlow(node, pragma[only_bind_into](config)) and
|
||||
PrevStage::revFlowIsReadAndStored(c, pragma[only_bind_into](config)) and
|
||||
expectsContentEx(node, c)
|
||||
)
|
||||
}
|
||||
|
||||
bindingset[node, state, ap, config]
|
||||
private predicate filter(NodeEx node, FlowState state, Ap ap, Configuration config) {
|
||||
PrevStage::revFlowState(state, pragma[only_bind_into](config)) and
|
||||
exists(ap) and
|
||||
not stateBarrier(node, state, config)
|
||||
not stateBarrier(node, state, config) and
|
||||
(
|
||||
notExpectsContent(node)
|
||||
or
|
||||
ap = true and
|
||||
expectsContentCand(node, config)
|
||||
)
|
||||
}
|
||||
|
||||
bindingset[ap, contentType]
|
||||
@@ -1646,10 +1681,24 @@ private module Stage2 {
|
||||
storeStepFwd(_, ap, tc, _, _, config)
|
||||
}
|
||||
|
||||
predicate consCand(TypedContent tc, Ap ap, Configuration config) {
|
||||
private predicate revConsCand(TypedContent tc, Ap ap, Configuration config) {
|
||||
storeStepCand(_, ap, tc, _, _, config)
|
||||
}
|
||||
|
||||
private predicate validAp(Ap ap, Configuration config) {
|
||||
revFlow(_, _, _, _, ap, config) and ap instanceof ApNil
|
||||
or
|
||||
exists(TypedContent head, Ap tail |
|
||||
consCand(head, tail, config) and
|
||||
ap = apCons(head, tail)
|
||||
)
|
||||
}
|
||||
|
||||
predicate consCand(TypedContent tc, Ap ap, Configuration config) {
|
||||
revConsCand(tc, ap, config) and
|
||||
validAp(ap, config)
|
||||
}
|
||||
|
||||
pragma[noinline]
|
||||
private predicate parameterFlow(
|
||||
ParamNodeEx p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config
|
||||
@@ -1740,7 +1789,8 @@ private module LocalFlowBigStep {
|
||||
private class FlowCheckNode extends NodeEx {
|
||||
FlowCheckNode() {
|
||||
castNode(this.asNode()) or
|
||||
clearsContentCached(this.asNode(), _)
|
||||
clearsContentCached(this.asNode(), _) or
|
||||
expectsContentCached(this.asNode(), _)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1979,6 +2029,16 @@ private module Stage3 {
|
||||
clearContent(node, ap.getHead().getContent(), config)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate expectsContentCand(NodeEx node, Ap ap, Configuration config) {
|
||||
exists(Content c |
|
||||
PrevStage::revFlow(node, pragma[only_bind_into](config)) and
|
||||
PrevStage::readStepCand(_, c, _, pragma[only_bind_into](config)) and
|
||||
expectsContentEx(node, c) and
|
||||
c = ap.getHead().getContent()
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate castingNodeEx(NodeEx node) { node.asNode() instanceof CastingNode }
|
||||
|
||||
@@ -1987,7 +2047,12 @@ private module Stage3 {
|
||||
exists(state) and
|
||||
exists(config) and
|
||||
not clear(node, ap, config) and
|
||||
if castingNodeEx(node) then compatibleTypes(node.getDataFlowType(), ap.getType()) else any()
|
||||
(if castingNodeEx(node) then compatibleTypes(node.getDataFlowType(), ap.getType()) else any()) and
|
||||
(
|
||||
notExpectsContent(node)
|
||||
or
|
||||
expectsContentCand(node, ap, config)
|
||||
)
|
||||
}
|
||||
|
||||
bindingset[ap, contentType]
|
||||
@@ -2452,10 +2517,24 @@ private module Stage3 {
|
||||
storeStepFwd(_, ap, tc, _, _, config)
|
||||
}
|
||||
|
||||
predicate consCand(TypedContent tc, Ap ap, Configuration config) {
|
||||
private predicate revConsCand(TypedContent tc, Ap ap, Configuration config) {
|
||||
storeStepCand(_, ap, tc, _, _, config)
|
||||
}
|
||||
|
||||
private predicate validAp(Ap ap, Configuration config) {
|
||||
revFlow(_, _, _, _, ap, config) and ap instanceof ApNil
|
||||
or
|
||||
exists(TypedContent head, Ap tail |
|
||||
consCand(head, tail, config) and
|
||||
ap = apCons(head, tail)
|
||||
)
|
||||
}
|
||||
|
||||
predicate consCand(TypedContent tc, Ap ap, Configuration config) {
|
||||
revConsCand(tc, ap, config) and
|
||||
validAp(ap, config)
|
||||
}
|
||||
|
||||
pragma[noinline]
|
||||
private predicate parameterFlow(
|
||||
ParamNodeEx p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config
|
||||
@@ -3279,10 +3358,24 @@ private module Stage4 {
|
||||
storeStepFwd(_, ap, tc, _, _, config)
|
||||
}
|
||||
|
||||
predicate consCand(TypedContent tc, Ap ap, Configuration config) {
|
||||
private predicate revConsCand(TypedContent tc, Ap ap, Configuration config) {
|
||||
storeStepCand(_, ap, tc, _, _, config)
|
||||
}
|
||||
|
||||
private predicate validAp(Ap ap, Configuration config) {
|
||||
revFlow(_, _, _, _, ap, config) and ap instanceof ApNil
|
||||
or
|
||||
exists(TypedContent head, Ap tail |
|
||||
consCand(head, tail, config) and
|
||||
ap = apCons(head, tail)
|
||||
)
|
||||
}
|
||||
|
||||
predicate consCand(TypedContent tc, Ap ap, Configuration config) {
|
||||
revConsCand(tc, ap, config) and
|
||||
validAp(ap, config)
|
||||
}
|
||||
|
||||
pragma[noinline]
|
||||
private predicate parameterFlow(
|
||||
ParamNodeEx p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config
|
||||
@@ -3351,17 +3444,28 @@ private Configuration unbindConf(Configuration conf) {
|
||||
exists(Configuration c | result = pragma[only_bind_into](c) and conf = pragma[only_bind_into](c))
|
||||
}
|
||||
|
||||
private predicate nodeMayUseSummary(
|
||||
NodeEx n, FlowState state, AccessPathApprox apa, Configuration config
|
||||
pragma[nomagic]
|
||||
private predicate nodeMayUseSummary0(
|
||||
NodeEx n, DataFlowCallable c, FlowState state, AccessPathApprox apa, Configuration config
|
||||
) {
|
||||
exists(DataFlowCallable c, AccessPathApprox apa0 |
|
||||
Stage4::parameterMayFlowThrough(_, c, apa, _) and
|
||||
exists(AccessPathApprox apa0 |
|
||||
Stage4::parameterMayFlowThrough(_, c, _, _) and
|
||||
Stage4::revFlow(n, state, true, _, apa0, config) and
|
||||
Stage4::fwdFlow(n, state, any(CallContextCall ccc), TAccessPathApproxSome(apa), apa0, config) and
|
||||
n.getEnclosingCallable() = c
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate nodeMayUseSummary(
|
||||
NodeEx n, FlowState state, AccessPathApprox apa, Configuration config
|
||||
) {
|
||||
exists(DataFlowCallable c |
|
||||
Stage4::parameterMayFlowThrough(_, c, apa, config) and
|
||||
nodeMayUseSummary0(n, c, state, apa, config)
|
||||
)
|
||||
}
|
||||
|
||||
private newtype TSummaryCtx =
|
||||
TSummaryCtxNone() or
|
||||
TSummaryCtxSome(ParamNodeEx p, FlowState state, AccessPath ap) {
|
||||
@@ -3557,7 +3661,7 @@ private newtype TPathNode =
|
||||
* of dereference operations needed to get from the value in the node to the
|
||||
* tracked object. The final type indicates the type of the tracked object.
|
||||
*/
|
||||
abstract private class AccessPath extends TAccessPath {
|
||||
private class AccessPath extends TAccessPath {
|
||||
/** Gets the head of this access path, if any. */
|
||||
abstract TypedContent getHead();
|
||||
|
||||
@@ -3772,11 +3876,14 @@ abstract private class PathNodeImpl extends PathNode {
|
||||
abstract NodeEx getNodeEx();
|
||||
|
||||
predicate isHidden() {
|
||||
hiddenNode(this.getNodeEx().asNode()) and
|
||||
not this.isSource() and
|
||||
not this instanceof PathNodeSink
|
||||
or
|
||||
this.getNodeEx() instanceof TNodeImplicitRead
|
||||
not this.getConfiguration().includeHiddenNodes() and
|
||||
(
|
||||
hiddenNode(this.getNodeEx().asNode()) and
|
||||
not this.isSource() and
|
||||
not this instanceof PathNodeSink
|
||||
or
|
||||
this.getNodeEx() instanceof TNodeImplicitRead
|
||||
)
|
||||
}
|
||||
|
||||
private string ppAp() {
|
||||
@@ -4257,6 +4364,12 @@ private module Subpaths {
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate hasSuccessor(PathNode pred, PathNodeMid succ, NodeEx succNode) {
|
||||
succ = pred.getASuccessor() and
|
||||
succNode = succ.getNodeEx()
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `(arg, par, ret, out)` forms a subpath-tuple, that is, flow through
|
||||
* a subpath between `par` and `ret` with the connecting edges `arg -> par` and
|
||||
@@ -4264,15 +4377,13 @@ private module Subpaths {
|
||||
*/
|
||||
predicate subpaths(PathNode arg, PathNodeImpl par, PathNodeImpl ret, PathNode out) {
|
||||
exists(ParamNodeEx p, NodeEx o, FlowState sout, AccessPath apout, PathNodeMid out0 |
|
||||
pragma[only_bind_into](arg).getASuccessor() = par and
|
||||
pragma[only_bind_into](arg).getASuccessor() = out0 and
|
||||
subpaths03(arg, p, localStepToHidden*(ret), o, sout, apout) and
|
||||
pragma[only_bind_into](arg).getASuccessor() = pragma[only_bind_into](out0) and
|
||||
subpaths03(pragma[only_bind_into](arg), p, localStepToHidden*(ret), o, sout, apout) and
|
||||
hasSuccessor(pragma[only_bind_into](arg), par, p) and
|
||||
not ret.isHidden() and
|
||||
par.getNodeEx() = p and
|
||||
out0.getNodeEx() = o and
|
||||
out0.getState() = sout and
|
||||
out0.getAp() = apout and
|
||||
(out = out0 or out = out0.projectToSink())
|
||||
pathNode(out0, o, sout, _, _, apout, _, _)
|
||||
|
|
||||
out = out0 or out = out0.projectToSink()
|
||||
)
|
||||
}
|
||||
|
||||
@@ -4609,6 +4720,10 @@ private module FlowExploration {
|
||||
exists(PartialPathNodeRev mid |
|
||||
revPartialPathStep(mid, node, state, sc1, sc2, sc3, ap, config) and
|
||||
not clearsContentEx(node, ap.getHead()) and
|
||||
(
|
||||
notExpectsContent(node) or
|
||||
expectsContentEx(node, ap.getHead())
|
||||
) and
|
||||
not fullBarrier(node, config) and
|
||||
not stateBarrier(node, state, config) and
|
||||
distSink(node.getEnclosingCallable(), config) <= config.explorationLimit()
|
||||
@@ -4625,6 +4740,10 @@ private module FlowExploration {
|
||||
not fullBarrier(node, config) and
|
||||
not stateBarrier(node, state, config) and
|
||||
not clearsContentEx(node, ap.getHead().getContent()) and
|
||||
(
|
||||
notExpectsContent(node) or
|
||||
expectsContentEx(node, ap.getHead().getContent())
|
||||
) and
|
||||
if node.asNode() instanceof CastingNode
|
||||
then compatibleTypes(node.getDataFlowType(), ap.getType())
|
||||
else any()
|
||||
|
||||
@@ -170,6 +170,14 @@ abstract class Configuration extends string {
|
||||
*/
|
||||
int explorationLimit() { none() }
|
||||
|
||||
/**
|
||||
* Holds if hidden nodes should be included in the data flow graph.
|
||||
*
|
||||
* This feature should only be used for debugging or when the data flow graph
|
||||
* is not visualized (for example in a `path-problem` query).
|
||||
*/
|
||||
predicate includeHiddenNodes() { none() }
|
||||
|
||||
/**
|
||||
* Holds if there is a partial data flow path from `source` to `node`. The
|
||||
* approximate distance between `node` and the closest source is `dist` and
|
||||
@@ -498,23 +506,35 @@ private predicate readSet(NodeEx node1, ContentSet c, NodeEx node2, Configuratio
|
||||
}
|
||||
|
||||
// inline to reduce fan-out via `getAReadContent`
|
||||
pragma[inline]
|
||||
bindingset[c]
|
||||
private predicate read(NodeEx node1, Content c, NodeEx node2, Configuration config) {
|
||||
exists(ContentSet cs |
|
||||
readSet(node1, cs, node2, config) and
|
||||
c = cs.getAReadContent()
|
||||
pragma[only_bind_out](c) = pragma[only_bind_into](cs).getAReadContent()
|
||||
)
|
||||
}
|
||||
|
||||
// inline to reduce fan-out via `getAReadContent`
|
||||
pragma[inline]
|
||||
bindingset[c]
|
||||
private predicate clearsContentEx(NodeEx n, Content c) {
|
||||
exists(ContentSet cs |
|
||||
clearsContentCached(n.asNode(), cs) and
|
||||
c = cs.getAReadContent()
|
||||
pragma[only_bind_out](c) = pragma[only_bind_into](cs).getAReadContent()
|
||||
)
|
||||
}
|
||||
|
||||
// inline to reduce fan-out via `getAReadContent`
|
||||
bindingset[c]
|
||||
private predicate expectsContentEx(NodeEx n, Content c) {
|
||||
exists(ContentSet cs |
|
||||
expectsContentCached(n.asNode(), cs) and
|
||||
pragma[only_bind_out](c) = pragma[only_bind_into](cs).getAReadContent()
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate notExpectsContent(NodeEx n) { not expectsContentCached(n.asNode(), _) }
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate store(
|
||||
NodeEx node1, TypedContent tc, NodeEx node2, DataFlowType contentType, Configuration config
|
||||
@@ -793,7 +813,7 @@ private module Stage1 {
|
||||
* by `revFlow`.
|
||||
*/
|
||||
pragma[nomagic]
|
||||
private predicate revFlowIsReadAndStored(Content c, Configuration conf) {
|
||||
predicate revFlowIsReadAndStored(Content c, Configuration conf) {
|
||||
revFlowConsCand(c, conf) and
|
||||
revFlowStore(c, _, _, conf)
|
||||
}
|
||||
@@ -891,7 +911,7 @@ private module Stage1 {
|
||||
|
||||
pragma[nomagic]
|
||||
predicate readStepCand(NodeEx n1, Content c, NodeEx n2, Configuration config) {
|
||||
revFlowIsReadAndStored(pragma[only_bind_into](c), pragma[only_bind_into](config)) and
|
||||
revFlowIsReadAndStored(c, pragma[only_bind_into](config)) and
|
||||
read(n1, c, n2, pragma[only_bind_into](config)) and
|
||||
revFlow(n2, pragma[only_bind_into](config))
|
||||
}
|
||||
@@ -1181,11 +1201,26 @@ private module Stage2 {
|
||||
|
||||
private predicate flowIntoCall = flowIntoCallNodeCand1/5;
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate expectsContentCand(NodeEx node, Configuration config) {
|
||||
exists(Content c |
|
||||
PrevStage::revFlow(node, pragma[only_bind_into](config)) and
|
||||
PrevStage::revFlowIsReadAndStored(c, pragma[only_bind_into](config)) and
|
||||
expectsContentEx(node, c)
|
||||
)
|
||||
}
|
||||
|
||||
bindingset[node, state, ap, config]
|
||||
private predicate filter(NodeEx node, FlowState state, Ap ap, Configuration config) {
|
||||
PrevStage::revFlowState(state, pragma[only_bind_into](config)) and
|
||||
exists(ap) and
|
||||
not stateBarrier(node, state, config)
|
||||
not stateBarrier(node, state, config) and
|
||||
(
|
||||
notExpectsContent(node)
|
||||
or
|
||||
ap = true and
|
||||
expectsContentCand(node, config)
|
||||
)
|
||||
}
|
||||
|
||||
bindingset[ap, contentType]
|
||||
@@ -1646,10 +1681,24 @@ private module Stage2 {
|
||||
storeStepFwd(_, ap, tc, _, _, config)
|
||||
}
|
||||
|
||||
predicate consCand(TypedContent tc, Ap ap, Configuration config) {
|
||||
private predicate revConsCand(TypedContent tc, Ap ap, Configuration config) {
|
||||
storeStepCand(_, ap, tc, _, _, config)
|
||||
}
|
||||
|
||||
private predicate validAp(Ap ap, Configuration config) {
|
||||
revFlow(_, _, _, _, ap, config) and ap instanceof ApNil
|
||||
or
|
||||
exists(TypedContent head, Ap tail |
|
||||
consCand(head, tail, config) and
|
||||
ap = apCons(head, tail)
|
||||
)
|
||||
}
|
||||
|
||||
predicate consCand(TypedContent tc, Ap ap, Configuration config) {
|
||||
revConsCand(tc, ap, config) and
|
||||
validAp(ap, config)
|
||||
}
|
||||
|
||||
pragma[noinline]
|
||||
private predicate parameterFlow(
|
||||
ParamNodeEx p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config
|
||||
@@ -1740,7 +1789,8 @@ private module LocalFlowBigStep {
|
||||
private class FlowCheckNode extends NodeEx {
|
||||
FlowCheckNode() {
|
||||
castNode(this.asNode()) or
|
||||
clearsContentCached(this.asNode(), _)
|
||||
clearsContentCached(this.asNode(), _) or
|
||||
expectsContentCached(this.asNode(), _)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1979,6 +2029,16 @@ private module Stage3 {
|
||||
clearContent(node, ap.getHead().getContent(), config)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate expectsContentCand(NodeEx node, Ap ap, Configuration config) {
|
||||
exists(Content c |
|
||||
PrevStage::revFlow(node, pragma[only_bind_into](config)) and
|
||||
PrevStage::readStepCand(_, c, _, pragma[only_bind_into](config)) and
|
||||
expectsContentEx(node, c) and
|
||||
c = ap.getHead().getContent()
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate castingNodeEx(NodeEx node) { node.asNode() instanceof CastingNode }
|
||||
|
||||
@@ -1987,7 +2047,12 @@ private module Stage3 {
|
||||
exists(state) and
|
||||
exists(config) and
|
||||
not clear(node, ap, config) and
|
||||
if castingNodeEx(node) then compatibleTypes(node.getDataFlowType(), ap.getType()) else any()
|
||||
(if castingNodeEx(node) then compatibleTypes(node.getDataFlowType(), ap.getType()) else any()) and
|
||||
(
|
||||
notExpectsContent(node)
|
||||
or
|
||||
expectsContentCand(node, ap, config)
|
||||
)
|
||||
}
|
||||
|
||||
bindingset[ap, contentType]
|
||||
@@ -2452,10 +2517,24 @@ private module Stage3 {
|
||||
storeStepFwd(_, ap, tc, _, _, config)
|
||||
}
|
||||
|
||||
predicate consCand(TypedContent tc, Ap ap, Configuration config) {
|
||||
private predicate revConsCand(TypedContent tc, Ap ap, Configuration config) {
|
||||
storeStepCand(_, ap, tc, _, _, config)
|
||||
}
|
||||
|
||||
private predicate validAp(Ap ap, Configuration config) {
|
||||
revFlow(_, _, _, _, ap, config) and ap instanceof ApNil
|
||||
or
|
||||
exists(TypedContent head, Ap tail |
|
||||
consCand(head, tail, config) and
|
||||
ap = apCons(head, tail)
|
||||
)
|
||||
}
|
||||
|
||||
predicate consCand(TypedContent tc, Ap ap, Configuration config) {
|
||||
revConsCand(tc, ap, config) and
|
||||
validAp(ap, config)
|
||||
}
|
||||
|
||||
pragma[noinline]
|
||||
private predicate parameterFlow(
|
||||
ParamNodeEx p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config
|
||||
@@ -3279,10 +3358,24 @@ private module Stage4 {
|
||||
storeStepFwd(_, ap, tc, _, _, config)
|
||||
}
|
||||
|
||||
predicate consCand(TypedContent tc, Ap ap, Configuration config) {
|
||||
private predicate revConsCand(TypedContent tc, Ap ap, Configuration config) {
|
||||
storeStepCand(_, ap, tc, _, _, config)
|
||||
}
|
||||
|
||||
private predicate validAp(Ap ap, Configuration config) {
|
||||
revFlow(_, _, _, _, ap, config) and ap instanceof ApNil
|
||||
or
|
||||
exists(TypedContent head, Ap tail |
|
||||
consCand(head, tail, config) and
|
||||
ap = apCons(head, tail)
|
||||
)
|
||||
}
|
||||
|
||||
predicate consCand(TypedContent tc, Ap ap, Configuration config) {
|
||||
revConsCand(tc, ap, config) and
|
||||
validAp(ap, config)
|
||||
}
|
||||
|
||||
pragma[noinline]
|
||||
private predicate parameterFlow(
|
||||
ParamNodeEx p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config
|
||||
@@ -3351,17 +3444,28 @@ private Configuration unbindConf(Configuration conf) {
|
||||
exists(Configuration c | result = pragma[only_bind_into](c) and conf = pragma[only_bind_into](c))
|
||||
}
|
||||
|
||||
private predicate nodeMayUseSummary(
|
||||
NodeEx n, FlowState state, AccessPathApprox apa, Configuration config
|
||||
pragma[nomagic]
|
||||
private predicate nodeMayUseSummary0(
|
||||
NodeEx n, DataFlowCallable c, FlowState state, AccessPathApprox apa, Configuration config
|
||||
) {
|
||||
exists(DataFlowCallable c, AccessPathApprox apa0 |
|
||||
Stage4::parameterMayFlowThrough(_, c, apa, _) and
|
||||
exists(AccessPathApprox apa0 |
|
||||
Stage4::parameterMayFlowThrough(_, c, _, _) and
|
||||
Stage4::revFlow(n, state, true, _, apa0, config) and
|
||||
Stage4::fwdFlow(n, state, any(CallContextCall ccc), TAccessPathApproxSome(apa), apa0, config) and
|
||||
n.getEnclosingCallable() = c
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate nodeMayUseSummary(
|
||||
NodeEx n, FlowState state, AccessPathApprox apa, Configuration config
|
||||
) {
|
||||
exists(DataFlowCallable c |
|
||||
Stage4::parameterMayFlowThrough(_, c, apa, config) and
|
||||
nodeMayUseSummary0(n, c, state, apa, config)
|
||||
)
|
||||
}
|
||||
|
||||
private newtype TSummaryCtx =
|
||||
TSummaryCtxNone() or
|
||||
TSummaryCtxSome(ParamNodeEx p, FlowState state, AccessPath ap) {
|
||||
@@ -3557,7 +3661,7 @@ private newtype TPathNode =
|
||||
* of dereference operations needed to get from the value in the node to the
|
||||
* tracked object. The final type indicates the type of the tracked object.
|
||||
*/
|
||||
abstract private class AccessPath extends TAccessPath {
|
||||
private class AccessPath extends TAccessPath {
|
||||
/** Gets the head of this access path, if any. */
|
||||
abstract TypedContent getHead();
|
||||
|
||||
@@ -3772,11 +3876,14 @@ abstract private class PathNodeImpl extends PathNode {
|
||||
abstract NodeEx getNodeEx();
|
||||
|
||||
predicate isHidden() {
|
||||
hiddenNode(this.getNodeEx().asNode()) and
|
||||
not this.isSource() and
|
||||
not this instanceof PathNodeSink
|
||||
or
|
||||
this.getNodeEx() instanceof TNodeImplicitRead
|
||||
not this.getConfiguration().includeHiddenNodes() and
|
||||
(
|
||||
hiddenNode(this.getNodeEx().asNode()) and
|
||||
not this.isSource() and
|
||||
not this instanceof PathNodeSink
|
||||
or
|
||||
this.getNodeEx() instanceof TNodeImplicitRead
|
||||
)
|
||||
}
|
||||
|
||||
private string ppAp() {
|
||||
@@ -4257,6 +4364,12 @@ private module Subpaths {
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate hasSuccessor(PathNode pred, PathNodeMid succ, NodeEx succNode) {
|
||||
succ = pred.getASuccessor() and
|
||||
succNode = succ.getNodeEx()
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `(arg, par, ret, out)` forms a subpath-tuple, that is, flow through
|
||||
* a subpath between `par` and `ret` with the connecting edges `arg -> par` and
|
||||
@@ -4264,15 +4377,13 @@ private module Subpaths {
|
||||
*/
|
||||
predicate subpaths(PathNode arg, PathNodeImpl par, PathNodeImpl ret, PathNode out) {
|
||||
exists(ParamNodeEx p, NodeEx o, FlowState sout, AccessPath apout, PathNodeMid out0 |
|
||||
pragma[only_bind_into](arg).getASuccessor() = par and
|
||||
pragma[only_bind_into](arg).getASuccessor() = out0 and
|
||||
subpaths03(arg, p, localStepToHidden*(ret), o, sout, apout) and
|
||||
pragma[only_bind_into](arg).getASuccessor() = pragma[only_bind_into](out0) and
|
||||
subpaths03(pragma[only_bind_into](arg), p, localStepToHidden*(ret), o, sout, apout) and
|
||||
hasSuccessor(pragma[only_bind_into](arg), par, p) and
|
||||
not ret.isHidden() and
|
||||
par.getNodeEx() = p and
|
||||
out0.getNodeEx() = o and
|
||||
out0.getState() = sout and
|
||||
out0.getAp() = apout and
|
||||
(out = out0 or out = out0.projectToSink())
|
||||
pathNode(out0, o, sout, _, _, apout, _, _)
|
||||
|
|
||||
out = out0 or out = out0.projectToSink()
|
||||
)
|
||||
}
|
||||
|
||||
@@ -4609,6 +4720,10 @@ private module FlowExploration {
|
||||
exists(PartialPathNodeRev mid |
|
||||
revPartialPathStep(mid, node, state, sc1, sc2, sc3, ap, config) and
|
||||
not clearsContentEx(node, ap.getHead()) and
|
||||
(
|
||||
notExpectsContent(node) or
|
||||
expectsContentEx(node, ap.getHead())
|
||||
) and
|
||||
not fullBarrier(node, config) and
|
||||
not stateBarrier(node, state, config) and
|
||||
distSink(node.getEnclosingCallable(), config) <= config.explorationLimit()
|
||||
@@ -4625,6 +4740,10 @@ private module FlowExploration {
|
||||
not fullBarrier(node, config) and
|
||||
not stateBarrier(node, state, config) and
|
||||
not clearsContentEx(node, ap.getHead().getContent()) and
|
||||
(
|
||||
notExpectsContent(node) or
|
||||
expectsContentEx(node, ap.getHead().getContent())
|
||||
) and
|
||||
if node.asNode() instanceof CastingNode
|
||||
then compatibleTypes(node.getDataFlowType(), ap.getType())
|
||||
else any()
|
||||
|
||||
@@ -305,7 +305,7 @@ cached
|
||||
private module Cached {
|
||||
/**
|
||||
* If needed, call this predicate from `DataFlowImplSpecific.qll` in order to
|
||||
* force a stage-dependency on the `DataFlowImplCommon.qll` stage and therby
|
||||
* force a stage-dependency on the `DataFlowImplCommon.qll` stage and thereby
|
||||
* collapsing the two stages.
|
||||
*/
|
||||
cached
|
||||
@@ -328,6 +328,9 @@ private module Cached {
|
||||
cached
|
||||
predicate clearsContentCached(Node n, ContentSet c) { clearsContent(n, c) }
|
||||
|
||||
cached
|
||||
predicate expectsContentCached(Node n, ContentSet c) { expectsContent(n, c) }
|
||||
|
||||
cached
|
||||
predicate isUnreachableInCallCached(Node n, DataFlowCall call) { isUnreachableInCall(n, call) }
|
||||
|
||||
|
||||
@@ -279,6 +279,12 @@ predicate clearsContent(Node n, Content c) {
|
||||
none() // stub implementation
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if the value that is being tracked is expected to be stored inside content `c`
|
||||
* at node `n`.
|
||||
*/
|
||||
predicate expectsContent(Node n, ContentSet c) { none() }
|
||||
|
||||
/** Gets the type of `n` used for type pruning. */
|
||||
IRType getNodeType(Node n) {
|
||||
suppressUnusedNode(n) and
|
||||
|
||||
@@ -37,7 +37,7 @@ private module Cached {
|
||||
* along the chain of addresses computed by `StoreNodeInstr.getInner` to identify field writes
|
||||
* and call `storeStep` accordingly (i.e., for an expression like `a.b.c = x`, we visit `c`, then
|
||||
* `b`, then `a`).
|
||||
* 2. Flow is transfered from a `WriteSideEffectInstruction` to a `StoreNodeOperand` after flow
|
||||
* 2. Flow is transferred from a `WriteSideEffectInstruction` to a `StoreNodeOperand` after flow
|
||||
* returns to a caller. Flow will then proceed to the defining instruction of the operand (because
|
||||
* the `StoreNodeInstr` computed by `StoreNodeOperand.getInner()` is the `StoreNode` containing
|
||||
* the defining instruction), and then along the chain computed by `StoreNodeInstr.getInner` like
|
||||
|
||||
@@ -67,7 +67,7 @@ class DefaultEdge extends EdgeKind, TDefaultEdge {
|
||||
|
||||
/**
|
||||
* A "case" edge, representing the successor of a `Switch` instruction when the
|
||||
* the condition value matches a correponding `case` label.
|
||||
* the condition value matches a corresponding `case` label.
|
||||
*/
|
||||
class CaseEdge extends EdgeKind, TCaseEdge {
|
||||
string minValue;
|
||||
|
||||
@@ -34,7 +34,7 @@ class ValueNumber extends TValueNumber {
|
||||
final Instruction getAnInstruction() { this = valueNumber(result) }
|
||||
|
||||
/**
|
||||
* Gets one of the instructions that was assigned this value number. The chosen instuction is
|
||||
* Gets one of the instructions that was assigned this value number. The chosen instruction is
|
||||
* deterministic but arbitrary. Intended for use only in debugging.
|
||||
*/
|
||||
final Instruction getExampleInstruction() {
|
||||
|
||||
@@ -1005,7 +1005,7 @@ predicate canReuseSsaForMemoryResult(Instruction instruction) {
|
||||
deprecated predicate canReuseSSAForMemoryResult = canReuseSsaForMemoryResult/1;
|
||||
|
||||
/**
|
||||
* Expose some of the internal predicates to PrintSSA.qll. We do this by publically importing those modules in the
|
||||
* Expose some of the internal predicates to PrintSSA.qll. We do this by publicly importing those modules in the
|
||||
* `DebugSSA` module, which is then imported by PrintSSA.
|
||||
*/
|
||||
module DebugSsa {
|
||||
|
||||
@@ -34,7 +34,7 @@ class ValueNumber extends TValueNumber {
|
||||
final Instruction getAnInstruction() { this = valueNumber(result) }
|
||||
|
||||
/**
|
||||
* Gets one of the instructions that was assigned this value number. The chosen instuction is
|
||||
* Gets one of the instructions that was assigned this value number. The chosen instruction is
|
||||
* deterministic but arbitrary. Intended for use only in debugging.
|
||||
*/
|
||||
final Instruction getExampleInstruction() {
|
||||
|
||||
@@ -421,20 +421,36 @@ class TranslatedCatchAnyHandler extends TranslatedHandler {
|
||||
class TranslatedIfStmt extends TranslatedStmt, ConditionContext {
|
||||
override IfStmt stmt;
|
||||
|
||||
override Instruction getFirstInstruction() { result = getCondition().getFirstInstruction() }
|
||||
override Instruction getFirstInstruction() {
|
||||
if hasInitialization()
|
||||
then result = getInitialization().getFirstInstruction()
|
||||
else result = getFirstConditionInstruction()
|
||||
}
|
||||
|
||||
override TranslatedElement getChild(int id) {
|
||||
id = 0 and result = getCondition()
|
||||
id = 0 and result = getInitialization()
|
||||
or
|
||||
id = 1 and result = getThen()
|
||||
id = 1 and result = getCondition()
|
||||
or
|
||||
id = 2 and result = getElse()
|
||||
id = 2 and result = getThen()
|
||||
or
|
||||
id = 3 and result = getElse()
|
||||
}
|
||||
|
||||
private predicate hasInitialization() { exists(stmt.getInitialization()) }
|
||||
|
||||
private TranslatedStmt getInitialization() {
|
||||
result = getTranslatedStmt(stmt.getInitialization())
|
||||
}
|
||||
|
||||
private TranslatedCondition getCondition() {
|
||||
result = getTranslatedCondition(stmt.getCondition().getFullyConverted())
|
||||
}
|
||||
|
||||
private Instruction getFirstConditionInstruction() {
|
||||
result = getCondition().getFirstInstruction()
|
||||
}
|
||||
|
||||
private TranslatedStmt getThen() { result = getTranslatedStmt(stmt.getThen()) }
|
||||
|
||||
private TranslatedStmt getElse() { result = getTranslatedStmt(stmt.getElse()) }
|
||||
@@ -456,6 +472,9 @@ class TranslatedIfStmt extends TranslatedStmt, ConditionContext {
|
||||
}
|
||||
|
||||
override Instruction getChildSuccessor(TranslatedElement child) {
|
||||
child = getInitialization() and
|
||||
result = getFirstConditionInstruction()
|
||||
or
|
||||
(child = getThen() or child = getElse()) and
|
||||
result = getParent().getChildSuccessor(this)
|
||||
}
|
||||
@@ -698,14 +717,28 @@ class TranslatedSwitchStmt extends TranslatedStmt {
|
||||
result = getTranslatedExpr(stmt.getExpr().getFullyConverted())
|
||||
}
|
||||
|
||||
private Instruction getFirstExprInstruction() { result = getExpr().getFirstInstruction() }
|
||||
|
||||
private TranslatedStmt getBody() { result = getTranslatedStmt(stmt.getStmt()) }
|
||||
|
||||
override Instruction getFirstInstruction() { result = getExpr().getFirstInstruction() }
|
||||
override Instruction getFirstInstruction() {
|
||||
if hasInitialization()
|
||||
then result = getInitialization().getFirstInstruction()
|
||||
else result = getFirstExprInstruction()
|
||||
}
|
||||
|
||||
override TranslatedElement getChild(int id) {
|
||||
id = 0 and result = getExpr()
|
||||
id = 0 and result = getInitialization()
|
||||
or
|
||||
id = 1 and result = getBody()
|
||||
id = 1 and result = getExpr()
|
||||
or
|
||||
id = 2 and result = getBody()
|
||||
}
|
||||
|
||||
private predicate hasInitialization() { exists(stmt.getInitialization()) }
|
||||
|
||||
private TranslatedStmt getInitialization() {
|
||||
result = getTranslatedStmt(stmt.getInitialization())
|
||||
}
|
||||
|
||||
override predicate hasInstruction(Opcode opcode, InstructionTag tag, CppType resultType) {
|
||||
@@ -735,6 +768,8 @@ class TranslatedSwitchStmt extends TranslatedStmt {
|
||||
}
|
||||
|
||||
override Instruction getChildSuccessor(TranslatedElement child) {
|
||||
child = getInitialization() and result = getFirstExprInstruction()
|
||||
or
|
||||
child = getExpr() and result = getInstruction(SwitchBranchTag())
|
||||
or
|
||||
child = getBody() and result = getParent().getChildSuccessor(this)
|
||||
|
||||
@@ -34,7 +34,7 @@ class ValueNumber extends TValueNumber {
|
||||
final Instruction getAnInstruction() { this = valueNumber(result) }
|
||||
|
||||
/**
|
||||
* Gets one of the instructions that was assigned this value number. The chosen instuction is
|
||||
* Gets one of the instructions that was assigned this value number. The chosen instruction is
|
||||
* deterministic but arbitrary. Intended for use only in debugging.
|
||||
*/
|
||||
final Instruction getExampleInstruction() {
|
||||
|
||||
@@ -1005,7 +1005,7 @@ predicate canReuseSsaForMemoryResult(Instruction instruction) {
|
||||
deprecated predicate canReuseSSAForMemoryResult = canReuseSsaForMemoryResult/1;
|
||||
|
||||
/**
|
||||
* Expose some of the internal predicates to PrintSSA.qll. We do this by publically importing those modules in the
|
||||
* Expose some of the internal predicates to PrintSSA.qll. We do this by publicly importing those modules in the
|
||||
* `DebugSSA` module, which is then imported by PrintSSA.
|
||||
*/
|
||||
module DebugSsa {
|
||||
|
||||
@@ -155,7 +155,7 @@ class StrCopyBW extends BufferWriteCall {
|
||||
// when result exists, it is an exact flow analysis
|
||||
reason instanceof ValueFlowAnalysis and
|
||||
result =
|
||||
this.getArgument(this.getParamSrc()).(AnalysedString).getMaxLength() * this.getCharSize()
|
||||
this.getArgument(this.getParamSrc()).(AnalyzedString).getMaxLength() * this.getCharSize()
|
||||
}
|
||||
|
||||
override int getMaxData(BufferWriteEstimationReason reason) {
|
||||
@@ -201,7 +201,7 @@ class StrCatBW extends BufferWriteCall {
|
||||
// when result exists, it is an exact flow analysis
|
||||
reason instanceof ValueFlowAnalysis and
|
||||
result =
|
||||
this.getArgument(this.getParamSrc()).(AnalysedString).getMaxLength() * this.getCharSize()
|
||||
this.getArgument(this.getParamSrc()).(AnalyzedString).getMaxLength() * this.getCharSize()
|
||||
}
|
||||
|
||||
override int getMaxData(BufferWriteEstimationReason reason) {
|
||||
|
||||
@@ -59,7 +59,7 @@ predicate isInsecureEncryption(string name) { name.regexpMatch(getInsecureAlgori
|
||||
/**
|
||||
* Holds if there is additional evidence that `name` looks like it might be
|
||||
* related to operations with an encyption algorithm, besides the name of a
|
||||
* specific algorithm. This can be used in conjuction with
|
||||
* specific algorithm. This can be used in conjunction with
|
||||
* `isInsecureEncryption` to produce a stronger heuristic.
|
||||
*/
|
||||
bindingset[name]
|
||||
|
||||
@@ -5,8 +5,6 @@
|
||||
|
||||
import cpp
|
||||
import semmle.code.cpp.controlflow.Dominance
|
||||
// `GlobalValueNumbering` is only imported to prevent IR re-evaluation.
|
||||
private import semmle.code.cpp.valuenumbering.GlobalValueNumbering
|
||||
import semmle.code.cpp.rangeanalysis.SimpleRangeAnalysis
|
||||
import semmle.code.cpp.rangeanalysis.RangeAnalysisUtils
|
||||
import semmle.code.cpp.controlflow.Guards
|
||||
|
||||
@@ -213,6 +213,26 @@ class ConditionalStmt extends ControlStructure, TConditionalStmt { }
|
||||
class IfStmt extends ConditionalStmt, @stmt_if {
|
||||
override string getAPrimaryQlClass() { result = "IfStmt" }
|
||||
|
||||
/**
|
||||
* Gets the initialization statement of this 'if' statement, if any.
|
||||
*
|
||||
* For example, for
|
||||
* ```
|
||||
* if (int x = y; b) { f(); }
|
||||
* ```
|
||||
* the result is `int x = y;`.
|
||||
*
|
||||
* Does not hold if the initialization statement is missing or an empty statement, as in
|
||||
* ```
|
||||
* if (b) { f(); }
|
||||
* ```
|
||||
* or
|
||||
* ```
|
||||
* if (; b) { f(); }
|
||||
* ```
|
||||
*/
|
||||
Stmt getInitialization() { if_initialization(underlyingElement(this), unresolveElement(result)) }
|
||||
|
||||
/**
|
||||
* Gets the condition expression of this 'if' statement.
|
||||
*
|
||||
@@ -222,7 +242,7 @@ class IfStmt extends ConditionalStmt, @stmt_if {
|
||||
* ```
|
||||
* the result is `b`.
|
||||
*/
|
||||
Expr getCondition() { result = this.getChild(0) }
|
||||
Expr getCondition() { result = this.getChild(1) }
|
||||
|
||||
override Expr getControllingExpr() { result = this.getCondition() }
|
||||
|
||||
@@ -299,6 +319,28 @@ class IfStmt extends ConditionalStmt, @stmt_if {
|
||||
class ConstexprIfStmt extends ConditionalStmt, @stmt_constexpr_if {
|
||||
override string getAPrimaryQlClass() { result = "ConstexprIfStmt" }
|
||||
|
||||
/**
|
||||
* Gets the initialization statement of this 'constexpr if' statement, if any.
|
||||
*
|
||||
* For example, for
|
||||
* ```
|
||||
* if constexpr (int x = y; b) { f(); }
|
||||
* ```
|
||||
* the result is `int x = y;`.
|
||||
*
|
||||
* Does not hold if the initialization statement is missing or an empty statement, as in
|
||||
* ```
|
||||
* if constexpr (b) { f(); }
|
||||
* ```
|
||||
* or
|
||||
* ```
|
||||
* if constexpr (; b) { f(); }
|
||||
* ```
|
||||
*/
|
||||
Stmt getInitialization() {
|
||||
constexpr_if_initialization(underlyingElement(this), unresolveElement(result))
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the condition expression of this 'constexpr if' statement.
|
||||
*
|
||||
@@ -308,7 +350,7 @@ class ConstexprIfStmt extends ConditionalStmt, @stmt_constexpr_if {
|
||||
* ```
|
||||
* the result is `b`.
|
||||
*/
|
||||
Expr getCondition() { result = this.getChild(0) }
|
||||
Expr getCondition() { result = this.getChild(1) }
|
||||
|
||||
override Expr getControllingExpr() { result = this.getCondition() }
|
||||
|
||||
@@ -926,7 +968,7 @@ class ForStmt extends Loop, @stmt_for {
|
||||
*
|
||||
* Does not hold if the initialization statement is an empty statement, as in
|
||||
* ```
|
||||
* for (; i < 10; i++) { j++ }
|
||||
* for (; i < 10; i++) { j++; }
|
||||
* ```
|
||||
*/
|
||||
Stmt getInitialization() { for_initialization(underlyingElement(this), unresolveElement(result)) }
|
||||
@@ -1470,6 +1512,28 @@ class DefaultCase extends SwitchCase {
|
||||
class SwitchStmt extends ConditionalStmt, @stmt_switch {
|
||||
override string getAPrimaryQlClass() { result = "SwitchStmt" }
|
||||
|
||||
/**
|
||||
* Gets the initialization statement of this 'switch' statement, if any.
|
||||
*
|
||||
* For example, for
|
||||
* ```
|
||||
* switch (int x = y; b) { }
|
||||
* ```
|
||||
* the result is `int x = y;`.
|
||||
*
|
||||
* Does not hold if the initialization statement is missing or an empty statement, as in
|
||||
* ```
|
||||
* switch (b) { }
|
||||
* ```
|
||||
* or
|
||||
* ```
|
||||
* switch (; b) { }
|
||||
* ```
|
||||
*/
|
||||
Stmt getInitialization() {
|
||||
switch_initialization(underlyingElement(this), unresolveElement(result))
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the expression that this 'switch' statement switches on.
|
||||
*
|
||||
@@ -1485,7 +1549,7 @@ class SwitchStmt extends ConditionalStmt, @stmt_switch {
|
||||
* ```
|
||||
* the result is `i`.
|
||||
*/
|
||||
Expr getExpr() { result = this.getChild(0) }
|
||||
Expr getExpr() { result = this.getChild(1) }
|
||||
|
||||
override Expr getControllingExpr() { result = this.getExpr() }
|
||||
|
||||
|
||||
@@ -1863,6 +1863,11 @@ variable_vla(
|
||||
int decl: @stmt_vla_decl ref
|
||||
);
|
||||
|
||||
if_initialization(
|
||||
unique int if_stmt: @stmt_if ref,
|
||||
int init_id: @stmt ref
|
||||
);
|
||||
|
||||
if_then(
|
||||
unique int if_stmt: @stmt_if ref,
|
||||
int then_id: @stmt ref
|
||||
@@ -1873,6 +1878,11 @@ if_else(
|
||||
int else_id: @stmt ref
|
||||
);
|
||||
|
||||
constexpr_if_initialization(
|
||||
unique int constexpr_if_stmt: @stmt_constexpr_if ref,
|
||||
int init_id: @stmt ref
|
||||
);
|
||||
|
||||
constexpr_if_then(
|
||||
unique int constexpr_if_stmt: @stmt_constexpr_if ref,
|
||||
int then_id: @stmt ref
|
||||
@@ -1893,6 +1903,11 @@ do_body(
|
||||
int body_id: @stmt ref
|
||||
);
|
||||
|
||||
switch_initialization(
|
||||
unique int switch_stmt: @stmt_switch ref,
|
||||
int init_id: @stmt ref
|
||||
);
|
||||
|
||||
#keyset[switch_stmt, index]
|
||||
switch_case(
|
||||
int switch_stmt: @stmt_switch ref,
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,21 @@
|
||||
class Element extends @element {
|
||||
string toString() { none() }
|
||||
}
|
||||
|
||||
class Expr extends @expr {
|
||||
string toString() { none() }
|
||||
}
|
||||
|
||||
class Stmt extends @stmt {
|
||||
string toString() { none() }
|
||||
}
|
||||
|
||||
predicate isStmtWithInitializer(Stmt stmt) {
|
||||
exists(int kind | stmts(stmt, kind, _) | kind = 2 or kind = 11 or kind = 35)
|
||||
}
|
||||
|
||||
from Expr child, int index, int index_new, Element parent
|
||||
where
|
||||
exprparents(child, index, parent) and
|
||||
if isStmtWithInitializer(parent) then index_new = index + 1 else index_new = index
|
||||
select child, index_new, parent
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,17 @@
|
||||
class Element extends @element {
|
||||
string toString() { none() }
|
||||
}
|
||||
|
||||
class Stmt extends @stmt {
|
||||
string toString() { none() }
|
||||
}
|
||||
|
||||
predicate isStmtWithInitializer(Stmt stmt) {
|
||||
exists(int kind | stmts(stmt, kind, _) | kind = 2 or kind = 11 or kind = 35)
|
||||
}
|
||||
|
||||
from Stmt child, int index, int index_new, Element parent
|
||||
where
|
||||
stmtparents(child, index, parent) and
|
||||
if isStmtWithInitializer(parent) then index_new = index + 1 else index_new = index
|
||||
select child, index_new, parent
|
||||
@@ -0,0 +1,4 @@
|
||||
description: Support C++17 if and switch initializers
|
||||
compatibility: partial
|
||||
exprparents.rel: run exprparents.qlo
|
||||
stmtparents.rel: run stmtparents.qlo
|
||||
@@ -57,6 +57,5 @@ where
|
||||
not declarationHasSideEffects(v) and
|
||||
not exists(AsmStmt s | f = s.getEnclosingFunction()) and
|
||||
not v.getAnAttribute().getName() = "unused" and
|
||||
not any(ErrorExpr e).getEnclosingFunction() = f and // unextracted expr may use `v`
|
||||
not any(ConditionDeclExpr cde).getEnclosingFunction() = f // this case can be removed when the `if (a = b; a)` and `switch (a = b; a)` test cases don't depend on this exclusion
|
||||
not any(ErrorExpr e).getEnclosingFunction() = f // unextracted expr may use `v`
|
||||
select v, "Variable " + v.getName() + " is not used"
|
||||
|
||||
@@ -1,3 +1,15 @@
|
||||
## 0.1.2
|
||||
|
||||
### Minor Analysis Improvements
|
||||
|
||||
* The "XML external entity expansion" (`cpp/external-entity-expansion`) query has been extended to support a broader selection of XML libraries and interfaces.
|
||||
|
||||
## 0.1.1
|
||||
|
||||
### New Queries
|
||||
|
||||
* An new query `cpp/external-entity-expansion` has been added. The query detects XML objects that are vulnerable to external entity expansion (XXE) attacks.
|
||||
|
||||
## 0.1.0
|
||||
|
||||
### Minor Analysis Improvements
|
||||
|
||||
@@ -57,10 +57,10 @@ class ExtractionProblem extends TExtractionProblem {
|
||||
/** Gets the problem message for this problem. */
|
||||
string getProblemMessage() { none() }
|
||||
|
||||
/** Gets the file this problem occured in. */
|
||||
/** Gets the file this problem occurred in. */
|
||||
File getFile() { none() }
|
||||
|
||||
/** Gets the location this problem occured in. */
|
||||
/** Gets the location this problem occurred in. */
|
||||
Location getLocation() { none() }
|
||||
|
||||
/** Gets the SARIF severity of this problem. */
|
||||
|
||||
@@ -57,10 +57,10 @@ class ExtractionError extends TExtractionError {
|
||||
/** Gets the error message for this error. */
|
||||
string getErrorMessage() { none() }
|
||||
|
||||
/** Gets the file this error occured in. */
|
||||
/** Gets the file this error occurred in. */
|
||||
File getFile() { none() }
|
||||
|
||||
/** Gets the location this error occured in. */
|
||||
/** Gets the location this error occurred in. */
|
||||
Location getLocation() { none() }
|
||||
|
||||
/** Gets the SARIF severity of this error. */
|
||||
|
||||
@@ -19,7 +19,7 @@ predicate whitelist(Function f) {
|
||||
"nearbyintl", "rint", "rintf", "rintl", "round", "roundf", "roundl", "trunc", "truncf",
|
||||
"truncl"
|
||||
] or
|
||||
f.getName().matches("__builtin_%")
|
||||
f.getName().matches("\\_\\_builtin\\_%")
|
||||
}
|
||||
|
||||
predicate whitelistPow(FunctionCall fc) {
|
||||
|
||||
@@ -14,9 +14,6 @@
|
||||
*/
|
||||
|
||||
import cpp
|
||||
// We don't actually use the global value numbering library in this query, but without it we end up
|
||||
// recomputing the IR.
|
||||
private import semmle.code.cpp.valuenumbering.GlobalValueNumbering
|
||||
import semmle.code.cpp.ir.IR
|
||||
import semmle.code.cpp.ir.dataflow.MustFlow
|
||||
import PathGraph
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
* @name Suspicious call to memset
|
||||
* @description Use of memset where the size argument is computed as the size of
|
||||
* some non-struct type. When initializing a buffer, you should specify
|
||||
* its size as <number of elements> * <size of one element> to ensure
|
||||
* its size as `<number of elements> * <size of one element>` to ensure
|
||||
* portability.
|
||||
* @kind problem
|
||||
* @id cpp/suspicious-call-to-memset
|
||||
|
||||
@@ -15,9 +15,6 @@
|
||||
*/
|
||||
|
||||
import cpp
|
||||
// We don't actually use the global value numbering library in this query, but without it we end up
|
||||
// recomputing the IR.
|
||||
import semmle.code.cpp.valuenumbering.GlobalValueNumbering
|
||||
import semmle.code.cpp.ir.IR
|
||||
import semmle.code.cpp.ir.dataflow.MustFlow
|
||||
import PathGraph
|
||||
|
||||
78
cpp/ql/src/Security/CWE/CWE-611/Libxml2.qll
Normal file
78
cpp/ql/src/Security/CWE/CWE-611/Libxml2.qll
Normal file
@@ -0,0 +1,78 @@
|
||||
/**
|
||||
* Models the libxml2 XML library.
|
||||
*/
|
||||
|
||||
import cpp
|
||||
import XML
|
||||
import semmle.code.cpp.valuenumbering.GlobalValueNumbering
|
||||
|
||||
/**
|
||||
* A call to a `libxml2` function that parses XML.
|
||||
*/
|
||||
class Libxml2ParseCall extends FunctionCall {
|
||||
int optionsArg;
|
||||
|
||||
Libxml2ParseCall() {
|
||||
exists(string fname | this.getTarget().getName() = fname |
|
||||
fname = "xmlCtxtUseOptions" and optionsArg = 1
|
||||
or
|
||||
fname = "xmlReadFile" and optionsArg = 2
|
||||
or
|
||||
fname = ["xmlCtxtReadFile", "xmlParseInNodeContext", "xmlReadDoc", "xmlReadFd"] and
|
||||
optionsArg = 3
|
||||
or
|
||||
fname = ["xmlCtxtReadDoc", "xmlCtxtReadFd", "xmlReadMemory"] and optionsArg = 4
|
||||
or
|
||||
fname = ["xmlCtxtReadMemory", "xmlReadIO"] and optionsArg = 5
|
||||
or
|
||||
fname = "xmlCtxtReadIO" and optionsArg = 6
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the argument that specifies `xmlParserOption`s.
|
||||
*/
|
||||
Expr getOptions() { result = this.getArgument(optionsArg) }
|
||||
}
|
||||
|
||||
/**
|
||||
* An `xmlParserOption` for `libxml2` that is considered unsafe.
|
||||
*/
|
||||
class Libxml2BadOption extends EnumConstant {
|
||||
Libxml2BadOption() { this.getName() = ["XML_PARSE_NOENT", "XML_PARSE_DTDLOAD"] }
|
||||
}
|
||||
|
||||
/**
|
||||
* The libxml2 XML library.
|
||||
*/
|
||||
class LibXml2Library extends XmlLibrary {
|
||||
LibXml2Library() { this = "LibXml2Library" }
|
||||
|
||||
override predicate configurationSource(DataFlow::Node node, string flowstate) {
|
||||
// source is an `options` argument on a libxml2 parse call that specifies
|
||||
// at least one unsafe option.
|
||||
//
|
||||
// note: we don't need to track an XML object for libxml2, so we don't
|
||||
// really need data flow. Nevertheless we jam it into this configuration,
|
||||
// with matching sources and sinks. This allows results to be presented by
|
||||
// the same query, in a consistent way as other results with flow paths.
|
||||
exists(Libxml2ParseCall call, Expr options |
|
||||
options = call.getOptions() and
|
||||
node.asExpr() = options and
|
||||
flowstate = "libxml2" and
|
||||
exists(Libxml2BadOption opt |
|
||||
globalValueNumber(options).getAnExpr().getValue().toInt().bitAnd(opt.getValue().toInt()) !=
|
||||
0
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
override predicate configurationSink(DataFlow::Node node, string flowstate) {
|
||||
// sink is the `options` argument on a `libxml2` parse call.
|
||||
exists(Libxml2ParseCall call, Expr options |
|
||||
options = call.getOptions() and
|
||||
node.asExpr() = options and
|
||||
flowstate = "libxml2"
|
||||
)
|
||||
}
|
||||
}
|
||||
55
cpp/ql/src/Security/CWE/CWE-611/XML.qll
Normal file
55
cpp/ql/src/Security/CWE/CWE-611/XML.qll
Normal file
@@ -0,0 +1,55 @@
|
||||
/**
|
||||
* Provides a abstract classes for modeling XML libraries. This design is
|
||||
* currently specialized for the purposes of the XXE query.
|
||||
*/
|
||||
|
||||
import cpp
|
||||
import semmle.code.cpp.ir.dataflow.DataFlow
|
||||
import Xerces
|
||||
import Libxml2
|
||||
|
||||
/**
|
||||
* A flow state representing a possible configuration of an XML object.
|
||||
*/
|
||||
abstract class XxeFlowState extends DataFlow::FlowState {
|
||||
bindingset[this]
|
||||
XxeFlowState() { any() } // required characteristic predicate
|
||||
}
|
||||
|
||||
/**
|
||||
* An XML library or interface.
|
||||
*/
|
||||
abstract class XmlLibrary extends string {
|
||||
bindingset[this]
|
||||
XmlLibrary() { any() } // required characteristic predicate
|
||||
|
||||
/**
|
||||
* Holds if `node` is the source node for a potentially unsafe configuration
|
||||
* object for this XML library, along with `flowstate` representing its
|
||||
* initial state.
|
||||
*/
|
||||
abstract predicate configurationSource(DataFlow::Node node, string flowstate);
|
||||
|
||||
/**
|
||||
* Holds if `node` is the sink node where an unsafe configuration object is
|
||||
* used to interpret XML.
|
||||
*/
|
||||
abstract predicate configurationSink(DataFlow::Node node, string flowstate);
|
||||
}
|
||||
|
||||
/**
|
||||
* An `Expr` that changes the configuration of an XML object, transforming the
|
||||
* `XxeFlowState` that flows through it.
|
||||
*/
|
||||
abstract class XxeFlowStateTransformer extends Expr {
|
||||
/**
|
||||
* Gets the flow state that `flowstate` is transformed into.
|
||||
*
|
||||
* Due to limitations of the implementation the transformation defined by this
|
||||
* predicate must be idempotent, that is, for any input `x` it must be that:
|
||||
* ```
|
||||
* transform(transform(x)) = transform(x)
|
||||
* ```
|
||||
*/
|
||||
abstract XxeFlowState transform(XxeFlowState flowstate);
|
||||
}
|
||||
@@ -7,169 +7,14 @@
|
||||
* @id cpp/external-entity-expansion
|
||||
* @problem.severity warning
|
||||
* @security-severity 9.1
|
||||
* @precision medium
|
||||
* @precision high
|
||||
* @tags security
|
||||
* external/cwe/cwe-611
|
||||
*/
|
||||
|
||||
import cpp
|
||||
import semmle.code.cpp.ir.dataflow.DataFlow
|
||||
import XML
|
||||
import DataFlow::PathGraph
|
||||
import semmle.code.cpp.ir.IR
|
||||
|
||||
/**
|
||||
* A flow state representing a possible configuration of an XML object.
|
||||
*/
|
||||
abstract class XXEFlowState extends DataFlow::FlowState {
|
||||
bindingset[this]
|
||||
XXEFlowState() { any() } // required characteristic predicate
|
||||
}
|
||||
|
||||
/**
|
||||
* An `Expr` that changes the configuration of an XML object, transforming the
|
||||
* `XXEFlowState` that flows through it.
|
||||
*/
|
||||
abstract class XXEFlowStateTranformer extends Expr {
|
||||
/**
|
||||
* Gets the flow state that `flowstate` is transformed into.
|
||||
*
|
||||
* Due to limitations of the implementation the transformation defined by this
|
||||
* predicate must be idempotent, that is, for any input `x` it must be that:
|
||||
* ```
|
||||
* transform(tranform(x)) = tranform(x)
|
||||
* ```
|
||||
*/
|
||||
abstract XXEFlowState transform(XXEFlowState flowstate);
|
||||
}
|
||||
|
||||
/**
|
||||
* The `AbstractDOMParser` class.
|
||||
*/
|
||||
class AbstractDOMParserClass extends Class {
|
||||
AbstractDOMParserClass() { this.hasName("AbstractDOMParser") }
|
||||
}
|
||||
|
||||
/**
|
||||
* The `XercesDOMParser` class.
|
||||
*/
|
||||
class XercesDOMParserClass extends Class {
|
||||
XercesDOMParserClass() { this.hasName("XercesDOMParser") }
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a valid flow state for `XercesDOMParser` flow.
|
||||
*
|
||||
* These flow states take the form `XercesDOM-A-B`, where:
|
||||
* - A is 1 if `setDisableDefaultEntityResolution` is `true`, 0 otherwise.
|
||||
* - B is 1 if `setCreateEntityReferenceNodes` is `true`, 0 otherwise.
|
||||
*/
|
||||
predicate encodeXercesDOMFlowState(
|
||||
string flowstate, int disabledDefaultEntityResolution, int createEntityReferenceNodes
|
||||
) {
|
||||
flowstate = "XercesDOM-0-0" and
|
||||
disabledDefaultEntityResolution = 0 and
|
||||
createEntityReferenceNodes = 0
|
||||
or
|
||||
flowstate = "XercesDOM-0-1" and
|
||||
disabledDefaultEntityResolution = 0 and
|
||||
createEntityReferenceNodes = 1
|
||||
or
|
||||
flowstate = "XercesDOM-1-0" and
|
||||
disabledDefaultEntityResolution = 1 and
|
||||
createEntityReferenceNodes = 0
|
||||
or
|
||||
flowstate = "XercesDOM-1-1" and
|
||||
disabledDefaultEntityResolution = 1 and
|
||||
createEntityReferenceNodes = 1
|
||||
}
|
||||
|
||||
/**
|
||||
* A flow state representing the configuration of a `XercesDOMParser` object.
|
||||
*/
|
||||
class XercesDOMParserFlowState extends XXEFlowState {
|
||||
XercesDOMParserFlowState() { encodeXercesDOMFlowState(this, _, _) }
|
||||
}
|
||||
|
||||
/**
|
||||
* A flow state transformer for a call to
|
||||
* `AbstractDOMParser.setDisableDefaultEntityResolution`. Transforms the flow
|
||||
* state through the qualifier according to the setting in the parameter.
|
||||
*/
|
||||
class DisableDefaultEntityResolutionTranformer extends XXEFlowStateTranformer {
|
||||
Expr newValue;
|
||||
|
||||
DisableDefaultEntityResolutionTranformer() {
|
||||
exists(Call call, Function f |
|
||||
call.getTarget() = f and
|
||||
f.getDeclaringType() instanceof AbstractDOMParserClass and
|
||||
f.hasName("setDisableDefaultEntityResolution") and
|
||||
this = call.getQualifier() and
|
||||
newValue = call.getArgument(0)
|
||||
)
|
||||
}
|
||||
|
||||
final override XXEFlowState transform(XXEFlowState flowstate) {
|
||||
exists(int createEntityReferenceNodes |
|
||||
encodeXercesDOMFlowState(flowstate, _, createEntityReferenceNodes) and
|
||||
(
|
||||
newValue.getValue().toInt() = 1 and // true
|
||||
encodeXercesDOMFlowState(result, 1, createEntityReferenceNodes)
|
||||
or
|
||||
not newValue.getValue().toInt() = 1 and // false or unknown
|
||||
encodeXercesDOMFlowState(result, 0, createEntityReferenceNodes)
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A flow state transformer for a call to
|
||||
* `AbstractDOMParser.setCreateEntityReferenceNodes`. Transforms the flow
|
||||
* state through the qualifier according to the setting in the parameter.
|
||||
*/
|
||||
class CreateEntityReferenceNodesTranformer extends XXEFlowStateTranformer {
|
||||
Expr newValue;
|
||||
|
||||
CreateEntityReferenceNodesTranformer() {
|
||||
exists(Call call, Function f |
|
||||
call.getTarget() = f and
|
||||
f.getDeclaringType() instanceof AbstractDOMParserClass and
|
||||
f.hasName("setCreateEntityReferenceNodes") and
|
||||
this = call.getQualifier() and
|
||||
newValue = call.getArgument(0)
|
||||
)
|
||||
}
|
||||
|
||||
final override XXEFlowState transform(XXEFlowState flowstate) {
|
||||
exists(int disabledDefaultEntityResolution |
|
||||
encodeXercesDOMFlowState(flowstate, disabledDefaultEntityResolution, _) and
|
||||
(
|
||||
newValue.getValue().toInt() = 1 and // true
|
||||
encodeXercesDOMFlowState(result, disabledDefaultEntityResolution, 1)
|
||||
or
|
||||
not newValue.getValue().toInt() = 1 and // false or unknown
|
||||
encodeXercesDOMFlowState(result, disabledDefaultEntityResolution, 0)
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The `AbstractDOMParser.parse` method.
|
||||
*/
|
||||
class ParseFunction extends Function {
|
||||
ParseFunction() { this.getClassAndName("parse") instanceof AbstractDOMParserClass }
|
||||
}
|
||||
|
||||
/**
|
||||
* The `createLSParser` function that returns a newly created `LSParser` object.
|
||||
*/
|
||||
class CreateLSParser extends Function {
|
||||
CreateLSParser() {
|
||||
this.hasName("createLSParser") and
|
||||
this.getUnspecifiedType().(PointerType).getBaseType().getName() = "DOMLSParser" // returns a `DOMLSParser *`.
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A configuration for tracking XML objects and their states.
|
||||
@@ -178,45 +23,25 @@ class XXEConfiguration extends DataFlow::Configuration {
|
||||
XXEConfiguration() { this = "XXEConfiguration" }
|
||||
|
||||
override predicate isSource(DataFlow::Node node, string flowstate) {
|
||||
// source is the write on `this` of a call to the `XercesDOMParser`
|
||||
// constructor.
|
||||
exists(CallInstruction call |
|
||||
call.getStaticCallTarget() = any(XercesDOMParserClass c).getAConstructor() and
|
||||
node.asInstruction().(WriteSideEffectInstruction).getDestinationAddress() =
|
||||
call.getThisArgument() and
|
||||
encodeXercesDOMFlowState(flowstate, 0, 1) // default configuration
|
||||
)
|
||||
or
|
||||
// source is the result of a call to `createLSParser`.
|
||||
exists(Call call |
|
||||
call.getTarget() instanceof CreateLSParser and
|
||||
call = node.asExpr() and
|
||||
encodeXercesDOMFlowState(flowstate, 0, 1) // default configuration
|
||||
)
|
||||
any(XmlLibrary l).configurationSource(node, flowstate)
|
||||
}
|
||||
|
||||
override predicate isSink(DataFlow::Node node, string flowstate) {
|
||||
// sink is the read of the qualifier of a call to `parse`.
|
||||
exists(Call call |
|
||||
call.getTarget() instanceof ParseFunction and
|
||||
call.getQualifier() = node.asConvertedExpr()
|
||||
) and
|
||||
flowstate instanceof XercesDOMParserFlowState and
|
||||
not encodeXercesDOMFlowState(flowstate, 1, 1) // safe configuration
|
||||
any(XmlLibrary l).configurationSink(node, flowstate)
|
||||
}
|
||||
|
||||
override predicate isAdditionalFlowStep(
|
||||
DataFlow::Node node1, string state1, DataFlow::Node node2, string state2
|
||||
) {
|
||||
// create additional flow steps for `XXEFlowStateTranformer`s
|
||||
state2 = node2.asConvertedExpr().(XXEFlowStateTranformer).transform(state1) and
|
||||
// create additional flow steps for `XxeFlowStateTransformer`s
|
||||
state2 = node2.asConvertedExpr().(XxeFlowStateTransformer).transform(state1) and
|
||||
DataFlow::simpleLocalFlowStep(node1, node2)
|
||||
}
|
||||
|
||||
override predicate isBarrier(DataFlow::Node node, string flowstate) {
|
||||
// when the flowstate is transformed at a call node, block the original
|
||||
// flowstate value.
|
||||
node.asConvertedExpr().(XXEFlowStateTranformer).transform(flowstate) != flowstate
|
||||
node.asConvertedExpr().(XxeFlowStateTransformer).transform(flowstate) != flowstate
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
376
cpp/ql/src/Security/CWE/CWE-611/Xerces.qll
Normal file
376
cpp/ql/src/Security/CWE/CWE-611/Xerces.qll
Normal file
@@ -0,0 +1,376 @@
|
||||
/**
|
||||
* Models the Xerces XML library.
|
||||
*/
|
||||
|
||||
import cpp
|
||||
import XML
|
||||
import semmle.code.cpp.valuenumbering.GlobalValueNumbering
|
||||
import semmle.code.cpp.ir.IR
|
||||
|
||||
/**
|
||||
* Gets a valid flow state for `AbstractDOMParser` or `SAXParser` flow.
|
||||
*
|
||||
* These flow states take the form `Xerces-A-B`, where:
|
||||
* - A is 1 if `setDisableDefaultEntityResolution` is `true`, 0 otherwise.
|
||||
* - B is 1 if `setCreateEntityReferenceNodes` is `true`, 0 otherwise.
|
||||
*/
|
||||
predicate encodeXercesFlowState(
|
||||
string flowstate, int disabledDefaultEntityResolution, int createEntityReferenceNodes
|
||||
) {
|
||||
flowstate = "Xerces-0-0" and
|
||||
disabledDefaultEntityResolution = 0 and
|
||||
createEntityReferenceNodes = 0
|
||||
or
|
||||
flowstate = "Xerces-0-1" and
|
||||
disabledDefaultEntityResolution = 0 and
|
||||
createEntityReferenceNodes = 1
|
||||
or
|
||||
flowstate = "Xerces-1-0" and
|
||||
disabledDefaultEntityResolution = 1 and
|
||||
createEntityReferenceNodes = 0
|
||||
or
|
||||
flowstate = "Xerces-1-1" and
|
||||
disabledDefaultEntityResolution = 1 and
|
||||
createEntityReferenceNodes = 1
|
||||
}
|
||||
|
||||
/**
|
||||
* A flow state representing the configuration of an `AbstractDOMParser` or
|
||||
* `SAXParser` object.
|
||||
*/
|
||||
class XercesFlowState extends XxeFlowState {
|
||||
XercesFlowState() { encodeXercesFlowState(this, _, _) }
|
||||
}
|
||||
|
||||
/**
|
||||
* The `AbstractDOMParser` class.
|
||||
*/
|
||||
class AbstractDomParserClass extends Class {
|
||||
AbstractDomParserClass() { this.hasName("AbstractDOMParser") }
|
||||
}
|
||||
|
||||
/**
|
||||
* The `XercesDOMParser` class.
|
||||
*/
|
||||
class XercesDomParserClass extends Class {
|
||||
XercesDomParserClass() { this.hasName("XercesDOMParser") }
|
||||
}
|
||||
|
||||
/**
|
||||
* The `XercesDOMParser` interface for the Xerces XML library.
|
||||
*/
|
||||
class XercesDomParserLibrary extends XmlLibrary {
|
||||
XercesDomParserLibrary() { this = "XercesDomParserLibrary" }
|
||||
|
||||
override predicate configurationSource(DataFlow::Node node, string flowstate) {
|
||||
// source is the write on `this` of a call to the `XercesDOMParser`
|
||||
// constructor.
|
||||
exists(CallInstruction call |
|
||||
call.getStaticCallTarget() = any(XercesDomParserClass c).getAConstructor() and
|
||||
node.asInstruction().(WriteSideEffectInstruction).getDestinationAddress() =
|
||||
call.getThisArgument() and
|
||||
encodeXercesFlowState(flowstate, 0, 1) // default configuration
|
||||
)
|
||||
}
|
||||
|
||||
override predicate configurationSink(DataFlow::Node node, string flowstate) {
|
||||
// sink is the read of the qualifier of a call to `AbstractDOMParser.parse`.
|
||||
exists(Call call |
|
||||
call.getTarget().getClassAndName("parse") instanceof AbstractDomParserClass and
|
||||
call.getQualifier() = node.asConvertedExpr()
|
||||
) and
|
||||
flowstate instanceof XercesFlowState and
|
||||
not encodeXercesFlowState(flowstate, 1, 1) // safe configuration
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The `DOMLSParser` class.
|
||||
*/
|
||||
class DomLSParserClass extends Class {
|
||||
DomLSParserClass() { this.hasName("DOMLSParser") }
|
||||
}
|
||||
|
||||
/**
|
||||
* The `createLSParser` function that returns a newly created `DOMLSParser`
|
||||
* object.
|
||||
*/
|
||||
class CreateLSParser extends Function {
|
||||
CreateLSParser() {
|
||||
this.hasName("createLSParser") and
|
||||
this.getUnspecifiedType().(PointerType).getBaseType() instanceof DomLSParserClass // returns a `DOMLSParser *`.
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The createLSParser interface for the Xerces XML library.
|
||||
*/
|
||||
class CreateLSParserLibrary extends XmlLibrary {
|
||||
CreateLSParserLibrary() { this = "CreateLSParserLibrary" }
|
||||
|
||||
override predicate configurationSource(DataFlow::Node node, string flowstate) {
|
||||
// source is the result of a call to `createLSParser`.
|
||||
exists(Call call |
|
||||
call.getTarget() instanceof CreateLSParser and
|
||||
call = node.asExpr() and
|
||||
encodeXercesFlowState(flowstate, 0, 1) // default configuration
|
||||
)
|
||||
}
|
||||
|
||||
override predicate configurationSink(DataFlow::Node node, string flowstate) {
|
||||
// sink is the read of the qualifier of a call to `DOMLSParserClass.parse`.
|
||||
exists(Call call |
|
||||
call.getTarget().getClassAndName("parse") instanceof DomLSParserClass and
|
||||
call.getQualifier() = node.asConvertedExpr()
|
||||
) and
|
||||
flowstate instanceof XercesFlowState and
|
||||
not encodeXercesFlowState(flowstate, 1, 1) // safe configuration
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The `SAXParser` class.
|
||||
*/
|
||||
class SaxParserClass extends Class {
|
||||
SaxParserClass() { this.hasName("SAXParser") }
|
||||
}
|
||||
|
||||
/**
|
||||
* The `SAX2XMLReader` class.
|
||||
*/
|
||||
class Sax2XmlReader extends Class {
|
||||
Sax2XmlReader() { this.hasName("SAX2XMLReader") }
|
||||
}
|
||||
|
||||
/**
|
||||
* The SAXParser interface for the Xerces XML library.
|
||||
*/
|
||||
class SaxParserLibrary extends XmlLibrary {
|
||||
SaxParserLibrary() { this = "SaxParserLibrary" }
|
||||
|
||||
override predicate configurationSource(DataFlow::Node node, string flowstate) {
|
||||
// source is the write on `this` of a call to the `SAXParser`
|
||||
// constructor.
|
||||
exists(CallInstruction call |
|
||||
call.getStaticCallTarget() = any(SaxParserClass c).getAConstructor() and
|
||||
node.asInstruction().(WriteSideEffectInstruction).getDestinationAddress() =
|
||||
call.getThisArgument() and
|
||||
encodeXercesFlowState(flowstate, 0, 1) // default configuration
|
||||
)
|
||||
}
|
||||
|
||||
override predicate configurationSink(DataFlow::Node node, string flowstate) {
|
||||
// sink is the read of the qualifier of a call to `SAXParser.parse`.
|
||||
exists(Call call |
|
||||
call.getTarget().getClassAndName("parse") instanceof SaxParserClass and
|
||||
call.getQualifier() = node.asConvertedExpr()
|
||||
) and
|
||||
flowstate instanceof XercesFlowState and
|
||||
not encodeXercesFlowState(flowstate, 1, 1) // safe configuration
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The `createXMLReader` function that returns a newly created `SAX2XMLReader`
|
||||
* object.
|
||||
*/
|
||||
class CreateXmlReader extends Function {
|
||||
CreateXmlReader() {
|
||||
this.hasName("createXMLReader") and
|
||||
this.getUnspecifiedType().(PointerType).getBaseType() instanceof Sax2XmlReader // returns a `SAX2XMLReader *`.
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The SAX2XMLReader interface for the Xerces XML library.
|
||||
*/
|
||||
class Sax2XmlReaderLibrary extends XmlLibrary {
|
||||
Sax2XmlReaderLibrary() { this = "Sax2XmlReaderLibrary" }
|
||||
|
||||
override predicate configurationSource(DataFlow::Node node, string flowstate) {
|
||||
// source is the result of a call to `createXMLReader`.
|
||||
exists(Call call |
|
||||
call.getTarget() instanceof CreateXmlReader and
|
||||
call = node.asExpr() and
|
||||
encodeXercesFlowState(flowstate, 0, 1) // default configuration
|
||||
)
|
||||
}
|
||||
|
||||
override predicate configurationSink(DataFlow::Node node, string flowstate) {
|
||||
// sink is the read of the qualifier of a call to `SAX2XMLReader.parse`.
|
||||
exists(Call call |
|
||||
call.getTarget().getClassAndName("parse") instanceof Sax2XmlReader and
|
||||
call.getQualifier() = node.asConvertedExpr()
|
||||
) and
|
||||
flowstate instanceof XercesFlowState and
|
||||
not encodeXercesFlowState(flowstate, 1, 1) // safe configuration
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A flow state transformer for a call to
|
||||
* `AbstractDOMParser.setDisableDefaultEntityResolution` or
|
||||
* `SAXParser.setDisableDefaultEntityResolution`. Transforms the flow
|
||||
* state through the qualifier according to the setting in the parameter.
|
||||
*/
|
||||
class DisableDefaultEntityResolutionTransformer extends XxeFlowStateTransformer {
|
||||
Expr newValue;
|
||||
|
||||
DisableDefaultEntityResolutionTransformer() {
|
||||
exists(Call call, Function f |
|
||||
call.getTarget() = f and
|
||||
(
|
||||
f.getDeclaringType() instanceof AbstractDomParserClass or
|
||||
f.getDeclaringType() instanceof SaxParserClass
|
||||
) and
|
||||
f.hasName("setDisableDefaultEntityResolution") and
|
||||
this = call.getQualifier() and
|
||||
newValue = call.getArgument(0)
|
||||
)
|
||||
}
|
||||
|
||||
final override XxeFlowState transform(XxeFlowState flowstate) {
|
||||
exists(int createEntityReferenceNodes |
|
||||
encodeXercesFlowState(flowstate, _, createEntityReferenceNodes) and
|
||||
(
|
||||
globalValueNumber(newValue).getAnExpr().getValue().toInt() = 1 and // true
|
||||
encodeXercesFlowState(result, 1, createEntityReferenceNodes)
|
||||
or
|
||||
not globalValueNumber(newValue).getAnExpr().getValue().toInt() = 1 and // false or unknown
|
||||
encodeXercesFlowState(result, 0, createEntityReferenceNodes)
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A flow state transformer for a call to
|
||||
* `AbstractDOMParser.setCreateEntityReferenceNodes`. Transforms the flow
|
||||
* state through the qualifier according to the setting in the parameter.
|
||||
*/
|
||||
class CreateEntityReferenceNodesTransformer extends XxeFlowStateTransformer {
|
||||
Expr newValue;
|
||||
|
||||
CreateEntityReferenceNodesTransformer() {
|
||||
exists(Call call, Function f |
|
||||
call.getTarget() = f and
|
||||
f.getClassAndName("setCreateEntityReferenceNodes") instanceof AbstractDomParserClass and
|
||||
this = call.getQualifier() and
|
||||
newValue = call.getArgument(0)
|
||||
)
|
||||
}
|
||||
|
||||
final override XxeFlowState transform(XxeFlowState flowstate) {
|
||||
exists(int disabledDefaultEntityResolution |
|
||||
encodeXercesFlowState(flowstate, disabledDefaultEntityResolution, _) and
|
||||
(
|
||||
globalValueNumber(newValue).getAnExpr().getValue().toInt() = 1 and // true
|
||||
encodeXercesFlowState(result, disabledDefaultEntityResolution, 1)
|
||||
or
|
||||
not globalValueNumber(newValue).getAnExpr().getValue().toInt() = 1 and // false or unknown
|
||||
encodeXercesFlowState(result, disabledDefaultEntityResolution, 0)
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The `XMLUni.fgXercesDisableDefaultEntityResolution` constant.
|
||||
*/
|
||||
class FeatureDisableDefaultEntityResolution extends Variable {
|
||||
FeatureDisableDefaultEntityResolution() {
|
||||
this.getName() = "fgXercesDisableDefaultEntityResolution" and
|
||||
this.getDeclaringType().getName() = "XMLUni"
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A flow state transformer for a call to `SAX2XMLReader.setFeature`
|
||||
* specifying the feature `XMLUni::fgXercesDisableDefaultEntityResolution`.
|
||||
* Transforms the flow state through the qualifier according to this setting.
|
||||
*/
|
||||
class SetFeatureTransformer extends XxeFlowStateTransformer {
|
||||
Expr newValue;
|
||||
|
||||
SetFeatureTransformer() {
|
||||
exists(Call call, Function f |
|
||||
call.getTarget() = f and
|
||||
f.getClassAndName("setFeature") instanceof Sax2XmlReader and
|
||||
this = call.getQualifier() and
|
||||
globalValueNumber(call.getArgument(0)).getAnExpr().(VariableAccess).getTarget() instanceof
|
||||
FeatureDisableDefaultEntityResolution and
|
||||
newValue = call.getArgument(1)
|
||||
)
|
||||
}
|
||||
|
||||
final override XxeFlowState transform(XxeFlowState flowstate) {
|
||||
exists(int createEntityReferenceNodes |
|
||||
encodeXercesFlowState(flowstate, _, createEntityReferenceNodes) and
|
||||
(
|
||||
globalValueNumber(newValue).getAnExpr().getValue().toInt() = 1 and // true
|
||||
encodeXercesFlowState(result, 1, createEntityReferenceNodes)
|
||||
or
|
||||
not globalValueNumber(newValue).getAnExpr().getValue().toInt() = 1 and // false or unknown
|
||||
encodeXercesFlowState(result, 0, createEntityReferenceNodes)
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The `DOMLSParser.getDomConfig` function.
|
||||
*/
|
||||
class GetDomConfig extends Function {
|
||||
GetDomConfig() { this.getClassAndName("getDomConfig") instanceof DomLSParserClass }
|
||||
}
|
||||
|
||||
/**
|
||||
* The `DOMConfiguration.setParameter` function.
|
||||
*/
|
||||
class DomConfigurationSetParameter extends Function {
|
||||
DomConfigurationSetParameter() {
|
||||
this.getClassAndName("setParameter").getName() = "DOMConfiguration"
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A flow state transformer for a call to `DOMConfiguration.setParameter`
|
||||
* specifying the feature `XMLUni::fgXercesDisableDefaultEntityResolution`.
|
||||
* This is a slightly more complex transformer because the qualifier is a
|
||||
* `DOMConfiguration` pointer returned by `DOMLSParser.getDomConfig` - and it
|
||||
* is *that* qualifier we want to transform the flow state of.
|
||||
*/
|
||||
class DomConfigurationSetParameterTransformer extends XxeFlowStateTransformer {
|
||||
Expr newValue;
|
||||
|
||||
DomConfigurationSetParameterTransformer() {
|
||||
exists(FunctionCall getDomConfigCall, FunctionCall setParameterCall |
|
||||
// this is the qualifier of a call to `DOMLSParser.getDomConfig`.
|
||||
getDomConfigCall.getTarget() instanceof GetDomConfig and
|
||||
this = getDomConfigCall.getQualifier() and
|
||||
// `setParameterCall` is a call to `setParameter` on the return value of
|
||||
// the same call to `DOMLSParser.getDomConfig`.
|
||||
setParameterCall.getTarget() instanceof DomConfigurationSetParameter and
|
||||
globalValueNumber(setParameterCall.getQualifier()).getAnExpr() = getDomConfigCall and
|
||||
// the parameter being set is
|
||||
// `XMLUni::fgXercesDisableDefaultEntityResolution`.
|
||||
globalValueNumber(setParameterCall.getArgument(0)).getAnExpr().(VariableAccess).getTarget()
|
||||
instanceof FeatureDisableDefaultEntityResolution and
|
||||
// the value being set is `newValue`.
|
||||
newValue = setParameterCall.getArgument(1)
|
||||
)
|
||||
}
|
||||
|
||||
final override XxeFlowState transform(XxeFlowState flowstate) {
|
||||
exists(int createEntityReferenceNodes |
|
||||
encodeXercesFlowState(flowstate, _, createEntityReferenceNodes) and
|
||||
(
|
||||
globalValueNumber(newValue).getAnExpr().getValue().toInt() = 1 and // true
|
||||
encodeXercesFlowState(result, 1, createEntityReferenceNodes)
|
||||
or
|
||||
not globalValueNumber(newValue).getAnExpr().getValue().toInt() = 1 and // false or unknown
|
||||
encodeXercesFlowState(result, 0, createEntityReferenceNodes)
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,4 @@
|
||||
---
|
||||
category: minorAnalysis
|
||||
---
|
||||
* The `cpp/unused-local-variable` no longer ignores functions that include `if` and `switch` statements with C++17-style initializers.
|
||||
@@ -0,0 +1,4 @@
|
||||
---
|
||||
category: minorAnalysis
|
||||
---
|
||||
* The "XML external entity expansion" (`cpp/external-entity-expansion`) query precision has been increased to `high`.
|
||||
@@ -1,4 +1,5 @@
|
||||
---
|
||||
category: newQuery
|
||||
---
|
||||
## 0.1.1
|
||||
|
||||
### New Queries
|
||||
|
||||
* An new query `cpp/external-entity-expansion` has been added. The query detects XML objects that are vulnerable to external entity expansion (XXE) attacks.
|
||||
5
cpp/ql/src/change-notes/released/0.1.2.md
Normal file
5
cpp/ql/src/change-notes/released/0.1.2.md
Normal file
@@ -0,0 +1,5 @@
|
||||
## 0.1.2
|
||||
|
||||
### Minor Analysis Improvements
|
||||
|
||||
* The "XML external entity expansion" (`cpp/external-entity-expansion`) query has been extended to support a broader selection of XML libraries and interfaces.
|
||||
@@ -1,2 +1,2 @@
|
||||
---
|
||||
lastReleaseVersion: 0.1.0
|
||||
lastReleaseVersion: 0.1.2
|
||||
|
||||
@@ -58,7 +58,7 @@ where
|
||||
// unfortunately cannot use numeric value here because // O_CREAT is defined differently on different OSes:
|
||||
// https://github.com/red/red/blob/92feb0c0d5f91e087ab35fface6906afbf99b603/runtime/definitions.reds#L477-L491
|
||||
// this may introduce false negatives
|
||||
fctmp.getArgument(1).(BitwiseOrExpr).getAChild*().getValueText().matches("O_CREAT") or
|
||||
fctmp.getArgument(1).(BitwiseOrExpr).getAChild*().getValueText() = "O_CREAT" or
|
||||
fctmp.getArgument(1).getValueText().matches("%O_CREAT%")
|
||||
) and
|
||||
fctmp.getNumberOfArguments() = 2 and
|
||||
|
||||
@@ -0,0 +1,48 @@
|
||||
...
|
||||
try {
|
||||
if (checkValue) throw exception();
|
||||
bufMyData = new myData*[sizeInt];
|
||||
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
for (size_t i = 0; i < sizeInt; i++)
|
||||
{
|
||||
delete[] bufMyData[i]->buffer; // BAD
|
||||
delete bufMyData[i];
|
||||
}
|
||||
...
|
||||
try {
|
||||
if (checkValue) throw exception();
|
||||
bufMyData = new myData*[sizeInt];
|
||||
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
for (size_t i = 0; i < sizeInt; i++)
|
||||
{
|
||||
if(bufMyData[i])
|
||||
{
|
||||
delete[] bufMyData[i]->buffer; // GOOD
|
||||
delete bufMyData[i];
|
||||
}
|
||||
}
|
||||
|
||||
...
|
||||
catch (const exception &) {
|
||||
delete valData;
|
||||
throw;
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
delete valData; // BAD
|
||||
...
|
||||
catch (const exception &) {
|
||||
delete valData;
|
||||
valData = NULL;
|
||||
throw;
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
delete valData; // GOOD
|
||||
...
|
||||
@@ -0,0 +1,23 @@
|
||||
<!DOCTYPE qhelp PUBLIC
|
||||
"-//Semmle//qhelp//EN"
|
||||
"qhelp.dtd">
|
||||
<qhelp>
|
||||
<overview>
|
||||
<p>When releasing memory in a catch block, be sure that the memory was allocated and has not already been released.</p>
|
||||
|
||||
</overview>
|
||||
|
||||
<example>
|
||||
<p>The following example shows erroneous and fixed ways to use exception handling.</p>
|
||||
<sample src="DangerousUseOfExceptionBlocks.cpp" />
|
||||
|
||||
</example>
|
||||
<references>
|
||||
|
||||
<li>
|
||||
CERT C Coding Standard:
|
||||
<a href="https://wiki.sei.cmu.edu/confluence/display/c/EXP34-C.+Do+not+dereference+null+pointers">EXP34-C. Do not dereference null pointers</a>.
|
||||
</li>
|
||||
|
||||
</references>
|
||||
</qhelp>
|
||||
@@ -0,0 +1,199 @@
|
||||
/**
|
||||
* @name Dangerous use of exception blocks.
|
||||
* @description When clearing the data in the catch block, you must be sure that the memory was allocated before the exception.
|
||||
* @kind problem
|
||||
* @id cpp/dangerous-use-of-exception-blocks
|
||||
* @problem.severity warning
|
||||
* @precision medium
|
||||
* @tags correctness
|
||||
* security
|
||||
* external/cwe/cwe-476
|
||||
* external/cwe/cwe-415
|
||||
*/
|
||||
|
||||
import cpp
|
||||
|
||||
/** Holds if `vr` may be released in the `try` block associated with `cb`, or in a `catch` block prior to `cb`. */
|
||||
pragma[inline]
|
||||
predicate doubleCallDelete(BlockStmt b, CatchAnyBlock cb, Variable vr) {
|
||||
// Search for exceptions after freeing memory.
|
||||
exists(Expr e1 |
|
||||
// `e1` is a delete of `vr`
|
||||
(
|
||||
e1 = vr.getAnAccess().getEnclosingStmt().(ExprStmt).getExpr().(DeleteArrayExpr) or
|
||||
e1 = vr.getAnAccess().getEnclosingStmt().(ExprStmt).getExpr().(DeleteExpr)
|
||||
) and
|
||||
e1.getEnclosingFunction() = cb.getEnclosingFunction() and
|
||||
// there is no assignment `vr = 0` in the `try` block after `e1`
|
||||
not exists(AssignExpr ae |
|
||||
ae.getLValue().(VariableAccess).getTarget() = vr and
|
||||
ae.getRValue().getValue() = "0" and
|
||||
e1.getASuccessor+() = ae and
|
||||
ae.getEnclosingStmt().getParentStmt*() = b
|
||||
) and
|
||||
// `e2` is a `throw` (or a function call that may throw) that occurs in the `try` or `catch` block after `e1`
|
||||
exists(Expr e2, ThrowExpr th |
|
||||
(
|
||||
e2 = th or
|
||||
e2 = th.getEnclosingFunction().getACallToThisFunction()
|
||||
) and
|
||||
e2.getEnclosingStmt().getParentStmt*() = b and
|
||||
e1.getASuccessor+() = e2
|
||||
) and
|
||||
e1.getEnclosingStmt().getParentStmt*() = b and
|
||||
(
|
||||
// Search for a situation where there is a release in the block of `try`.
|
||||
b = cb.getTryStmt().getStmt()
|
||||
or
|
||||
// Search for a situation when there is a higher catch block that also frees memory.
|
||||
exists(b.(CatchBlock).getParameter())
|
||||
) and
|
||||
// Exclude the presence of a check in catch block.
|
||||
not exists(IfStmt ifst | ifst.getEnclosingStmt().getParentStmt*() = cb.getAStmt())
|
||||
)
|
||||
}
|
||||
|
||||
/** Holds if an exception can be thrown before the memory is allocated, and when the exception is handled, an attempt is made to access unallocated memory in the catch block. */
|
||||
pragma[inline]
|
||||
predicate pointerDereference(CatchAnyBlock cb, Variable vr, Variable vro) {
|
||||
// Search exceptions before allocating memory.
|
||||
exists(Expr e0, Expr e1 |
|
||||
(
|
||||
// `e0` is a `new` expression (or equivalent function call) assigned to `vro`
|
||||
exists(AssignExpr ase |
|
||||
ase = vro.getAnAccess().getEnclosingStmt().(ExprStmt).getExpr() and
|
||||
(
|
||||
e0 = ase.getRValue().(NewOrNewArrayExpr) or
|
||||
e0 = ase.getRValue().(NewOrNewArrayExpr).getEnclosingFunction().getACallToThisFunction()
|
||||
) and
|
||||
vro = ase.getLValue().(VariableAccess).getTarget()
|
||||
)
|
||||
or
|
||||
// `e0` is a `new` expression (or equivalent function call) assigned to the array element `vro`
|
||||
exists(AssignExpr ase |
|
||||
ase = vro.getAnAccess().(Qualifier).getEnclosingStmt().(ExprStmt).getExpr() and
|
||||
(
|
||||
e0 = ase.getRValue().(NewOrNewArrayExpr) or
|
||||
e0 = ase.getRValue().(NewOrNewArrayExpr).getEnclosingFunction().getACallToThisFunction()
|
||||
) and
|
||||
not ase.getLValue() instanceof VariableAccess and
|
||||
vro = ase.getLValue().getAPredecessor().(VariableAccess).getTarget()
|
||||
)
|
||||
) and
|
||||
// `e1` is a `new` expression (or equivalent function call) assigned to `vr`
|
||||
exists(AssignExpr ase |
|
||||
ase = vr.getAnAccess().getEnclosingStmt().(ExprStmt).getExpr() and
|
||||
(
|
||||
e1 = ase.getRValue().(NewOrNewArrayExpr) or
|
||||
e1 = ase.getRValue().(NewOrNewArrayExpr).getEnclosingFunction().getACallToThisFunction()
|
||||
) and
|
||||
vr = ase.getLValue().(VariableAccess).getTarget()
|
||||
) and
|
||||
e0.getASuccessor*() = e1 and
|
||||
e0.getEnclosingStmt().getParentStmt*() = cb.getTryStmt().getStmt() and
|
||||
e1.getEnclosingStmt().getParentStmt*() = cb.getTryStmt().getStmt() and
|
||||
// `e2` is a `throw` (or a function call that may throw) that occurs in the `try` block before `e0`
|
||||
exists(Expr e2, ThrowExpr th |
|
||||
(
|
||||
e2 = th or
|
||||
e2 = th.getEnclosingFunction().getACallToThisFunction()
|
||||
) and
|
||||
e2.getEnclosingStmt().getParentStmt*() = cb.getTryStmt().getStmt() and
|
||||
e2.getASuccessor+() = e0
|
||||
)
|
||||
) and
|
||||
// We exclude checking the value of a variable or its parent in the catch block.
|
||||
not exists(IfStmt ifst |
|
||||
ifst.getEnclosingStmt().getParentStmt*() = cb.getAStmt() and
|
||||
(
|
||||
ifst.getCondition().getAChild*().(VariableAccess).getTarget() = vr or
|
||||
ifst.getCondition().getAChild*().(VariableAccess).getTarget() = vro
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
/** Holds if `vro` may be released in the `catch`. */
|
||||
pragma[inline]
|
||||
predicate newThrowDelete(CatchAnyBlock cb, Variable vro) {
|
||||
exists(Expr e0, AssignExpr ase, NewOrNewArrayExpr nae |
|
||||
ase = vro.getAnAccess().getEnclosingStmt().(ExprStmt).getExpr() and
|
||||
nae = ase.getRValue() and
|
||||
not nae.getAChild*().toString() = "nothrow" and
|
||||
(
|
||||
e0 = nae or
|
||||
e0 = nae.getEnclosingFunction().getACallToThisFunction()
|
||||
) and
|
||||
vro = ase.getLValue().(VariableAccess).getTarget() and
|
||||
e0.getEnclosingStmt().getParentStmt*() = cb.getTryStmt().getStmt() and
|
||||
not exists(AssignExpr ase1 |
|
||||
vro = ase1.getLValue().(VariableAccess).getTarget() and
|
||||
ase1.getRValue().getValue() = "0" and
|
||||
ase1.getASuccessor*() = e0
|
||||
)
|
||||
) and
|
||||
not exists(Initializer it |
|
||||
vro.getInitializer() = it and
|
||||
it.getExpr().getValue() = "0"
|
||||
) and
|
||||
not exists(ConstructorFieldInit ci | vro = ci.getTarget())
|
||||
}
|
||||
|
||||
from CatchAnyBlock cb, string msg
|
||||
where
|
||||
exists(Variable vr, Variable vro, Expr exp |
|
||||
exp.getEnclosingStmt().getParentStmt*() = cb and
|
||||
exists(VariableAccess va |
|
||||
(
|
||||
(
|
||||
va = exp.(DeleteArrayExpr).getExpr().getAPredecessor+().(Qualifier) or
|
||||
va = exp.(DeleteArrayExpr).getExpr().getAPredecessor+()
|
||||
) and
|
||||
vr = exp.(DeleteArrayExpr).getExpr().(VariableAccess).getTarget()
|
||||
or
|
||||
(
|
||||
va = exp.(DeleteExpr).getExpr().getAPredecessor+().(Qualifier) or
|
||||
va = exp.(DeleteExpr).getExpr().getAPredecessor+()
|
||||
) and
|
||||
vr = exp.(DeleteExpr).getExpr().(VariableAccess).getTarget()
|
||||
) and
|
||||
va.getEnclosingStmt() = exp.getEnclosingStmt() and
|
||||
vro = va.getTarget() and
|
||||
vr != vro
|
||||
) and
|
||||
pointerDereference(cb, vr, vro) and
|
||||
msg =
|
||||
"it is possible to dereference a pointer when accessing a " + vr.getName() +
|
||||
", since it is possible to throw an exception before the memory for the " + vro.getName() +
|
||||
" is allocated"
|
||||
)
|
||||
or
|
||||
exists(Expr exp, Variable vr |
|
||||
(
|
||||
exp.(DeleteExpr).getEnclosingStmt().getParentStmt*() = cb and
|
||||
vr = exp.(DeleteExpr).getExpr().(VariableAccess).getTarget()
|
||||
or
|
||||
exp.(DeleteArrayExpr).getEnclosingStmt().getParentStmt*() = cb and
|
||||
vr = exp.(DeleteArrayExpr).getExpr().(VariableAccess).getTarget()
|
||||
) and
|
||||
doubleCallDelete(_, cb, vr) and
|
||||
msg =
|
||||
"This allocation may have been released in the try block or a previous catch block." +
|
||||
vr.getName()
|
||||
)
|
||||
or
|
||||
exists(Variable vro, Expr exp |
|
||||
exp.getEnclosingStmt().getParentStmt*() = cb and
|
||||
exists(VariableAccess va |
|
||||
(
|
||||
va = exp.(DeleteArrayExpr).getExpr() or
|
||||
va = exp.(DeleteExpr).getExpr()
|
||||
) and
|
||||
va.getEnclosingStmt() = exp.getEnclosingStmt() and
|
||||
vro = va.getTarget()
|
||||
) and
|
||||
newThrowDelete(cb, vro) and
|
||||
msg =
|
||||
"If the allocation in the try block fails, then an unallocated pointer " + vro.getName() +
|
||||
" will be freed in the catch block."
|
||||
)
|
||||
select cb, msg
|
||||
@@ -13,7 +13,7 @@ import cpp
|
||||
|
||||
from Function f
|
||||
where
|
||||
f.getName().regexpMatch("atof|atoi|atol") and
|
||||
f.getName() = ["atof", "atoi", "atol"] and
|
||||
f.getFile().getAbsolutePath().matches("%stdlib.h")
|
||||
select f.getACallToThisFunction(),
|
||||
"AV Rule 23: The library functions atof, atoi and atol from library <stdlib.h> shall not be used."
|
||||
|
||||
@@ -13,7 +13,7 @@ import cpp
|
||||
|
||||
from Function f
|
||||
where
|
||||
f.getName().regexpMatch("abort|exit|getenv|system") and
|
||||
f.getName() = ["abort", "exit", "getenv", "system"] and
|
||||
f.getFile().getAbsolutePath().matches("%stdlib.h")
|
||||
select f.getACallToThisFunction(),
|
||||
"The library functions abort, exit, getenv and system from library <stdlib.h> should not be used."
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
name: codeql/cpp-queries
|
||||
version: 0.1.1-dev
|
||||
version: 0.1.3-dev
|
||||
groups:
|
||||
- cpp
|
||||
- queries
|
||||
|
||||
@@ -181,7 +181,7 @@ private string expectationCommentPattern() { result = "\\s*\\$((?:[^/]|/[^/])*)(
|
||||
/**
|
||||
* The possible columns in an expectation comment. The `TDefaultColumn` branch represents the first
|
||||
* column in a comment. This column is not precedeeded by a name. `TNamedColumn(name)` represents a
|
||||
* column containing expected results preceeded by the string `name:`.
|
||||
* column containing expected results preceded by the string `name:`.
|
||||
*/
|
||||
private newtype TColumn =
|
||||
TDefaultColumn() or
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
| test.cpp:14:16:14:16 | p | unsafe_put_user write user-mode pointer $@ without check. | test.cpp:14:16:14:16 | p | p |
|
||||
@@ -0,0 +1 @@
|
||||
experimental/Security/CWE/CWE-020/NoCheckBeforeUnsafePutUser.ql
|
||||
@@ -0,0 +1,82 @@
|
||||
|
||||
typedef unsigned long size_t;
|
||||
|
||||
void SYSC_SOMESYSTEMCALL(void *param);
|
||||
|
||||
bool user_access_begin_impl(const void *where, size_t sz);
|
||||
void user_access_end_impl();
|
||||
#define user_access_begin(where, sz) user_access_begin_impl(where, sz)
|
||||
#define user_access_end() user_access_end_impl()
|
||||
|
||||
void unsafe_put_user_impl(int what, const void *where, size_t sz);
|
||||
#define unsafe_put_user(what, where) unsafe_put_user_impl( (what), (where), sizeof(*(where)) )
|
||||
|
||||
void test1(int p)
|
||||
{
|
||||
SYSC_SOMESYSTEMCALL(&p);
|
||||
|
||||
unsafe_put_user(123, &p); // BAD
|
||||
}
|
||||
|
||||
void test2(int p)
|
||||
{
|
||||
SYSC_SOMESYSTEMCALL(&p);
|
||||
|
||||
if (user_access_begin(&p, sizeof(p)))
|
||||
{
|
||||
unsafe_put_user(123, &p); // GOOD
|
||||
|
||||
user_access_end();
|
||||
}
|
||||
}
|
||||
|
||||
void test3()
|
||||
{
|
||||
int v;
|
||||
|
||||
SYSC_SOMESYSTEMCALL(&v);
|
||||
|
||||
unsafe_put_user(123, &v); // BAD [NOT DETECTED]
|
||||
}
|
||||
|
||||
void test4()
|
||||
{
|
||||
int v;
|
||||
|
||||
SYSC_SOMESYSTEMCALL(&v);
|
||||
|
||||
if (user_access_begin(&v, sizeof(v)))
|
||||
{
|
||||
unsafe_put_user(123, &v); // GOOD
|
||||
|
||||
user_access_end();
|
||||
}
|
||||
}
|
||||
|
||||
struct data
|
||||
{
|
||||
int x;
|
||||
};
|
||||
|
||||
void test5()
|
||||
{
|
||||
data myData;
|
||||
|
||||
SYSC_SOMESYSTEMCALL(&myData);
|
||||
|
||||
unsafe_put_user(123, &(myData.x)); // BAD [NOT DETECTED]
|
||||
}
|
||||
|
||||
void test6()
|
||||
{
|
||||
data myData;
|
||||
|
||||
SYSC_SOMESYSTEMCALL(&myData);
|
||||
|
||||
if (user_access_begin(&myData, sizeof(myData)))
|
||||
{
|
||||
unsafe_put_user(123, &(myData.x)); // GOOD
|
||||
|
||||
user_access_end();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
| test.cpp:63:3:71:3 | { ... } | If the allocation in the try block fails, then an unallocated pointer bufMyData will be freed in the catch block. |
|
||||
| test.cpp:63:3:71:3 | { ... } | If the allocation in the try block fails, then an unallocated pointer buffer will be freed in the catch block. |
|
||||
| test.cpp:63:3:71:3 | { ... } | it is possible to dereference a pointer when accessing a buffer, since it is possible to throw an exception before the memory for the bufMyData is allocated |
|
||||
| test.cpp:91:3:100:3 | { ... } | If the allocation in the try block fails, then an unallocated pointer buffer will be freed in the catch block. |
|
||||
| test.cpp:120:3:128:3 | { ... } | If the allocation in the try block fails, then an unallocated pointer buffer will be freed in the catch block. |
|
||||
| test.cpp:143:3:151:3 | { ... } | If the allocation in the try block fails, then an unallocated pointer buffer will be freed in the catch block. |
|
||||
| test.cpp:181:3:183:3 | { ... } | This allocation may have been released in the try block or a previous catch block.valData |
|
||||
| test.cpp:219:3:221:3 | { ... } | This allocation may have been released in the try block or a previous catch block.valData |
|
||||
@@ -0,0 +1 @@
|
||||
experimental/Security/CWE/CWE-476/DangerousUseOfExceptionBlocks.ql
|
||||
@@ -0,0 +1,255 @@
|
||||
#define NULL ((void*)0)
|
||||
typedef unsigned long size_t;
|
||||
namespace std {
|
||||
enum class align_val_t : size_t {};
|
||||
}
|
||||
|
||||
class exception {};
|
||||
|
||||
void cleanFunction();
|
||||
|
||||
void* operator new(size_t, float);
|
||||
void* operator new[](size_t, float);
|
||||
void* operator new(size_t, std::align_val_t, float);
|
||||
void* operator new[](size_t, std::align_val_t, float);
|
||||
void operator delete(void*, float);
|
||||
void operator delete[](void*, float);
|
||||
void operator delete(void*, std::align_val_t, float);
|
||||
void operator delete[](void*, std::align_val_t, float);
|
||||
|
||||
struct myData
|
||||
{
|
||||
int sizeInt;
|
||||
char* buffer;
|
||||
};
|
||||
|
||||
struct myGlobalData
|
||||
{
|
||||
int sizeInt;
|
||||
myData** bufMyData;
|
||||
};
|
||||
|
||||
void allocData(myData ** bufMyData) {
|
||||
for (size_t i = 0; i < 10; i++)
|
||||
{
|
||||
bufMyData[i] = new myData;
|
||||
bufMyData[i]->sizeInt = 10;
|
||||
bufMyData[i]->buffer = new char[10];
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void throwFunction(int a) {
|
||||
if (a == 5) throw "my exception!";
|
||||
}
|
||||
void throwFunction2(int a) {
|
||||
if (a == 5) throw exception();
|
||||
}
|
||||
void funcWork1b() {
|
||||
int a;
|
||||
myData **bufMyData;
|
||||
|
||||
try {
|
||||
cleanFunction();
|
||||
throwFunction(a);
|
||||
|
||||
bufMyData = new myData*[10];
|
||||
cleanFunction();
|
||||
allocData(bufMyData);
|
||||
cleanFunction();
|
||||
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
for (size_t i = 0; i < 10; i++)
|
||||
{
|
||||
delete[] bufMyData[i]->buffer; // BAD
|
||||
delete bufMyData[i];
|
||||
}
|
||||
delete [] bufMyData;
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
void funcWork1() {
|
||||
int a;
|
||||
int i;
|
||||
myData **bufMyData;
|
||||
|
||||
bufMyData = new myData*[10];
|
||||
for (i = 0; i < 10; i++)
|
||||
bufMyData[i] = 0;
|
||||
try {
|
||||
cleanFunction();
|
||||
throwFunction(a);
|
||||
cleanFunction();
|
||||
allocData(bufMyData);
|
||||
cleanFunction();
|
||||
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
for (size_t i = 0; i < 10; i++)
|
||||
{
|
||||
if (bufMyData[i])
|
||||
delete[] bufMyData[i]->buffer; // BAD
|
||||
delete bufMyData[i];
|
||||
}
|
||||
delete [] bufMyData;
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
void funcWork2() {
|
||||
int a;
|
||||
myData **bufMyData;
|
||||
|
||||
bufMyData = new myData*[10];
|
||||
try {
|
||||
do {
|
||||
cleanFunction();
|
||||
allocData(bufMyData);
|
||||
cleanFunction();
|
||||
throwFunction(a);
|
||||
|
||||
}
|
||||
while(0);
|
||||
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
for (size_t i = 0; i < 10; i++)
|
||||
{
|
||||
delete[] bufMyData[i]->buffer; // BAD
|
||||
delete bufMyData[i];
|
||||
}
|
||||
delete [] bufMyData;
|
||||
|
||||
}
|
||||
}
|
||||
void funcWork3() {
|
||||
int a;
|
||||
myData **bufMyData;
|
||||
|
||||
bufMyData = new myData*[10];
|
||||
try {
|
||||
cleanFunction();
|
||||
allocData(bufMyData);
|
||||
cleanFunction();
|
||||
throwFunction(a);
|
||||
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
for (size_t i = 0; i < 10; i++)
|
||||
{
|
||||
delete[] bufMyData[i]->buffer; // BAD
|
||||
delete bufMyData[i];
|
||||
}
|
||||
delete [] bufMyData;
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void funcWork4() {
|
||||
int a;
|
||||
myGlobalData *valData = 0;
|
||||
try {
|
||||
valData = new myGlobalData;
|
||||
cleanFunction();
|
||||
delete valData;
|
||||
valData = 0;
|
||||
throwFunction(a);
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
delete valData; // GOOD
|
||||
}
|
||||
}
|
||||
|
||||
void funcWork4b() {
|
||||
int a;
|
||||
myGlobalData *valData = 0;
|
||||
try {
|
||||
valData = new myGlobalData;
|
||||
cleanFunction();
|
||||
delete valData;
|
||||
throwFunction(a);
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
delete valData; // BAD
|
||||
}
|
||||
}
|
||||
void funcWork5() {
|
||||
int a;
|
||||
myGlobalData *valData = 0;
|
||||
try {
|
||||
valData = new myGlobalData;
|
||||
cleanFunction();
|
||||
delete valData;
|
||||
valData = 0;
|
||||
throwFunction2(a);
|
||||
}
|
||||
catch (const exception &) {
|
||||
delete valData;
|
||||
valData = 0;
|
||||
throw;
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
delete valData; // GOOD
|
||||
}
|
||||
}
|
||||
|
||||
void funcWork5b() {
|
||||
int a;
|
||||
myGlobalData *valData = 0;
|
||||
try {
|
||||
valData = new myGlobalData;
|
||||
cleanFunction();
|
||||
throwFunction2(a);
|
||||
}
|
||||
catch (const exception &) {
|
||||
delete valData;
|
||||
throw;
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
delete valData; // BAD
|
||||
}
|
||||
}
|
||||
void funcWork6() {
|
||||
int a;
|
||||
int flagB = 0;
|
||||
myGlobalData *valData = 0;
|
||||
try {
|
||||
valData = new myGlobalData;
|
||||
cleanFunction();
|
||||
throwFunction2(a);
|
||||
}
|
||||
catch (const exception &) {
|
||||
delete valData;
|
||||
flagB = 1;
|
||||
throw;
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
if(flagB == 0)
|
||||
delete valData; // GOOD
|
||||
}
|
||||
}
|
||||
|
||||
void runnerFunc()
|
||||
{
|
||||
funcWork1();
|
||||
funcWork1b();
|
||||
funcWork2();
|
||||
funcWork3();
|
||||
funcWork4();
|
||||
funcWork4b();
|
||||
funcWork5();
|
||||
funcWork5b();
|
||||
funcWork6();
|
||||
}
|
||||
@@ -13559,6 +13559,422 @@ ir.cpp:
|
||||
# 1754| Type = [SpecifiedType] const CopyConstructorTestVirtualClass
|
||||
# 1754| ValueCategory = lvalue
|
||||
# 1755| getStmt(2): [ReturnStmt] return ...
|
||||
# 1757| [TopLevelFunction] void if_initialization(int)
|
||||
# 1757| <params>:
|
||||
# 1757| getParameter(0): [Parameter] x
|
||||
# 1757| Type = [IntType] int
|
||||
# 1757| getEntryPoint(): [BlockStmt] { ... }
|
||||
# 1758| getStmt(0): [IfStmt] if (...) ...
|
||||
# 1758| getInitialization(): [DeclStmt] declaration
|
||||
# 1758| getDeclarationEntry(0): [VariableDeclarationEntry] definition of y
|
||||
# 1758| Type = [IntType] int
|
||||
# 1758| getVariable().getInitializer(): [Initializer] initializer for y
|
||||
# 1758| getExpr(): [VariableAccess] x
|
||||
# 1758| Type = [IntType] int
|
||||
# 1758| ValueCategory = prvalue(load)
|
||||
# 1758| getCondition(): [AddExpr] ... + ...
|
||||
# 1758| Type = [IntType] int
|
||||
# 1758| ValueCategory = prvalue
|
||||
# 1758| getLeftOperand(): [VariableAccess] x
|
||||
# 1758| Type = [IntType] int
|
||||
# 1758| ValueCategory = prvalue(load)
|
||||
# 1758| getRightOperand(): [Literal] 1
|
||||
# 1758| Type = [IntType] int
|
||||
# 1758| Value = [Literal] 1
|
||||
# 1758| ValueCategory = prvalue
|
||||
# 1758| getThen(): [BlockStmt] { ... }
|
||||
# 1759| getStmt(0): [ExprStmt] ExprStmt
|
||||
# 1759| getExpr(): [AssignExpr] ... = ...
|
||||
# 1759| Type = [IntType] int
|
||||
# 1759| ValueCategory = lvalue
|
||||
# 1759| getLValue(): [VariableAccess] x
|
||||
# 1759| Type = [IntType] int
|
||||
# 1759| ValueCategory = lvalue
|
||||
# 1759| getRValue(): [AddExpr] ... + ...
|
||||
# 1759| Type = [IntType] int
|
||||
# 1759| ValueCategory = prvalue
|
||||
# 1759| getLeftOperand(): [VariableAccess] x
|
||||
# 1759| Type = [IntType] int
|
||||
# 1759| ValueCategory = prvalue(load)
|
||||
# 1759| getRightOperand(): [VariableAccess] y
|
||||
# 1759| Type = [IntType] int
|
||||
# 1759| ValueCategory = prvalue(load)
|
||||
# 1758| getCondition().getFullyConverted(): [CStyleCast] (bool)...
|
||||
# 1758| Conversion = [BoolConversion] conversion to bool
|
||||
# 1758| Type = [BoolType] bool
|
||||
# 1758| ValueCategory = prvalue
|
||||
# 1762| getStmt(1): [DeclStmt] declaration
|
||||
# 1762| getDeclarationEntry(0): [VariableDeclarationEntry] definition of w
|
||||
# 1762| Type = [IntType] int
|
||||
# 1763| getStmt(2): [IfStmt] if (...) ...
|
||||
# 1763| getInitialization(): [ExprStmt] ExprStmt
|
||||
# 1763| getExpr(): [AssignExpr] ... = ...
|
||||
# 1763| Type = [IntType] int
|
||||
# 1763| ValueCategory = lvalue
|
||||
# 1763| getLValue(): [VariableAccess] w
|
||||
# 1763| Type = [IntType] int
|
||||
# 1763| ValueCategory = lvalue
|
||||
# 1763| getRValue(): [VariableAccess] x
|
||||
# 1763| Type = [IntType] int
|
||||
# 1763| ValueCategory = prvalue(load)
|
||||
# 1763| getCondition(): [AddExpr] ... + ...
|
||||
# 1763| Type = [IntType] int
|
||||
# 1763| ValueCategory = prvalue
|
||||
# 1763| getLeftOperand(): [VariableAccess] x
|
||||
# 1763| Type = [IntType] int
|
||||
# 1763| ValueCategory = prvalue(load)
|
||||
# 1763| getRightOperand(): [Literal] 1
|
||||
# 1763| Type = [IntType] int
|
||||
# 1763| Value = [Literal] 1
|
||||
# 1763| ValueCategory = prvalue
|
||||
# 1763| getThen(): [BlockStmt] { ... }
|
||||
# 1764| getStmt(0): [ExprStmt] ExprStmt
|
||||
# 1764| getExpr(): [AssignExpr] ... = ...
|
||||
# 1764| Type = [IntType] int
|
||||
# 1764| ValueCategory = lvalue
|
||||
# 1764| getLValue(): [VariableAccess] x
|
||||
# 1764| Type = [IntType] int
|
||||
# 1764| ValueCategory = lvalue
|
||||
# 1764| getRValue(): [AddExpr] ... + ...
|
||||
# 1764| Type = [IntType] int
|
||||
# 1764| ValueCategory = prvalue
|
||||
# 1764| getLeftOperand(): [VariableAccess] x
|
||||
# 1764| Type = [IntType] int
|
||||
# 1764| ValueCategory = prvalue(load)
|
||||
# 1764| getRightOperand(): [VariableAccess] w
|
||||
# 1764| Type = [IntType] int
|
||||
# 1764| ValueCategory = prvalue(load)
|
||||
# 1763| getCondition().getFullyConverted(): [CStyleCast] (bool)...
|
||||
# 1763| Conversion = [BoolConversion] conversion to bool
|
||||
# 1763| Type = [BoolType] bool
|
||||
# 1763| ValueCategory = prvalue
|
||||
# 1767| getStmt(3): [IfStmt] if (...) ...
|
||||
# 1767| getInitialization(): [ExprStmt] ExprStmt
|
||||
# 1767| getExpr(): [AssignExpr] ... = ...
|
||||
# 1767| Type = [IntType] int
|
||||
# 1767| ValueCategory = lvalue
|
||||
# 1767| getLValue(): [VariableAccess] w
|
||||
# 1767| Type = [IntType] int
|
||||
# 1767| ValueCategory = lvalue
|
||||
# 1767| getRValue(): [VariableAccess] x
|
||||
# 1767| Type = [IntType] int
|
||||
# 1767| ValueCategory = prvalue(load)
|
||||
# 1767| getCondition(): [ConditionDeclExpr] (condition decl)
|
||||
# 1767| Type = [BoolType] bool
|
||||
# 1767| ValueCategory = prvalue
|
||||
# 1767| getVariableAccess(): [VariableAccess] w2
|
||||
# 1767| Type = [IntType] int
|
||||
# 1767| ValueCategory = prvalue(load)
|
||||
# 1767| getVariableAccess().getFullyConverted(): [CStyleCast] (bool)...
|
||||
# 1767| Conversion = [BoolConversion] conversion to bool
|
||||
# 1767| Type = [BoolType] bool
|
||||
# 1767| ValueCategory = prvalue
|
||||
# 1767| getThen(): [BlockStmt] { ... }
|
||||
# 1768| getStmt(0): [ExprStmt] ExprStmt
|
||||
# 1768| getExpr(): [AssignExpr] ... = ...
|
||||
# 1768| Type = [IntType] int
|
||||
# 1768| ValueCategory = lvalue
|
||||
# 1768| getLValue(): [VariableAccess] x
|
||||
# 1768| Type = [IntType] int
|
||||
# 1768| ValueCategory = lvalue
|
||||
# 1768| getRValue(): [AddExpr] ... + ...
|
||||
# 1768| Type = [IntType] int
|
||||
# 1768| ValueCategory = prvalue
|
||||
# 1768| getLeftOperand(): [VariableAccess] x
|
||||
# 1768| Type = [IntType] int
|
||||
# 1768| ValueCategory = prvalue(load)
|
||||
# 1768| getRightOperand(): [VariableAccess] w
|
||||
# 1768| Type = [IntType] int
|
||||
# 1768| ValueCategory = prvalue(load)
|
||||
# 1771| getStmt(4): [IfStmt] if (...) ...
|
||||
# 1771| getInitialization(): [DeclStmt] declaration
|
||||
# 1771| getDeclarationEntry(0): [VariableDeclarationEntry] definition of v
|
||||
# 1771| Type = [IntType] int
|
||||
# 1771| getVariable().getInitializer(): [Initializer] initializer for v
|
||||
# 1771| getExpr(): [VariableAccess] x
|
||||
# 1771| Type = [IntType] int
|
||||
# 1771| ValueCategory = prvalue(load)
|
||||
# 1771| getCondition(): [ConditionDeclExpr] (condition decl)
|
||||
# 1771| Type = [BoolType] bool
|
||||
# 1771| ValueCategory = prvalue
|
||||
# 1771| getVariableAccess(): [VariableAccess] v2
|
||||
# 1771| Type = [IntType] int
|
||||
# 1771| ValueCategory = prvalue(load)
|
||||
# 1771| getVariableAccess().getFullyConverted(): [CStyleCast] (bool)...
|
||||
# 1771| Conversion = [BoolConversion] conversion to bool
|
||||
# 1771| Type = [BoolType] bool
|
||||
# 1771| ValueCategory = prvalue
|
||||
# 1771| getThen(): [BlockStmt] { ... }
|
||||
# 1772| getStmt(0): [ExprStmt] ExprStmt
|
||||
# 1772| getExpr(): [AssignExpr] ... = ...
|
||||
# 1772| Type = [IntType] int
|
||||
# 1772| ValueCategory = lvalue
|
||||
# 1772| getLValue(): [VariableAccess] x
|
||||
# 1772| Type = [IntType] int
|
||||
# 1772| ValueCategory = lvalue
|
||||
# 1772| getRValue(): [AddExpr] ... + ...
|
||||
# 1772| Type = [IntType] int
|
||||
# 1772| ValueCategory = prvalue
|
||||
# 1772| getLeftOperand(): [VariableAccess] x
|
||||
# 1772| Type = [IntType] int
|
||||
# 1772| ValueCategory = prvalue(load)
|
||||
# 1772| getRightOperand(): [VariableAccess] v
|
||||
# 1772| Type = [IntType] int
|
||||
# 1772| ValueCategory = prvalue(load)
|
||||
# 1775| getStmt(5): [DeclStmt] declaration
|
||||
# 1775| getDeclarationEntry(0): [VariableDeclarationEntry] definition of z
|
||||
# 1775| Type = [IntType] int
|
||||
# 1775| getVariable().getInitializer(): [Initializer] initializer for z
|
||||
# 1775| getExpr(): [VariableAccess] x
|
||||
# 1775| Type = [IntType] int
|
||||
# 1775| ValueCategory = prvalue(load)
|
||||
# 1776| getStmt(6): [IfStmt] if (...) ...
|
||||
# 1776| getCondition(): [VariableAccess] z
|
||||
# 1776| Type = [IntType] int
|
||||
# 1776| ValueCategory = prvalue(load)
|
||||
# 1776| getThen(): [BlockStmt] { ... }
|
||||
# 1777| getStmt(0): [ExprStmt] ExprStmt
|
||||
# 1777| getExpr(): [AssignExpr] ... = ...
|
||||
# 1777| Type = [IntType] int
|
||||
# 1777| ValueCategory = lvalue
|
||||
# 1777| getLValue(): [VariableAccess] x
|
||||
# 1777| Type = [IntType] int
|
||||
# 1777| ValueCategory = lvalue
|
||||
# 1777| getRValue(): [AddExpr] ... + ...
|
||||
# 1777| Type = [IntType] int
|
||||
# 1777| ValueCategory = prvalue
|
||||
# 1777| getLeftOperand(): [VariableAccess] x
|
||||
# 1777| Type = [IntType] int
|
||||
# 1777| ValueCategory = prvalue(load)
|
||||
# 1777| getRightOperand(): [VariableAccess] z
|
||||
# 1777| Type = [IntType] int
|
||||
# 1777| ValueCategory = prvalue(load)
|
||||
# 1776| getCondition().getFullyConverted(): [CStyleCast] (bool)...
|
||||
# 1776| Conversion = [BoolConversion] conversion to bool
|
||||
# 1776| Type = [BoolType] bool
|
||||
# 1776| ValueCategory = prvalue
|
||||
# 1780| getStmt(7): [IfStmt] if (...) ...
|
||||
# 1780| getCondition(): [ConditionDeclExpr] (condition decl)
|
||||
# 1780| Type = [BoolType] bool
|
||||
# 1780| ValueCategory = prvalue
|
||||
# 1780| getVariableAccess(): [VariableAccess] z2
|
||||
# 1780| Type = [IntType] int
|
||||
# 1780| ValueCategory = prvalue(load)
|
||||
# 1780| getVariableAccess().getFullyConverted(): [CStyleCast] (bool)...
|
||||
# 1780| Conversion = [BoolConversion] conversion to bool
|
||||
# 1780| Type = [BoolType] bool
|
||||
# 1780| ValueCategory = prvalue
|
||||
# 1780| getThen(): [BlockStmt] { ... }
|
||||
# 1781| getStmt(0): [ExprStmt] ExprStmt
|
||||
# 1781| getExpr(): [AssignAddExpr] ... += ...
|
||||
# 1781| Type = [IntType] int
|
||||
# 1781| ValueCategory = lvalue
|
||||
# 1781| getLValue(): [VariableAccess] x
|
||||
# 1781| Type = [IntType] int
|
||||
# 1781| ValueCategory = lvalue
|
||||
# 1781| getRValue(): [VariableAccess] z2
|
||||
# 1781| Type = [IntType] int
|
||||
# 1781| ValueCategory = prvalue(load)
|
||||
# 1783| getStmt(8): [ReturnStmt] return ...
|
||||
# 1785| [TopLevelFunction] void switch_initialization(int)
|
||||
# 1785| <params>:
|
||||
# 1785| getParameter(0): [Parameter] x
|
||||
# 1785| Type = [IntType] int
|
||||
# 1785| getEntryPoint(): [BlockStmt] { ... }
|
||||
# 1786| getStmt(0): [SwitchStmt] switch (...) ...
|
||||
# 1786| getInitialization(): [DeclStmt] declaration
|
||||
# 1786| getDeclarationEntry(0): [VariableDeclarationEntry] definition of y
|
||||
# 1786| Type = [IntType] int
|
||||
# 1786| getVariable().getInitializer(): [Initializer] initializer for y
|
||||
# 1786| getExpr(): [VariableAccess] x
|
||||
# 1786| Type = [IntType] int
|
||||
# 1786| ValueCategory = prvalue(load)
|
||||
# 1786| getExpr(): [AddExpr] ... + ...
|
||||
# 1786| Type = [IntType] int
|
||||
# 1786| ValueCategory = prvalue
|
||||
# 1786| getLeftOperand(): [VariableAccess] x
|
||||
# 1786| Type = [IntType] int
|
||||
# 1786| ValueCategory = prvalue(load)
|
||||
# 1786| getRightOperand(): [Literal] 1
|
||||
# 1786| Type = [IntType] int
|
||||
# 1786| Value = [Literal] 1
|
||||
# 1786| ValueCategory = prvalue
|
||||
# 1786| getStmt(): [BlockStmt] { ... }
|
||||
# 1787| getStmt(0): [SwitchCase] default:
|
||||
# 1788| getStmt(1): [ExprStmt] ExprStmt
|
||||
# 1788| getExpr(): [AssignExpr] ... = ...
|
||||
# 1788| Type = [IntType] int
|
||||
# 1788| ValueCategory = lvalue
|
||||
# 1788| getLValue(): [VariableAccess] x
|
||||
# 1788| Type = [IntType] int
|
||||
# 1788| ValueCategory = lvalue
|
||||
# 1788| getRValue(): [AddExpr] ... + ...
|
||||
# 1788| Type = [IntType] int
|
||||
# 1788| ValueCategory = prvalue
|
||||
# 1788| getLeftOperand(): [VariableAccess] x
|
||||
# 1788| Type = [IntType] int
|
||||
# 1788| ValueCategory = prvalue(load)
|
||||
# 1788| getRightOperand(): [VariableAccess] y
|
||||
# 1788| Type = [IntType] int
|
||||
# 1788| ValueCategory = prvalue(load)
|
||||
# 1791| getStmt(1): [DeclStmt] declaration
|
||||
# 1791| getDeclarationEntry(0): [VariableDeclarationEntry] definition of w
|
||||
# 1791| Type = [IntType] int
|
||||
# 1792| getStmt(2): [SwitchStmt] switch (...) ...
|
||||
# 1792| getInitialization(): [ExprStmt] ExprStmt
|
||||
# 1792| getExpr(): [AssignExpr] ... = ...
|
||||
# 1792| Type = [IntType] int
|
||||
# 1792| ValueCategory = lvalue
|
||||
# 1792| getLValue(): [VariableAccess] w
|
||||
# 1792| Type = [IntType] int
|
||||
# 1792| ValueCategory = lvalue
|
||||
# 1792| getRValue(): [VariableAccess] x
|
||||
# 1792| Type = [IntType] int
|
||||
# 1792| ValueCategory = prvalue(load)
|
||||
# 1792| getExpr(): [AddExpr] ... + ...
|
||||
# 1792| Type = [IntType] int
|
||||
# 1792| ValueCategory = prvalue
|
||||
# 1792| getLeftOperand(): [VariableAccess] x
|
||||
# 1792| Type = [IntType] int
|
||||
# 1792| ValueCategory = prvalue(load)
|
||||
# 1792| getRightOperand(): [Literal] 1
|
||||
# 1792| Type = [IntType] int
|
||||
# 1792| Value = [Literal] 1
|
||||
# 1792| ValueCategory = prvalue
|
||||
# 1792| getStmt(): [BlockStmt] { ... }
|
||||
# 1793| getStmt(0): [SwitchCase] default:
|
||||
# 1794| getStmt(1): [ExprStmt] ExprStmt
|
||||
# 1794| getExpr(): [AssignExpr] ... = ...
|
||||
# 1794| Type = [IntType] int
|
||||
# 1794| ValueCategory = lvalue
|
||||
# 1794| getLValue(): [VariableAccess] x
|
||||
# 1794| Type = [IntType] int
|
||||
# 1794| ValueCategory = lvalue
|
||||
# 1794| getRValue(): [AddExpr] ... + ...
|
||||
# 1794| Type = [IntType] int
|
||||
# 1794| ValueCategory = prvalue
|
||||
# 1794| getLeftOperand(): [VariableAccess] x
|
||||
# 1794| Type = [IntType] int
|
||||
# 1794| ValueCategory = prvalue(load)
|
||||
# 1794| getRightOperand(): [VariableAccess] w
|
||||
# 1794| Type = [IntType] int
|
||||
# 1794| ValueCategory = prvalue(load)
|
||||
# 1797| getStmt(3): [SwitchStmt] switch (...) ...
|
||||
# 1797| getInitialization(): [ExprStmt] ExprStmt
|
||||
# 1797| getExpr(): [AssignExpr] ... = ...
|
||||
# 1797| Type = [IntType] int
|
||||
# 1797| ValueCategory = lvalue
|
||||
# 1797| getLValue(): [VariableAccess] w
|
||||
# 1797| Type = [IntType] int
|
||||
# 1797| ValueCategory = lvalue
|
||||
# 1797| getRValue(): [VariableAccess] x
|
||||
# 1797| Type = [IntType] int
|
||||
# 1797| ValueCategory = prvalue(load)
|
||||
# 1797| getExpr(): [ConditionDeclExpr] (condition decl)
|
||||
# 1797| Type = [IntType] int
|
||||
# 1797| ValueCategory = prvalue
|
||||
# 1797| getVariableAccess(): [VariableAccess] w2
|
||||
# 1797| Type = [IntType] int
|
||||
# 1797| ValueCategory = prvalue(load)
|
||||
# 1797| getStmt(): [BlockStmt] { ... }
|
||||
# 1798| getStmt(0): [SwitchCase] default:
|
||||
# 1799| getStmt(1): [ExprStmt] ExprStmt
|
||||
# 1799| getExpr(): [AssignExpr] ... = ...
|
||||
# 1799| Type = [IntType] int
|
||||
# 1799| ValueCategory = lvalue
|
||||
# 1799| getLValue(): [VariableAccess] x
|
||||
# 1799| Type = [IntType] int
|
||||
# 1799| ValueCategory = lvalue
|
||||
# 1799| getRValue(): [AddExpr] ... + ...
|
||||
# 1799| Type = [IntType] int
|
||||
# 1799| ValueCategory = prvalue
|
||||
# 1799| getLeftOperand(): [VariableAccess] x
|
||||
# 1799| Type = [IntType] int
|
||||
# 1799| ValueCategory = prvalue(load)
|
||||
# 1799| getRightOperand(): [VariableAccess] w
|
||||
# 1799| Type = [IntType] int
|
||||
# 1799| ValueCategory = prvalue(load)
|
||||
# 1802| getStmt(4): [SwitchStmt] switch (...) ...
|
||||
# 1802| getInitialization(): [DeclStmt] declaration
|
||||
# 1802| getDeclarationEntry(0): [VariableDeclarationEntry] definition of v
|
||||
# 1802| Type = [IntType] int
|
||||
# 1802| getVariable().getInitializer(): [Initializer] initializer for v
|
||||
# 1802| getExpr(): [VariableAccess] x
|
||||
# 1802| Type = [IntType] int
|
||||
# 1802| ValueCategory = prvalue(load)
|
||||
# 1802| getExpr(): [ConditionDeclExpr] (condition decl)
|
||||
# 1802| Type = [IntType] int
|
||||
# 1802| ValueCategory = prvalue
|
||||
# 1802| getVariableAccess(): [VariableAccess] v2
|
||||
# 1802| Type = [IntType] int
|
||||
# 1802| ValueCategory = prvalue(load)
|
||||
# 1802| getStmt(): [BlockStmt] { ... }
|
||||
# 1803| getStmt(0): [SwitchCase] default:
|
||||
# 1804| getStmt(1): [ExprStmt] ExprStmt
|
||||
# 1804| getExpr(): [AssignExpr] ... = ...
|
||||
# 1804| Type = [IntType] int
|
||||
# 1804| ValueCategory = lvalue
|
||||
# 1804| getLValue(): [VariableAccess] x
|
||||
# 1804| Type = [IntType] int
|
||||
# 1804| ValueCategory = lvalue
|
||||
# 1804| getRValue(): [AddExpr] ... + ...
|
||||
# 1804| Type = [IntType] int
|
||||
# 1804| ValueCategory = prvalue
|
||||
# 1804| getLeftOperand(): [VariableAccess] x
|
||||
# 1804| Type = [IntType] int
|
||||
# 1804| ValueCategory = prvalue(load)
|
||||
# 1804| getRightOperand(): [VariableAccess] v
|
||||
# 1804| Type = [IntType] int
|
||||
# 1804| ValueCategory = prvalue(load)
|
||||
# 1807| getStmt(5): [DeclStmt] declaration
|
||||
# 1807| getDeclarationEntry(0): [VariableDeclarationEntry] definition of z
|
||||
# 1807| Type = [IntType] int
|
||||
# 1807| getVariable().getInitializer(): [Initializer] initializer for z
|
||||
# 1807| getExpr(): [VariableAccess] x
|
||||
# 1807| Type = [IntType] int
|
||||
# 1807| ValueCategory = prvalue(load)
|
||||
# 1808| getStmt(6): [SwitchStmt] switch (...) ...
|
||||
# 1808| getExpr(): [VariableAccess] z
|
||||
# 1808| Type = [IntType] int
|
||||
# 1808| ValueCategory = prvalue(load)
|
||||
# 1808| getStmt(): [BlockStmt] { ... }
|
||||
# 1809| getStmt(0): [SwitchCase] default:
|
||||
# 1810| getStmt(1): [ExprStmt] ExprStmt
|
||||
# 1810| getExpr(): [AssignExpr] ... = ...
|
||||
# 1810| Type = [IntType] int
|
||||
# 1810| ValueCategory = lvalue
|
||||
# 1810| getLValue(): [VariableAccess] x
|
||||
# 1810| Type = [IntType] int
|
||||
# 1810| ValueCategory = lvalue
|
||||
# 1810| getRValue(): [AddExpr] ... + ...
|
||||
# 1810| Type = [IntType] int
|
||||
# 1810| ValueCategory = prvalue
|
||||
# 1810| getLeftOperand(): [VariableAccess] x
|
||||
# 1810| Type = [IntType] int
|
||||
# 1810| ValueCategory = prvalue(load)
|
||||
# 1810| getRightOperand(): [VariableAccess] z
|
||||
# 1810| Type = [IntType] int
|
||||
# 1810| ValueCategory = prvalue(load)
|
||||
# 1813| getStmt(7): [SwitchStmt] switch (...) ...
|
||||
# 1813| getExpr(): [ConditionDeclExpr] (condition decl)
|
||||
# 1813| Type = [IntType] int
|
||||
# 1813| ValueCategory = prvalue
|
||||
# 1813| getVariableAccess(): [VariableAccess] z2
|
||||
# 1813| Type = [IntType] int
|
||||
# 1813| ValueCategory = prvalue(load)
|
||||
# 1813| getStmt(): [BlockStmt] { ... }
|
||||
# 1814| getStmt(0): [SwitchCase] default:
|
||||
# 1815| getStmt(1): [ExprStmt] ExprStmt
|
||||
# 1815| getExpr(): [AssignAddExpr] ... += ...
|
||||
# 1815| Type = [IntType] int
|
||||
# 1815| ValueCategory = lvalue
|
||||
# 1815| getLValue(): [VariableAccess] x
|
||||
# 1815| Type = [IntType] int
|
||||
# 1815| ValueCategory = lvalue
|
||||
# 1815| getRValue(): [VariableAccess] z2
|
||||
# 1815| Type = [IntType] int
|
||||
# 1815| ValueCategory = prvalue(load)
|
||||
# 1817| getStmt(8): [ReturnStmt] return ...
|
||||
perf-regression.cpp:
|
||||
# 4| [CopyAssignmentOperator] Big& Big::operator=(Big const&)
|
||||
# 4| <params>:
|
||||
|
||||
@@ -6,6 +6,8 @@ missingOperandType
|
||||
duplicateChiOperand
|
||||
sideEffectWithoutPrimary
|
||||
instructionWithoutSuccessor
|
||||
| ir.cpp:1757:28:1757:28 | InitializeParameter: x | Instruction 'InitializeParameter: x' has no successors in function '$@'. | ir.cpp:1757:6:1757:22 | void if_initialization(int) | void if_initialization(int) |
|
||||
| ir.cpp:1785:32:1785:32 | InitializeParameter: x | Instruction 'InitializeParameter: x' has no successors in function '$@'. | ir.cpp:1785:6:1785:26 | void switch_initialization(int) | void switch_initialization(int) |
|
||||
ambiguousSuccessors
|
||||
unexplainedLoop
|
||||
unnecessaryPhiInstruction
|
||||
|
||||
@@ -6,6 +6,8 @@ missingOperandType
|
||||
duplicateChiOperand
|
||||
sideEffectWithoutPrimary
|
||||
instructionWithoutSuccessor
|
||||
| ir.cpp:1757:28:1757:28 | InitializeParameter: x | Instruction 'InitializeParameter: x' has no successors in function '$@'. | ir.cpp:1757:6:1757:22 | void if_initialization(int) | void if_initialization(int) |
|
||||
| ir.cpp:1785:32:1785:32 | InitializeParameter: x | Instruction 'InitializeParameter: x' has no successors in function '$@'. | ir.cpp:1785:6:1785:26 | void switch_initialization(int) | void switch_initialization(int) |
|
||||
ambiguousSuccessors
|
||||
unexplainedLoop
|
||||
unnecessaryPhiInstruction
|
||||
|
||||
@@ -1754,6 +1754,68 @@ int implicit_copy_constructor_test(
|
||||
CopyConstructorTestVirtualClass cy = y;
|
||||
}
|
||||
|
||||
void if_initialization(int x) {
|
||||
if (int y = x; x + 1) {
|
||||
x = x + y;
|
||||
}
|
||||
|
||||
int w;
|
||||
if (w = x; x + 1) {
|
||||
x = x + w;
|
||||
}
|
||||
|
||||
if (w = x; int w2 = w) {
|
||||
x = x + w;
|
||||
}
|
||||
|
||||
if (int v = x; int v2 = v) {
|
||||
x = x + v;
|
||||
}
|
||||
|
||||
int z = x;
|
||||
if (z) {
|
||||
x = x + z;
|
||||
}
|
||||
|
||||
if (int z2 = z) {
|
||||
x += z2;
|
||||
}
|
||||
}
|
||||
|
||||
void switch_initialization(int x) {
|
||||
switch (int y = x; x + 1) {
|
||||
default:
|
||||
x = x + y;
|
||||
}
|
||||
|
||||
int w;
|
||||
switch (w = x; x + 1) {
|
||||
default:
|
||||
x = x + w;
|
||||
}
|
||||
|
||||
switch (w = x; int w2 = w) {
|
||||
default:
|
||||
x = x + w;
|
||||
}
|
||||
|
||||
switch (int v = x; int v2 = v) {
|
||||
default:
|
||||
x = x + v;
|
||||
}
|
||||
|
||||
int z = x;
|
||||
switch (z) {
|
||||
default:
|
||||
x = x + z;
|
||||
}
|
||||
|
||||
switch (int z2 = z) {
|
||||
default:
|
||||
x += z2;
|
||||
}
|
||||
}
|
||||
|
||||
int global_1;
|
||||
|
||||
int global_2 = 1;
|
||||
|
||||
@@ -4841,9 +4841,6 @@
|
||||
| ir.cpp:1043:24:1043:24 | SideEffect | ~m1043_20 |
|
||||
| ir.cpp:1043:31:1043:31 | Address | &:r1043_9 |
|
||||
| ir.cpp:1043:36:1043:55 | Address | &:r1043_11 |
|
||||
| ir.cpp:1043:43:1043:43 | Address | &:r1043_16 |
|
||||
| ir.cpp:1043:43:1043:43 | Arg(this) | this:r1043_16 |
|
||||
| ir.cpp:1043:43:1043:43 | SideEffect | ~m1043_20 |
|
||||
| ir.cpp:1043:43:1043:54 | Address | &:r1043_22 |
|
||||
| ir.cpp:1043:43:1043:54 | Address | &:r1043_24 |
|
||||
| ir.cpp:1043:43:1043:54 | Address | &:r1043_25 |
|
||||
@@ -4864,8 +4861,11 @@
|
||||
| ir.cpp:1043:45:1043:49 | SideEffect | ~m1043_4 |
|
||||
| ir.cpp:1043:45:1043:49 | Unary | r1043_13 |
|
||||
| ir.cpp:1043:45:1043:49 | Unary | r1043_15 |
|
||||
| ir.cpp:1043:53:1043:53 | Load | ~m1043_20 |
|
||||
| ir.cpp:1043:53:1043:53 | Right | r1043_26 |
|
||||
| ir.cpp:1043:52:1043:52 | Address | &:r1043_16 |
|
||||
| ir.cpp:1043:52:1043:52 | Arg(this) | this:r1043_16 |
|
||||
| ir.cpp:1043:52:1043:52 | SideEffect | ~m1043_20 |
|
||||
| ir.cpp:1043:54:1043:54 | Load | ~m1043_20 |
|
||||
| ir.cpp:1043:54:1043:54 | Right | r1043_26 |
|
||||
| ir.cpp:1043:58:1043:58 | ChiPartial | partial:m1043_9 |
|
||||
| ir.cpp:1043:58:1043:58 | ChiTotal | total:m1043_3 |
|
||||
| ir.cpp:1043:58:1043:58 | StoreValue | r1043_8 |
|
||||
@@ -4980,9 +4980,6 @@
|
||||
| ir.cpp:1047:34:1047:34 | SideEffect | ~m1047_20 |
|
||||
| ir.cpp:1047:41:1047:41 | Address | &:r1047_9 |
|
||||
| ir.cpp:1047:46:1047:65 | Address | &:r1047_11 |
|
||||
| ir.cpp:1047:53:1047:53 | Address | &:r1047_16 |
|
||||
| ir.cpp:1047:53:1047:53 | Arg(this) | this:r1047_16 |
|
||||
| ir.cpp:1047:53:1047:53 | SideEffect | ~m1047_20 |
|
||||
| ir.cpp:1047:53:1047:64 | Address | &:r1047_23 |
|
||||
| ir.cpp:1047:53:1047:64 | Load | ~m1047_20 |
|
||||
| ir.cpp:1047:53:1047:64 | StoreValue | r1047_24 |
|
||||
@@ -4997,6 +4994,9 @@
|
||||
| ir.cpp:1047:55:1047:59 | SideEffect | ~m1047_4 |
|
||||
| ir.cpp:1047:55:1047:59 | Unary | r1047_13 |
|
||||
| ir.cpp:1047:55:1047:59 | Unary | r1047_15 |
|
||||
| ir.cpp:1047:62:1047:62 | Address | &:r1047_16 |
|
||||
| ir.cpp:1047:62:1047:62 | Arg(this) | this:r1047_16 |
|
||||
| ir.cpp:1047:62:1047:62 | SideEffect | ~m1047_20 |
|
||||
| ir.cpp:1047:63:1047:63 | Right | r1047_22 |
|
||||
| ir.cpp:1047:68:1047:68 | StoreValue | r1047_8 |
|
||||
| ir.cpp:1047:68:1047:68 | Unary | r1047_7 |
|
||||
@@ -5105,9 +5105,6 @@
|
||||
| ir.cpp:1051:39:1051:39 | SideEffect | ~m1051_20 |
|
||||
| ir.cpp:1051:46:1051:46 | Address | &:r1051_9 |
|
||||
| ir.cpp:1051:51:1051:70 | Address | &:r1051_11 |
|
||||
| ir.cpp:1051:58:1051:58 | Address | &:r1051_16 |
|
||||
| ir.cpp:1051:58:1051:58 | Arg(this) | this:r1051_16 |
|
||||
| ir.cpp:1051:58:1051:58 | SideEffect | ~m1051_20 |
|
||||
| ir.cpp:1051:58:1051:69 | Address | &:r1051_22 |
|
||||
| ir.cpp:1051:58:1051:69 | Address | &:r1051_24 |
|
||||
| ir.cpp:1051:58:1051:69 | Address | &:r1051_26 |
|
||||
@@ -5128,6 +5125,9 @@
|
||||
| ir.cpp:1051:60:1051:64 | SideEffect | ~m1051_4 |
|
||||
| ir.cpp:1051:60:1051:64 | Unary | r1051_13 |
|
||||
| ir.cpp:1051:60:1051:64 | Unary | r1051_15 |
|
||||
| ir.cpp:1051:67:1051:67 | Address | &:r1051_16 |
|
||||
| ir.cpp:1051:67:1051:67 | Arg(this) | this:r1051_16 |
|
||||
| ir.cpp:1051:67:1051:67 | SideEffect | ~m1051_20 |
|
||||
| ir.cpp:1051:73:1051:73 | ChiPartial | partial:m1051_9 |
|
||||
| ir.cpp:1051:73:1051:73 | ChiTotal | total:m1051_3 |
|
||||
| ir.cpp:1051:73:1051:73 | StoreValue | r1051_8 |
|
||||
@@ -5192,9 +5192,6 @@
|
||||
| ir.cpp:1054:49:1054:49 | SideEffect | ~m1054_20 |
|
||||
| ir.cpp:1054:56:1054:56 | Address | &:r1054_9 |
|
||||
| ir.cpp:1054:61:1054:88 | Address | &:r1054_11 |
|
||||
| ir.cpp:1054:68:1054:68 | Address | &:r1054_16 |
|
||||
| ir.cpp:1054:68:1054:68 | Arg(this) | this:r1054_16 |
|
||||
| ir.cpp:1054:68:1054:68 | SideEffect | ~m1054_20 |
|
||||
| ir.cpp:1054:68:1054:87 | Address | &:r1054_37 |
|
||||
| ir.cpp:1054:68:1054:87 | Load | ~m1054_20 |
|
||||
| ir.cpp:1054:68:1054:87 | StoreValue | r1054_38 |
|
||||
@@ -5209,6 +5206,9 @@
|
||||
| ir.cpp:1054:70:1054:74 | SideEffect | ~m1054_4 |
|
||||
| ir.cpp:1054:70:1054:74 | Unary | r1054_13 |
|
||||
| ir.cpp:1054:70:1054:74 | Unary | r1054_15 |
|
||||
| ir.cpp:1054:77:1054:77 | Address | &:r1054_16 |
|
||||
| ir.cpp:1054:77:1054:77 | Arg(this) | this:r1054_16 |
|
||||
| ir.cpp:1054:77:1054:77 | SideEffect | ~m1054_20 |
|
||||
| ir.cpp:1054:78:1054:82 | Address | &:r1054_22 |
|
||||
| ir.cpp:1054:78:1054:82 | Address | &:r1054_24 |
|
||||
| ir.cpp:1054:78:1054:82 | Left | r1054_25 |
|
||||
@@ -8223,45 +8223,51 @@
|
||||
| ir.cpp:1754:42:1754:42 | SideEffect | ~m1752_4 |
|
||||
| ir.cpp:1754:42:1754:42 | Unary | r1754_5 |
|
||||
| ir.cpp:1754:42:1754:42 | Unary | r1754_6 |
|
||||
| ir.cpp:1759:5:1759:12 | Address | &:r1759_3 |
|
||||
| ir.cpp:1759:5:1759:12 | SideEffect | ~m1759_6 |
|
||||
| ir.cpp:1759:16:1759:16 | ChiPartial | partial:m1759_5 |
|
||||
| ir.cpp:1759:16:1759:16 | ChiTotal | total:m1759_2 |
|
||||
| ir.cpp:1759:16:1759:16 | StoreValue | r1759_4 |
|
||||
| ir.cpp:1763:18:1763:25 | Address | &:r1763_3 |
|
||||
| ir.cpp:1763:18:1763:25 | Arg(this) | this:r1763_3 |
|
||||
| ir.cpp:1763:18:1763:25 | SideEffect | ~m1763_10 |
|
||||
| ir.cpp:1763:27:1763:27 | Arg(0) | 0:r1763_5 |
|
||||
| ir.cpp:1763:27:1763:28 | CallTarget | func:r1763_4 |
|
||||
| ir.cpp:1763:27:1763:28 | ChiPartial | partial:m1763_7 |
|
||||
| ir.cpp:1763:27:1763:28 | ChiPartial | partial:m1763_9 |
|
||||
| ir.cpp:1763:27:1763:28 | ChiTotal | total:m1763_2 |
|
||||
| ir.cpp:1763:27:1763:28 | ChiTotal | total:m1763_8 |
|
||||
| ir.cpp:1763:27:1763:28 | SideEffect | ~m1763_2 |
|
||||
| ir.cpp:1765:18:1765:25 | Address | &:r1765_3 |
|
||||
| ir.cpp:1765:18:1765:25 | Arg(this) | this:r1765_3 |
|
||||
| ir.cpp:1765:18:1765:25 | SideEffect | ~m1765_10 |
|
||||
| ir.cpp:1765:28:1765:47 | CallTarget | func:r1765_4 |
|
||||
| ir.cpp:1765:28:1765:47 | ChiPartial | partial:m1765_7 |
|
||||
| ir.cpp:1765:28:1765:47 | ChiPartial | partial:m1765_9 |
|
||||
| ir.cpp:1765:28:1765:47 | ChiTotal | total:m1765_2 |
|
||||
| ir.cpp:1765:28:1765:47 | ChiTotal | total:m1765_8 |
|
||||
| ir.cpp:1765:28:1765:47 | SideEffect | ~m1765_2 |
|
||||
| ir.cpp:1765:46:1765:46 | Arg(0) | 0:r1765_5 |
|
||||
| ir.cpp:1767:7:1767:19 | Address | &:r1767_3 |
|
||||
| ir.cpp:1767:7:1767:19 | SideEffect | ~m1767_8 |
|
||||
| ir.cpp:1767:23:1767:37 | ChiPartial | partial:m1767_7 |
|
||||
| ir.cpp:1767:23:1767:37 | ChiTotal | total:m1767_2 |
|
||||
| ir.cpp:1767:23:1767:37 | StoreValue | r1767_6 |
|
||||
| ir.cpp:1767:23:1767:37 | Unary | r1767_4 |
|
||||
| ir.cpp:1767:23:1767:37 | Unary | r1767_5 |
|
||||
| ir.cpp:1769:5:1769:12 | Address | &:r1769_3 |
|
||||
| ir.cpp:1769:5:1769:12 | SideEffect | ~m1769_7 |
|
||||
| ir.cpp:1769:16:1769:23 | Address | &:r1769_4 |
|
||||
| ir.cpp:1769:16:1769:23 | ChiPartial | partial:m1769_6 |
|
||||
| ir.cpp:1769:16:1769:23 | ChiTotal | total:m1769_2 |
|
||||
| ir.cpp:1769:16:1769:23 | Load | ~m1769_2 |
|
||||
| ir.cpp:1769:16:1769:23 | StoreValue | r1769_5 |
|
||||
| ir.cpp:1757:6:1757:22 | ChiPartial | partial:m1757_3 |
|
||||
| ir.cpp:1757:6:1757:22 | ChiTotal | total:m1757_2 |
|
||||
| ir.cpp:1757:28:1757:28 | Address | &:r1757_5 |
|
||||
| ir.cpp:1785:6:1785:26 | ChiPartial | partial:m1785_3 |
|
||||
| ir.cpp:1785:6:1785:26 | ChiTotal | total:m1785_2 |
|
||||
| ir.cpp:1785:32:1785:32 | Address | &:r1785_5 |
|
||||
| ir.cpp:1821:5:1821:12 | Address | &:r1821_3 |
|
||||
| ir.cpp:1821:5:1821:12 | SideEffect | ~m1821_6 |
|
||||
| ir.cpp:1821:16:1821:16 | ChiPartial | partial:m1821_5 |
|
||||
| ir.cpp:1821:16:1821:16 | ChiTotal | total:m1821_2 |
|
||||
| ir.cpp:1821:16:1821:16 | StoreValue | r1821_4 |
|
||||
| ir.cpp:1825:18:1825:25 | Address | &:r1825_3 |
|
||||
| ir.cpp:1825:18:1825:25 | Arg(this) | this:r1825_3 |
|
||||
| ir.cpp:1825:18:1825:25 | SideEffect | ~m1825_10 |
|
||||
| ir.cpp:1825:27:1825:27 | Arg(0) | 0:r1825_5 |
|
||||
| ir.cpp:1825:27:1825:28 | CallTarget | func:r1825_4 |
|
||||
| ir.cpp:1825:27:1825:28 | ChiPartial | partial:m1825_7 |
|
||||
| ir.cpp:1825:27:1825:28 | ChiPartial | partial:m1825_9 |
|
||||
| ir.cpp:1825:27:1825:28 | ChiTotal | total:m1825_2 |
|
||||
| ir.cpp:1825:27:1825:28 | ChiTotal | total:m1825_8 |
|
||||
| ir.cpp:1825:27:1825:28 | SideEffect | ~m1825_2 |
|
||||
| ir.cpp:1827:18:1827:25 | Address | &:r1827_3 |
|
||||
| ir.cpp:1827:18:1827:25 | Arg(this) | this:r1827_3 |
|
||||
| ir.cpp:1827:18:1827:25 | SideEffect | ~m1827_10 |
|
||||
| ir.cpp:1827:28:1827:47 | CallTarget | func:r1827_4 |
|
||||
| ir.cpp:1827:28:1827:47 | ChiPartial | partial:m1827_7 |
|
||||
| ir.cpp:1827:28:1827:47 | ChiPartial | partial:m1827_9 |
|
||||
| ir.cpp:1827:28:1827:47 | ChiTotal | total:m1827_2 |
|
||||
| ir.cpp:1827:28:1827:47 | ChiTotal | total:m1827_8 |
|
||||
| ir.cpp:1827:28:1827:47 | SideEffect | ~m1827_2 |
|
||||
| ir.cpp:1827:46:1827:46 | Arg(0) | 0:r1827_5 |
|
||||
| ir.cpp:1829:7:1829:19 | Address | &:r1829_3 |
|
||||
| ir.cpp:1829:7:1829:19 | SideEffect | ~m1829_8 |
|
||||
| ir.cpp:1829:23:1829:37 | ChiPartial | partial:m1829_7 |
|
||||
| ir.cpp:1829:23:1829:37 | ChiTotal | total:m1829_2 |
|
||||
| ir.cpp:1829:23:1829:37 | StoreValue | r1829_6 |
|
||||
| ir.cpp:1829:23:1829:37 | Unary | r1829_4 |
|
||||
| ir.cpp:1829:23:1829:37 | Unary | r1829_5 |
|
||||
| ir.cpp:1831:5:1831:12 | Address | &:r1831_3 |
|
||||
| ir.cpp:1831:5:1831:12 | SideEffect | ~m1831_7 |
|
||||
| ir.cpp:1831:16:1831:23 | Address | &:r1831_4 |
|
||||
| ir.cpp:1831:16:1831:23 | ChiPartial | partial:m1831_6 |
|
||||
| ir.cpp:1831:16:1831:23 | ChiTotal | total:m1831_2 |
|
||||
| ir.cpp:1831:16:1831:23 | Load | ~m1831_2 |
|
||||
| ir.cpp:1831:16:1831:23 | StoreValue | r1831_5 |
|
||||
| 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 |
|
||||
|
||||
@@ -1,4 +1,8 @@
|
||||
missingOperand
|
||||
| ir.cpp:1758:9:1758:24 | CopyValue: (condition decl) | Instruction 'CopyValue' is missing an expected operand with tag 'Unary' in function '$@'. | ir.cpp:1757:6:1757:22 | void if_initialization(int) | void if_initialization(int) |
|
||||
| ir.cpp:1763:14:1763:20 | CopyValue: (condition decl) | Instruction 'CopyValue' is missing an expected operand with tag 'Unary' in function '$@'. | ir.cpp:1757:6:1757:22 | void if_initialization(int) | void if_initialization(int) |
|
||||
| ir.cpp:1786:13:1786:28 | CopyValue: (condition decl) | Instruction 'CopyValue' is missing an expected operand with tag 'Unary' in function '$@'. | ir.cpp:1785:6:1785:26 | void switch_initialization(int) | void switch_initialization(int) |
|
||||
| ir.cpp:1792:18:1792:24 | CopyValue: (condition decl) | Instruction 'CopyValue' is missing an expected operand with tag 'Unary' in function '$@'. | ir.cpp:1785:6:1785:26 | void switch_initialization(int) | void switch_initialization(int) |
|
||||
unexpectedOperand
|
||||
duplicateOperand
|
||||
missingPhiOperand
|
||||
@@ -7,6 +11,14 @@ duplicateChiOperand
|
||||
sideEffectWithoutPrimary
|
||||
instructionWithoutSuccessor
|
||||
| ../../../include/memory.h:68:25:68:33 | CopyValue: (reference to) | Instruction 'CopyValue: (reference to)' has no successors in function '$@'. | ../../../include/memory.h:67:5:67:5 | void std::unique_ptr<int, std::default_delete<int>>::~unique_ptr() | void std::unique_ptr<int, std::default_delete<int>>::~unique_ptr() |
|
||||
| ir.cpp:1757:28:1757:28 | InitializeParameter: x | Instruction 'InitializeParameter: x' has no successors in function '$@'. | ir.cpp:1757:6:1757:22 | void if_initialization(int) | void if_initialization(int) |
|
||||
| ir.cpp:1758:20:1758:24 | CompareNE: (bool)... | Instruction 'CompareNE: (bool)...' has no successors in function '$@'. | ir.cpp:1757:6:1757:22 | void if_initialization(int) | void if_initialization(int) |
|
||||
| ir.cpp:1762:9:1762:9 | Uninitialized: definition of w | Instruction 'Uninitialized: definition of w' has no successors in function '$@'. | ir.cpp:1757:6:1757:22 | void if_initialization(int) | void if_initialization(int) |
|
||||
| ir.cpp:1763:16:1763:20 | CompareNE: (bool)... | Instruction 'CompareNE: (bool)...' has no successors in function '$@'. | ir.cpp:1757:6:1757:22 | void if_initialization(int) | void if_initialization(int) |
|
||||
| ir.cpp:1785:32:1785:32 | InitializeParameter: x | Instruction 'InitializeParameter: x' has no successors in function '$@'. | ir.cpp:1785:6:1785:26 | void switch_initialization(int) | void switch_initialization(int) |
|
||||
| ir.cpp:1786:24:1786:28 | Add: ... + ... | Instruction 'Add: ... + ...' has no successors in function '$@'. | ir.cpp:1785:6:1785:26 | void switch_initialization(int) | void switch_initialization(int) |
|
||||
| ir.cpp:1791:9:1791:9 | Uninitialized: definition of w | Instruction 'Uninitialized: definition of w' has no successors in function '$@'. | ir.cpp:1785:6:1785:26 | void switch_initialization(int) | void switch_initialization(int) |
|
||||
| ir.cpp:1792:20:1792:24 | Add: ... + ... | Instruction 'Add: ... + ...' has no successors in function '$@'. | ir.cpp:1785:6:1785:26 | void switch_initialization(int) | void switch_initialization(int) |
|
||||
ambiguousSuccessors
|
||||
unexplainedLoop
|
||||
unnecessaryPhiInstruction
|
||||
@@ -28,6 +40,12 @@ nonUniqueEnclosingIRFunction
|
||||
fieldAddressOnNonPointer
|
||||
thisArgumentIsNonPointer
|
||||
nonUniqueIRVariable
|
||||
| ir.cpp:1759:17:1759:17 | VariableAddress: y | Variable address instruction 'VariableAddress: y' has no associated variable, in function '$@'. | ir.cpp:1757:6:1757:22 | void if_initialization(int) | void if_initialization(int) |
|
||||
| ir.cpp:1771:29:1771:29 | VariableAddress: v | Variable address instruction 'VariableAddress: v' has no associated variable, in function '$@'. | ir.cpp:1757:6:1757:22 | void if_initialization(int) | void if_initialization(int) |
|
||||
| ir.cpp:1772:17:1772:17 | VariableAddress: v | Variable address instruction 'VariableAddress: v' has no associated variable, in function '$@'. | ir.cpp:1757:6:1757:22 | void if_initialization(int) | void if_initialization(int) |
|
||||
| ir.cpp:1788:17:1788:17 | VariableAddress: y | Variable address instruction 'VariableAddress: y' has no associated variable, in function '$@'. | ir.cpp:1785:6:1785:26 | void switch_initialization(int) | void switch_initialization(int) |
|
||||
| ir.cpp:1802:33:1802:33 | VariableAddress: v | Variable address instruction 'VariableAddress: v' has no associated variable, in function '$@'. | ir.cpp:1785:6:1785:26 | void switch_initialization(int) | void switch_initialization(int) |
|
||||
| ir.cpp:1804:17:1804:17 | VariableAddress: v | Variable address instruction 'VariableAddress: v' has no associated variable, in function '$@'. | ir.cpp:1785:6:1785:26 | void switch_initialization(int) | void switch_initialization(int) |
|
||||
missingCanonicalLanguageType
|
||||
multipleCanonicalLanguageTypes
|
||||
missingIRType
|
||||
|
||||
@@ -9431,69 +9431,359 @@ ir.cpp:
|
||||
# 1750| v1750_6(void) = AliasedUse : ~m?
|
||||
# 1750| v1750_7(void) = ExitFunction :
|
||||
|
||||
# 1759| int global_2
|
||||
# 1759| Block 0
|
||||
# 1759| v1759_1(void) = EnterFunction :
|
||||
# 1759| mu1759_2(unknown) = AliasedDefinition :
|
||||
# 1759| r1759_3(glval<int>) = VariableAddress[global_2] :
|
||||
# 1759| r1759_4(int) = Constant[1] :
|
||||
# 1759| mu1759_5(int) = Store[global_2] : &:r1759_3, r1759_4
|
||||
# 1759| v1759_6(void) = ReturnVoid :
|
||||
# 1759| v1759_7(void) = AliasedUse : ~m?
|
||||
# 1759| v1759_8(void) = ExitFunction :
|
||||
# 1757| void if_initialization(int)
|
||||
# 1758| (no string representation)
|
||||
# 1758| CopyValue: (condition decl)
|
||||
# 1758| ConditionalBranch: (condition decl)
|
||||
#-----| False -> Block 4
|
||||
#-----| True -> Block 3
|
||||
|
||||
# 1763| constructor_only global_4
|
||||
# 1763| Block 0
|
||||
# 1763| v1763_1(void) = EnterFunction :
|
||||
# 1763| mu1763_2(unknown) = AliasedDefinition :
|
||||
# 1763| r1763_3(glval<constructor_only>) = VariableAddress[global_4] :
|
||||
# 1763| r1763_4(glval<unknown>) = FunctionAddress[constructor_only] :
|
||||
# 1763| r1763_5(int) = Constant[1] :
|
||||
# 1763| v1763_6(void) = Call[constructor_only] : func:r1763_4, this:r1763_3, 0:r1763_5
|
||||
# 1763| mu1763_7(unknown) = ^CallSideEffect : ~m?
|
||||
# 1763| mu1763_8(constructor_only) = ^IndirectMayWriteSideEffect[-1] : &:r1763_3
|
||||
# 1763| v1763_9(void) = ReturnVoid :
|
||||
# 1763| v1763_10(void) = AliasedUse : ~m?
|
||||
# 1763| v1763_11(void) = ExitFunction :
|
||||
# 1763| (no string representation)
|
||||
# 1763| CopyValue: (condition decl)
|
||||
# 1763| ConditionalBranch: (condition decl)
|
||||
#-----| False -> Block 6
|
||||
#-----| True -> Block 5
|
||||
|
||||
# 1765| constructor_only global_5
|
||||
# 1765| Block 0
|
||||
# 1765| v1765_1(void) = EnterFunction :
|
||||
# 1765| mu1765_2(unknown) = AliasedDefinition :
|
||||
# 1765| r1765_3(glval<constructor_only>) = VariableAddress[global_5] :
|
||||
# 1765| r1765_4(glval<unknown>) = FunctionAddress[constructor_only] :
|
||||
# 1765| r1765_5(int) = Constant[2] :
|
||||
# 1765| v1765_6(void) = Call[constructor_only] : func:r1765_4, this:r1765_3, 0:r1765_5
|
||||
# 1765| mu1765_7(unknown) = ^CallSideEffect : ~m?
|
||||
# 1765| mu1765_8(constructor_only) = ^IndirectMayWriteSideEffect[-1] : &:r1765_3
|
||||
# 1765| v1765_9(void) = ReturnVoid :
|
||||
# 1765| v1765_10(void) = AliasedUse : ~m?
|
||||
# 1765| v1765_11(void) = ExitFunction :
|
||||
# 1757| Block 0
|
||||
# 1757| v1757_1(void) = EnterFunction :
|
||||
# 1757| mu1757_2(unknown) = AliasedDefinition :
|
||||
# 1757| mu1757_3(unknown) = InitializeNonLocal :
|
||||
# 1757| r1757_4(glval<int>) = VariableAddress[x] :
|
||||
# 1757| mu1757_5(int) = InitializeParameter[x] : &:r1757_4
|
||||
|
||||
# 1767| char* global_string
|
||||
# 1767| Block 0
|
||||
# 1767| v1767_1(void) = EnterFunction :
|
||||
# 1767| mu1767_2(unknown) = AliasedDefinition :
|
||||
# 1767| r1767_3(glval<char *>) = VariableAddress[global_string] :
|
||||
# 1767| r1767_4(glval<char[14]>) = StringConstant["global string"] :
|
||||
# 1767| r1767_5(char *) = Convert : r1767_4
|
||||
# 1767| r1767_6(char *) = Convert : r1767_5
|
||||
# 1767| mu1767_7(char *) = Store[global_string] : &:r1767_3, r1767_6
|
||||
# 1767| v1767_8(void) = ReturnVoid :
|
||||
# 1767| v1767_9(void) = AliasedUse : ~m?
|
||||
# 1767| v1767_10(void) = ExitFunction :
|
||||
# 1758| Block 1
|
||||
# 1758| r1758_1(glval<int>) = VariableAddress[x] :
|
||||
# 1758| r1758_2(int) = Load[x] : &:r1758_1, ~m?
|
||||
# 1758| r1758_3(int) = Constant[1] :
|
||||
# 1758| r1758_4(int) = Add : r1758_2, r1758_3
|
||||
# 1758| r1758_5(int) = Constant[0] :
|
||||
# 1758| r1758_6(bool) = CompareNE : r1758_4, r1758_5
|
||||
|
||||
# 1769| int global_6
|
||||
# 1769| Block 0
|
||||
# 1769| v1769_1(void) = EnterFunction :
|
||||
# 1769| mu1769_2(unknown) = AliasedDefinition :
|
||||
# 1769| r1769_3(glval<int>) = VariableAddress[global_6] :
|
||||
# 1769| r1769_4(glval<int>) = VariableAddress[global_2] :
|
||||
# 1769| r1769_5(int) = Load[global_2] : &:r1769_4, ~m?
|
||||
# 1769| mu1769_6(int) = Store[global_6] : &:r1769_3, r1769_5
|
||||
# 1769| v1769_7(void) = ReturnVoid :
|
||||
# 1769| v1769_8(void) = AliasedUse : ~m?
|
||||
# 1769| v1769_9(void) = ExitFunction :
|
||||
# 1763| Block 1
|
||||
# 1763| r1763_1(glval<int>) = VariableAddress[x] :
|
||||
# 1763| r1763_2(int) = Load[x] : &:r1763_1, ~m?
|
||||
# 1763| r1763_3(int) = Constant[1] :
|
||||
# 1763| r1763_4(int) = Add : r1763_2, r1763_3
|
||||
# 1763| r1763_5(int) = Constant[0] :
|
||||
# 1763| r1763_6(bool) = CompareNE : r1763_4, r1763_5
|
||||
|
||||
# 1759| Block 3
|
||||
# 1759| r1759_1(glval<int>) = VariableAddress[x] :
|
||||
# 1759| r1759_2(int) = Load[x] : &:r1759_1, ~m?
|
||||
# 1759| r1759_3(glval<int>) = VariableAddress :
|
||||
# 1759| r1759_4(int) = Load[?] : &:r1759_3, ~m?
|
||||
# 1759| r1759_5(int) = Add : r1759_2, r1759_4
|
||||
# 1759| r1759_6(glval<int>) = VariableAddress[x] :
|
||||
# 1759| mu1759_7(int) = Store[x] : &:r1759_6, r1759_5
|
||||
#-----| Goto -> Block 4
|
||||
|
||||
# 1762| Block 4
|
||||
# 1762| r1762_1(glval<int>) = VariableAddress[w] :
|
||||
# 1762| mu1762_2(int) = Uninitialized[w] : &:r1762_1
|
||||
|
||||
# 1764| Block 5
|
||||
# 1764| r1764_1(glval<int>) = VariableAddress[x] :
|
||||
# 1764| r1764_2(int) = Load[x] : &:r1764_1, ~m?
|
||||
# 1764| r1764_3(glval<int>) = VariableAddress[w] :
|
||||
# 1764| r1764_4(int) = Load[w] : &:r1764_3, ~m?
|
||||
# 1764| r1764_5(int) = Add : r1764_2, r1764_4
|
||||
# 1764| r1764_6(glval<int>) = VariableAddress[x] :
|
||||
# 1764| mu1764_7(int) = Store[x] : &:r1764_6, r1764_5
|
||||
#-----| Goto -> Block 6
|
||||
|
||||
# 1767| Block 6
|
||||
# 1767| r1767_1(glval<int>) = VariableAddress[w2] :
|
||||
# 1767| r1767_2(glval<int>) = VariableAddress[w] :
|
||||
# 1767| r1767_3(int) = Load[w] : &:r1767_2, ~m?
|
||||
# 1767| mu1767_4(int) = Store[w2] : &:r1767_1, r1767_3
|
||||
# 1767| r1767_5(glval<int>) = VariableAddress[w2] :
|
||||
# 1767| r1767_6(int) = Load[w2] : &:r1767_5, ~m?
|
||||
# 1767| r1767_7(int) = Constant[0] :
|
||||
# 1767| r1767_8(bool) = CompareNE : r1767_6, r1767_7
|
||||
# 1767| r1767_9(bool) = CopyValue : r1767_8
|
||||
# 1767| v1767_10(void) = ConditionalBranch : r1767_9
|
||||
#-----| False -> Block 8
|
||||
#-----| True -> Block 7
|
||||
|
||||
# 1768| Block 7
|
||||
# 1768| r1768_1(glval<int>) = VariableAddress[x] :
|
||||
# 1768| r1768_2(int) = Load[x] : &:r1768_1, ~m?
|
||||
# 1768| r1768_3(glval<int>) = VariableAddress[w] :
|
||||
# 1768| r1768_4(int) = Load[w] : &:r1768_3, ~m?
|
||||
# 1768| r1768_5(int) = Add : r1768_2, r1768_4
|
||||
# 1768| r1768_6(glval<int>) = VariableAddress[x] :
|
||||
# 1768| mu1768_7(int) = Store[x] : &:r1768_6, r1768_5
|
||||
#-----| Goto -> Block 8
|
||||
|
||||
# 1771| Block 8
|
||||
# 1771| r1771_1(glval<int>) = VariableAddress[v2] :
|
||||
# 1771| r1771_2(glval<int>) = VariableAddress :
|
||||
# 1771| r1771_3(int) = Load[?] : &:r1771_2, ~m?
|
||||
# 1771| mu1771_4(int) = Store[v2] : &:r1771_1, r1771_3
|
||||
# 1771| r1771_5(glval<int>) = VariableAddress[v2] :
|
||||
# 1771| r1771_6(int) = Load[v2] : &:r1771_5, ~m?
|
||||
# 1771| r1771_7(int) = Constant[0] :
|
||||
# 1771| r1771_8(bool) = CompareNE : r1771_6, r1771_7
|
||||
# 1771| r1771_9(bool) = CopyValue : r1771_8
|
||||
# 1771| v1771_10(void) = ConditionalBranch : r1771_9
|
||||
#-----| False -> Block 10
|
||||
#-----| True -> Block 9
|
||||
|
||||
# 1772| Block 9
|
||||
# 1772| r1772_1(glval<int>) = VariableAddress[x] :
|
||||
# 1772| r1772_2(int) = Load[x] : &:r1772_1, ~m?
|
||||
# 1772| r1772_3(glval<int>) = VariableAddress :
|
||||
# 1772| r1772_4(int) = Load[?] : &:r1772_3, ~m?
|
||||
# 1772| r1772_5(int) = Add : r1772_2, r1772_4
|
||||
# 1772| r1772_6(glval<int>) = VariableAddress[x] :
|
||||
# 1772| mu1772_7(int) = Store[x] : &:r1772_6, r1772_5
|
||||
#-----| Goto -> Block 10
|
||||
|
||||
# 1775| Block 10
|
||||
# 1775| r1775_1(glval<int>) = VariableAddress[z] :
|
||||
# 1775| r1775_2(glval<int>) = VariableAddress[x] :
|
||||
# 1775| r1775_3(int) = Load[x] : &:r1775_2, ~m?
|
||||
# 1775| mu1775_4(int) = Store[z] : &:r1775_1, r1775_3
|
||||
# 1776| r1776_1(glval<int>) = VariableAddress[z] :
|
||||
# 1776| r1776_2(int) = Load[z] : &:r1776_1, ~m?
|
||||
# 1776| r1776_3(int) = Constant[0] :
|
||||
# 1776| r1776_4(bool) = CompareNE : r1776_2, r1776_3
|
||||
# 1776| v1776_5(void) = ConditionalBranch : r1776_4
|
||||
#-----| False -> Block 12
|
||||
#-----| True -> Block 11
|
||||
|
||||
# 1777| Block 11
|
||||
# 1777| r1777_1(glval<int>) = VariableAddress[x] :
|
||||
# 1777| r1777_2(int) = Load[x] : &:r1777_1, ~m?
|
||||
# 1777| r1777_3(glval<int>) = VariableAddress[z] :
|
||||
# 1777| r1777_4(int) = Load[z] : &:r1777_3, ~m?
|
||||
# 1777| r1777_5(int) = Add : r1777_2, r1777_4
|
||||
# 1777| r1777_6(glval<int>) = VariableAddress[x] :
|
||||
# 1777| mu1777_7(int) = Store[x] : &:r1777_6, r1777_5
|
||||
#-----| Goto -> Block 12
|
||||
|
||||
# 1780| Block 12
|
||||
# 1780| r1780_1(glval<int>) = VariableAddress[z2] :
|
||||
# 1780| r1780_2(glval<int>) = VariableAddress[z] :
|
||||
# 1780| r1780_3(int) = Load[z] : &:r1780_2, ~m?
|
||||
# 1780| mu1780_4(int) = Store[z2] : &:r1780_1, r1780_3
|
||||
# 1780| r1780_5(glval<int>) = VariableAddress[z2] :
|
||||
# 1780| r1780_6(int) = Load[z2] : &:r1780_5, ~m?
|
||||
# 1780| r1780_7(int) = Constant[0] :
|
||||
# 1780| r1780_8(bool) = CompareNE : r1780_6, r1780_7
|
||||
# 1780| r1780_9(bool) = CopyValue : r1780_8
|
||||
# 1780| v1780_10(void) = ConditionalBranch : r1780_9
|
||||
#-----| False -> Block 14
|
||||
#-----| True -> Block 13
|
||||
|
||||
# 1781| Block 13
|
||||
# 1781| r1781_1(glval<int>) = VariableAddress[z2] :
|
||||
# 1781| r1781_2(int) = Load[z2] : &:r1781_1, ~m?
|
||||
# 1781| r1781_3(glval<int>) = VariableAddress[x] :
|
||||
# 1781| r1781_4(int) = Load[x] : &:r1781_3, ~m?
|
||||
# 1781| r1781_5(int) = Add : r1781_4, r1781_2
|
||||
# 1781| mu1781_6(int) = Store[x] : &:r1781_3, r1781_5
|
||||
#-----| Goto -> Block 14
|
||||
|
||||
# 1783| Block 14
|
||||
# 1783| v1783_1(void) = NoOp :
|
||||
# 1757| v1757_6(void) = ReturnVoid :
|
||||
# 1757| v1757_7(void) = AliasedUse : ~m?
|
||||
# 1757| v1757_8(void) = ExitFunction :
|
||||
|
||||
# 1785| void switch_initialization(int)
|
||||
# 1786| (no string representation)
|
||||
# 1786| CopyValue: (condition decl)
|
||||
# 1786| Switch: switch (...) ...
|
||||
#-----| Default -> Block 3
|
||||
|
||||
# 1792| (no string representation)
|
||||
# 1792| CopyValue: (condition decl)
|
||||
# 1792| Switch: switch (...) ...
|
||||
#-----| Default -> Block 4
|
||||
|
||||
# 1785| Block 0
|
||||
# 1785| v1785_1(void) = EnterFunction :
|
||||
# 1785| mu1785_2(unknown) = AliasedDefinition :
|
||||
# 1785| mu1785_3(unknown) = InitializeNonLocal :
|
||||
# 1785| r1785_4(glval<int>) = VariableAddress[x] :
|
||||
# 1785| mu1785_5(int) = InitializeParameter[x] : &:r1785_4
|
||||
|
||||
# 1786| Block 1
|
||||
# 1786| r1786_1(glval<int>) = VariableAddress[x] :
|
||||
# 1786| r1786_2(int) = Load[x] : &:r1786_1, ~m?
|
||||
# 1786| r1786_3(int) = Constant[1] :
|
||||
# 1786| r1786_4(int) = Add : r1786_2, r1786_3
|
||||
|
||||
# 1792| Block 1
|
||||
# 1792| r1792_1(glval<int>) = VariableAddress[x] :
|
||||
# 1792| r1792_2(int) = Load[x] : &:r1792_1, ~m?
|
||||
# 1792| r1792_3(int) = Constant[1] :
|
||||
# 1792| r1792_4(int) = Add : r1792_2, r1792_3
|
||||
|
||||
# 1787| Block 3
|
||||
# 1787| v1787_1(void) = NoOp :
|
||||
# 1788| r1788_1(glval<int>) = VariableAddress[x] :
|
||||
# 1788| r1788_2(int) = Load[x] : &:r1788_1, ~m?
|
||||
# 1788| r1788_3(glval<int>) = VariableAddress :
|
||||
# 1788| r1788_4(int) = Load[?] : &:r1788_3, ~m?
|
||||
# 1788| r1788_5(int) = Add : r1788_2, r1788_4
|
||||
# 1788| r1788_6(glval<int>) = VariableAddress[x] :
|
||||
# 1788| mu1788_7(int) = Store[x] : &:r1788_6, r1788_5
|
||||
# 1791| r1791_1(glval<int>) = VariableAddress[w] :
|
||||
# 1791| mu1791_2(int) = Uninitialized[w] : &:r1791_1
|
||||
|
||||
# 1793| Block 4
|
||||
# 1793| v1793_1(void) = NoOp :
|
||||
# 1794| r1794_1(glval<int>) = VariableAddress[x] :
|
||||
# 1794| r1794_2(int) = Load[x] : &:r1794_1, ~m?
|
||||
# 1794| r1794_3(glval<int>) = VariableAddress[w] :
|
||||
# 1794| r1794_4(int) = Load[w] : &:r1794_3, ~m?
|
||||
# 1794| r1794_5(int) = Add : r1794_2, r1794_4
|
||||
# 1794| r1794_6(glval<int>) = VariableAddress[x] :
|
||||
# 1794| mu1794_7(int) = Store[x] : &:r1794_6, r1794_5
|
||||
# 1797| r1797_1(glval<int>) = VariableAddress[w2] :
|
||||
# 1797| r1797_2(glval<int>) = VariableAddress[w] :
|
||||
# 1797| r1797_3(int) = Load[w] : &:r1797_2, ~m?
|
||||
# 1797| mu1797_4(int) = Store[w2] : &:r1797_1, r1797_3
|
||||
# 1797| r1797_5(glval<int>) = VariableAddress[w2] :
|
||||
# 1797| r1797_6(int) = Load[w2] : &:r1797_5, ~m?
|
||||
# 1797| r1797_7(int) = CopyValue : r1797_6
|
||||
# 1797| v1797_8(void) = Switch : r1797_7
|
||||
#-----| Default -> Block 5
|
||||
|
||||
# 1798| Block 5
|
||||
# 1798| v1798_1(void) = NoOp :
|
||||
# 1799| r1799_1(glval<int>) = VariableAddress[x] :
|
||||
# 1799| r1799_2(int) = Load[x] : &:r1799_1, ~m?
|
||||
# 1799| r1799_3(glval<int>) = VariableAddress[w] :
|
||||
# 1799| r1799_4(int) = Load[w] : &:r1799_3, ~m?
|
||||
# 1799| r1799_5(int) = Add : r1799_2, r1799_4
|
||||
# 1799| r1799_6(glval<int>) = VariableAddress[x] :
|
||||
# 1799| mu1799_7(int) = Store[x] : &:r1799_6, r1799_5
|
||||
# 1802| r1802_1(glval<int>) = VariableAddress[v2] :
|
||||
# 1802| r1802_2(glval<int>) = VariableAddress :
|
||||
# 1802| r1802_3(int) = Load[?] : &:r1802_2, ~m?
|
||||
# 1802| mu1802_4(int) = Store[v2] : &:r1802_1, r1802_3
|
||||
# 1802| r1802_5(glval<int>) = VariableAddress[v2] :
|
||||
# 1802| r1802_6(int) = Load[v2] : &:r1802_5, ~m?
|
||||
# 1802| r1802_7(int) = CopyValue : r1802_6
|
||||
# 1802| v1802_8(void) = Switch : r1802_7
|
||||
#-----| Default -> Block 6
|
||||
|
||||
# 1803| Block 6
|
||||
# 1803| v1803_1(void) = NoOp :
|
||||
# 1804| r1804_1(glval<int>) = VariableAddress[x] :
|
||||
# 1804| r1804_2(int) = Load[x] : &:r1804_1, ~m?
|
||||
# 1804| r1804_3(glval<int>) = VariableAddress :
|
||||
# 1804| r1804_4(int) = Load[?] : &:r1804_3, ~m?
|
||||
# 1804| r1804_5(int) = Add : r1804_2, r1804_4
|
||||
# 1804| r1804_6(glval<int>) = VariableAddress[x] :
|
||||
# 1804| mu1804_7(int) = Store[x] : &:r1804_6, r1804_5
|
||||
# 1807| r1807_1(glval<int>) = VariableAddress[z] :
|
||||
# 1807| r1807_2(glval<int>) = VariableAddress[x] :
|
||||
# 1807| r1807_3(int) = Load[x] : &:r1807_2, ~m?
|
||||
# 1807| mu1807_4(int) = Store[z] : &:r1807_1, r1807_3
|
||||
# 1808| r1808_1(glval<int>) = VariableAddress[z] :
|
||||
# 1808| r1808_2(int) = Load[z] : &:r1808_1, ~m?
|
||||
# 1808| v1808_3(void) = Switch : r1808_2
|
||||
#-----| Default -> Block 7
|
||||
|
||||
# 1809| Block 7
|
||||
# 1809| v1809_1(void) = NoOp :
|
||||
# 1810| r1810_1(glval<int>) = VariableAddress[x] :
|
||||
# 1810| r1810_2(int) = Load[x] : &:r1810_1, ~m?
|
||||
# 1810| r1810_3(glval<int>) = VariableAddress[z] :
|
||||
# 1810| r1810_4(int) = Load[z] : &:r1810_3, ~m?
|
||||
# 1810| r1810_5(int) = Add : r1810_2, r1810_4
|
||||
# 1810| r1810_6(glval<int>) = VariableAddress[x] :
|
||||
# 1810| mu1810_7(int) = Store[x] : &:r1810_6, r1810_5
|
||||
# 1813| r1813_1(glval<int>) = VariableAddress[z2] :
|
||||
# 1813| r1813_2(glval<int>) = VariableAddress[z] :
|
||||
# 1813| r1813_3(int) = Load[z] : &:r1813_2, ~m?
|
||||
# 1813| mu1813_4(int) = Store[z2] : &:r1813_1, r1813_3
|
||||
# 1813| r1813_5(glval<int>) = VariableAddress[z2] :
|
||||
# 1813| r1813_6(int) = Load[z2] : &:r1813_5, ~m?
|
||||
# 1813| r1813_7(int) = CopyValue : r1813_6
|
||||
# 1813| v1813_8(void) = Switch : r1813_7
|
||||
#-----| Default -> Block 8
|
||||
|
||||
# 1814| Block 8
|
||||
# 1814| v1814_1(void) = NoOp :
|
||||
# 1815| r1815_1(glval<int>) = VariableAddress[z2] :
|
||||
# 1815| r1815_2(int) = Load[z2] : &:r1815_1, ~m?
|
||||
# 1815| r1815_3(glval<int>) = VariableAddress[x] :
|
||||
# 1815| r1815_4(int) = Load[x] : &:r1815_3, ~m?
|
||||
# 1815| r1815_5(int) = Add : r1815_4, r1815_2
|
||||
# 1815| mu1815_6(int) = Store[x] : &:r1815_3, r1815_5
|
||||
# 1817| v1817_1(void) = NoOp :
|
||||
# 1785| v1785_6(void) = ReturnVoid :
|
||||
# 1785| v1785_7(void) = AliasedUse : ~m?
|
||||
# 1785| v1785_8(void) = ExitFunction :
|
||||
|
||||
# 1821| int global_2
|
||||
# 1821| Block 0
|
||||
# 1821| v1821_1(void) = EnterFunction :
|
||||
# 1821| mu1821_2(unknown) = AliasedDefinition :
|
||||
# 1821| r1821_3(glval<int>) = VariableAddress[global_2] :
|
||||
# 1821| r1821_4(int) = Constant[1] :
|
||||
# 1821| mu1821_5(int) = Store[global_2] : &:r1821_3, r1821_4
|
||||
# 1821| v1821_6(void) = ReturnVoid :
|
||||
# 1821| v1821_7(void) = AliasedUse : ~m?
|
||||
# 1821| v1821_8(void) = ExitFunction :
|
||||
|
||||
# 1825| constructor_only global_4
|
||||
# 1825| Block 0
|
||||
# 1825| v1825_1(void) = EnterFunction :
|
||||
# 1825| mu1825_2(unknown) = AliasedDefinition :
|
||||
# 1825| r1825_3(glval<constructor_only>) = VariableAddress[global_4] :
|
||||
# 1825| r1825_4(glval<unknown>) = FunctionAddress[constructor_only] :
|
||||
# 1825| r1825_5(int) = Constant[1] :
|
||||
# 1825| v1825_6(void) = Call[constructor_only] : func:r1825_4, this:r1825_3, 0:r1825_5
|
||||
# 1825| mu1825_7(unknown) = ^CallSideEffect : ~m?
|
||||
# 1825| mu1825_8(constructor_only) = ^IndirectMayWriteSideEffect[-1] : &:r1825_3
|
||||
# 1825| v1825_9(void) = ReturnVoid :
|
||||
# 1825| v1825_10(void) = AliasedUse : ~m?
|
||||
# 1825| v1825_11(void) = ExitFunction :
|
||||
|
||||
# 1827| constructor_only global_5
|
||||
# 1827| Block 0
|
||||
# 1827| v1827_1(void) = EnterFunction :
|
||||
# 1827| mu1827_2(unknown) = AliasedDefinition :
|
||||
# 1827| r1827_3(glval<constructor_only>) = VariableAddress[global_5] :
|
||||
# 1827| r1827_4(glval<unknown>) = FunctionAddress[constructor_only] :
|
||||
# 1827| r1827_5(int) = Constant[2] :
|
||||
# 1827| v1827_6(void) = Call[constructor_only] : func:r1827_4, this:r1827_3, 0:r1827_5
|
||||
# 1827| mu1827_7(unknown) = ^CallSideEffect : ~m?
|
||||
# 1827| mu1827_8(constructor_only) = ^IndirectMayWriteSideEffect[-1] : &:r1827_3
|
||||
# 1827| v1827_9(void) = ReturnVoid :
|
||||
# 1827| v1827_10(void) = AliasedUse : ~m?
|
||||
# 1827| v1827_11(void) = ExitFunction :
|
||||
|
||||
# 1829| char* global_string
|
||||
# 1829| Block 0
|
||||
# 1829| v1829_1(void) = EnterFunction :
|
||||
# 1829| mu1829_2(unknown) = AliasedDefinition :
|
||||
# 1829| r1829_3(glval<char *>) = VariableAddress[global_string] :
|
||||
# 1829| r1829_4(glval<char[14]>) = StringConstant["global string"] :
|
||||
# 1829| r1829_5(char *) = Convert : r1829_4
|
||||
# 1829| r1829_6(char *) = Convert : r1829_5
|
||||
# 1829| mu1829_7(char *) = Store[global_string] : &:r1829_3, r1829_6
|
||||
# 1829| v1829_8(void) = ReturnVoid :
|
||||
# 1829| v1829_9(void) = AliasedUse : ~m?
|
||||
# 1829| v1829_10(void) = ExitFunction :
|
||||
|
||||
# 1831| int global_6
|
||||
# 1831| Block 0
|
||||
# 1831| v1831_1(void) = EnterFunction :
|
||||
# 1831| mu1831_2(unknown) = AliasedDefinition :
|
||||
# 1831| r1831_3(glval<int>) = VariableAddress[global_6] :
|
||||
# 1831| r1831_4(glval<int>) = VariableAddress[global_2] :
|
||||
# 1831| r1831_5(int) = Load[global_2] : &:r1831_4, ~m?
|
||||
# 1831| mu1831_6(int) = Store[global_6] : &:r1831_3, r1831_5
|
||||
# 1831| v1831_7(void) = ReturnVoid :
|
||||
# 1831| v1831_8(void) = AliasedUse : ~m?
|
||||
# 1831| v1831_9(void) = ExitFunction :
|
||||
|
||||
perf-regression.cpp:
|
||||
# 6| void Big::Big()
|
||||
|
||||
@@ -6,6 +6,8 @@ missingOperandType
|
||||
duplicateChiOperand
|
||||
sideEffectWithoutPrimary
|
||||
instructionWithoutSuccessor
|
||||
| ir.cpp:1757:28:1757:28 | InitializeParameter: x | Instruction 'InitializeParameter: x' has no successors in function '$@'. | ir.cpp:1757:6:1757:22 | void if_initialization(int) | void if_initialization(int) |
|
||||
| ir.cpp:1785:32:1785:32 | InitializeParameter: x | Instruction 'InitializeParameter: x' has no successors in function '$@'. | ir.cpp:1785:6:1785:26 | void switch_initialization(int) | void switch_initialization(int) |
|
||||
ambiguousSuccessors
|
||||
unexplainedLoop
|
||||
unnecessaryPhiInstruction
|
||||
|
||||
@@ -6,6 +6,8 @@ missingOperandType
|
||||
duplicateChiOperand
|
||||
sideEffectWithoutPrimary
|
||||
instructionWithoutSuccessor
|
||||
| ir.cpp:1757:28:1757:28 | InitializeParameter: x | Instruction 'InitializeParameter: x' has no successors in function '$@'. | ir.cpp:1757:6:1757:22 | void if_initialization(int) | void if_initialization(int) |
|
||||
| ir.cpp:1785:32:1785:32 | InitializeParameter: x | Instruction 'InitializeParameter: x' has no successors in function '$@'. | ir.cpp:1785:6:1785:26 | void switch_initialization(int) | void switch_initialization(int) |
|
||||
ambiguousSuccessors
|
||||
unexplainedLoop
|
||||
unnecessaryPhiInstruction
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
|
||||
import cpp
|
||||
|
||||
from AnalysedString s, string str
|
||||
from AnalyzedString s, string str
|
||||
where
|
||||
if s.(StringLiteral).getUnspecifiedType().(DerivedType).getBaseType() instanceof Wchar_t
|
||||
then str = "[?]"
|
||||
|
||||
@@ -1,80 +1,133 @@
|
||||
edges
|
||||
| tests.cpp:33:23:33:43 | XercesDOMParser output argument | tests.cpp:35:2:35:2 | p |
|
||||
| tests.cpp:46:23:46:43 | XercesDOMParser output argument | tests.cpp:49:2:49:2 | p |
|
||||
| tests.cpp:53:19:53:19 | VariableAddress [post update] | tests.cpp:55:2:55:2 | p |
|
||||
| tests.cpp:53:23:53:43 | XercesDOMParser output argument | tests.cpp:53:19:53:19 | VariableAddress [post update] |
|
||||
| tests2.cpp:20:17:20:31 | SAXParser output argument | tests2.cpp:22:2:22:2 | p |
|
||||
| tests2.cpp:33:17:33:31 | SAXParser output argument | tests2.cpp:37:2:37:2 | p |
|
||||
| tests3.cpp:23:21:23:53 | call to createXMLReader | tests3.cpp:25:2:25:2 | p |
|
||||
| tests3.cpp:60:21:60:53 | call to createXMLReader | tests3.cpp:63:2:63:2 | p |
|
||||
| tests3.cpp:67:21:67:53 | call to createXMLReader | tests3.cpp:70:2:70:2 | p |
|
||||
| tests5.cpp:27:25:27:38 | call to createLSParser | tests5.cpp:29:2:29:2 | p |
|
||||
| tests5.cpp:40:25:40:38 | call to createLSParser | tests5.cpp:43:2:43:2 | p |
|
||||
| tests5.cpp:55:25:55:38 | call to createLSParser | tests5.cpp:59:2:59:2 | p |
|
||||
| tests5.cpp:81:25:81:38 | call to createLSParser | tests5.cpp:83:2:83:2 | p |
|
||||
| tests5.cpp:81:25:81:38 | call to createLSParser | tests5.cpp:83:2:83:2 | p |
|
||||
| tests5.cpp:83:2:83:2 | p | tests5.cpp:85:2:85:2 | p |
|
||||
| tests5.cpp:85:2:85:2 | p | tests5.cpp:86:2:86:2 | p |
|
||||
| tests5.cpp:86:2:86:2 | p | tests5.cpp:88:2:88:2 | p |
|
||||
| tests5.cpp:88:2:88:2 | p | tests5.cpp:89:2:89:2 | p |
|
||||
| tests.cpp:15:23:15:43 | XercesDOMParser output argument | tests.cpp:17:2:17:2 | p |
|
||||
| tests.cpp:28:23:28:43 | XercesDOMParser output argument | tests.cpp:31:2:31:2 | p |
|
||||
| tests.cpp:35:19:35:19 | VariableAddress [post update] | tests.cpp:37:2:37:2 | p |
|
||||
| tests.cpp:35:23:35:43 | XercesDOMParser output argument | tests.cpp:35:19:35:19 | VariableAddress [post update] |
|
||||
| tests.cpp:37:2:37:2 | p | tests.cpp:38:2:38:2 | p |
|
||||
| tests.cpp:38:2:38:2 | p | tests.cpp:39:2:39:2 | p |
|
||||
| tests.cpp:51:19:51:19 | VariableAddress [post update] | tests.cpp:53:2:53:2 | p |
|
||||
| tests.cpp:51:23:51:43 | XercesDOMParser output argument | tests.cpp:51:19:51:19 | VariableAddress [post update] |
|
||||
| tests.cpp:53:2:53:2 | p | tests.cpp:54:2:54:2 | p |
|
||||
| tests.cpp:54:2:54:2 | p | tests.cpp:55:2:55:2 | p |
|
||||
| tests.cpp:55:2:55:2 | p | tests.cpp:56:2:56:2 | p |
|
||||
| tests.cpp:55:2:55:2 | p | tests.cpp:56:2:56:2 | p |
|
||||
| tests.cpp:56:2:56:2 | p | tests.cpp:57:2:57:2 | p |
|
||||
| tests.cpp:69:19:69:19 | VariableAddress [post update] | tests.cpp:71:2:71:2 | p |
|
||||
| tests.cpp:69:23:69:43 | XercesDOMParser output argument | tests.cpp:69:19:69:19 | VariableAddress [post update] |
|
||||
| tests.cpp:71:2:71:2 | p | tests.cpp:72:2:72:2 | p |
|
||||
| tests.cpp:72:2:72:2 | p | tests.cpp:73:2:73:2 | p |
|
||||
| tests.cpp:73:2:73:2 | p | tests.cpp:74:2:74:2 | p |
|
||||
| tests.cpp:73:2:73:2 | p | tests.cpp:74:2:74:2 | p |
|
||||
| tests.cpp:74:2:74:2 | p | tests.cpp:75:2:75:2 | p |
|
||||
| tests.cpp:75:2:75:2 | p | tests.cpp:76:2:76:2 | p |
|
||||
| tests.cpp:76:2:76:2 | p | tests.cpp:77:2:77:2 | p |
|
||||
| tests.cpp:77:2:77:2 | p | tests.cpp:78:2:78:2 | p |
|
||||
| tests.cpp:84:23:84:43 | XercesDOMParser output argument | tests.cpp:87:2:87:2 | p |
|
||||
| tests.cpp:91:23:91:43 | XercesDOMParser output argument | tests.cpp:98:2:98:2 | p |
|
||||
| tests.cpp:103:24:103:44 | XercesDOMParser output argument | tests.cpp:106:3:106:3 | q |
|
||||
| tests.cpp:118:24:118:44 | XercesDOMParser output argument | tests.cpp:122:3:122:3 | q |
|
||||
| tests.cpp:130:39:130:39 | p | tests.cpp:131:2:131:2 | p |
|
||||
| tests.cpp:134:39:134:39 | p | tests.cpp:135:2:135:2 | p |
|
||||
| tests.cpp:140:23:140:43 | XercesDOMParser output argument | tests.cpp:144:18:144:18 | q |
|
||||
| tests.cpp:140:23:140:43 | XercesDOMParser output argument | tests.cpp:146:18:146:18 | q |
|
||||
| tests.cpp:144:18:144:18 | q | tests.cpp:130:39:130:39 | p |
|
||||
| tests.cpp:146:18:146:18 | q | tests.cpp:134:39:134:39 | p |
|
||||
| tests.cpp:150:19:150:32 | call to createLSParser | tests.cpp:152:2:152:2 | p |
|
||||
| tests.cpp:57:2:57:2 | p | tests.cpp:58:2:58:2 | p |
|
||||
| tests.cpp:58:2:58:2 | p | tests.cpp:59:2:59:2 | p |
|
||||
| tests.cpp:59:2:59:2 | p | tests.cpp:60:2:60:2 | p |
|
||||
| tests.cpp:66:23:66:43 | XercesDOMParser output argument | tests.cpp:69:2:69:2 | p |
|
||||
| tests.cpp:73:23:73:43 | XercesDOMParser output argument | tests.cpp:80:2:80:2 | p |
|
||||
| tests.cpp:85:24:85:44 | XercesDOMParser output argument | tests.cpp:88:3:88:3 | q |
|
||||
| tests.cpp:100:24:100:44 | XercesDOMParser output argument | tests.cpp:104:3:104:3 | q |
|
||||
| tests.cpp:112:39:112:39 | p | tests.cpp:113:2:113:2 | p |
|
||||
| tests.cpp:116:39:116:39 | p | tests.cpp:117:2:117:2 | p |
|
||||
| tests.cpp:122:23:122:43 | XercesDOMParser output argument | tests.cpp:126:18:126:18 | q |
|
||||
| tests.cpp:122:23:122:43 | XercesDOMParser output argument | tests.cpp:128:18:128:18 | q |
|
||||
| tests.cpp:126:18:126:18 | q | tests.cpp:112:39:112:39 | p |
|
||||
| tests.cpp:128:18:128:18 | q | tests.cpp:116:39:116:39 | p |
|
||||
nodes
|
||||
| tests.cpp:33:23:33:43 | XercesDOMParser output argument | semmle.label | XercesDOMParser output argument |
|
||||
| tests.cpp:35:2:35:2 | p | semmle.label | p |
|
||||
| tests.cpp:46:23:46:43 | XercesDOMParser output argument | semmle.label | XercesDOMParser output argument |
|
||||
| tests.cpp:49:2:49:2 | p | semmle.label | p |
|
||||
| tests.cpp:53:19:53:19 | VariableAddress [post update] | semmle.label | VariableAddress [post update] |
|
||||
| tests.cpp:53:23:53:43 | XercesDOMParser output argument | semmle.label | XercesDOMParser output argument |
|
||||
| tests2.cpp:20:17:20:31 | SAXParser output argument | semmle.label | SAXParser output argument |
|
||||
| tests2.cpp:22:2:22:2 | p | semmle.label | p |
|
||||
| tests2.cpp:33:17:33:31 | SAXParser output argument | semmle.label | SAXParser output argument |
|
||||
| tests2.cpp:37:2:37:2 | p | semmle.label | p |
|
||||
| tests3.cpp:23:21:23:53 | call to createXMLReader | semmle.label | call to createXMLReader |
|
||||
| tests3.cpp:25:2:25:2 | p | semmle.label | p |
|
||||
| tests3.cpp:60:21:60:53 | call to createXMLReader | semmle.label | call to createXMLReader |
|
||||
| tests3.cpp:63:2:63:2 | p | semmle.label | p |
|
||||
| tests3.cpp:67:21:67:53 | call to createXMLReader | semmle.label | call to createXMLReader |
|
||||
| tests3.cpp:70:2:70:2 | p | semmle.label | p |
|
||||
| tests4.cpp:26:34:26:48 | (int)... | semmle.label | (int)... |
|
||||
| tests4.cpp:36:34:36:50 | (int)... | semmle.label | (int)... |
|
||||
| tests4.cpp:46:34:46:68 | ... \| ... | semmle.label | ... \| ... |
|
||||
| tests4.cpp:77:34:77:38 | flags | semmle.label | flags |
|
||||
| tests4.cpp:130:39:130:55 | (int)... | semmle.label | (int)... |
|
||||
| tests5.cpp:27:25:27:38 | call to createLSParser | semmle.label | call to createLSParser |
|
||||
| tests5.cpp:29:2:29:2 | p | semmle.label | p |
|
||||
| tests5.cpp:40:25:40:38 | call to createLSParser | semmle.label | call to createLSParser |
|
||||
| tests5.cpp:43:2:43:2 | p | semmle.label | p |
|
||||
| tests5.cpp:55:25:55:38 | call to createLSParser | semmle.label | call to createLSParser |
|
||||
| tests5.cpp:59:2:59:2 | p | semmle.label | p |
|
||||
| tests5.cpp:81:25:81:38 | call to createLSParser | semmle.label | call to createLSParser |
|
||||
| tests5.cpp:83:2:83:2 | p | semmle.label | p |
|
||||
| tests5.cpp:83:2:83:2 | p | semmle.label | p |
|
||||
| tests5.cpp:85:2:85:2 | p | semmle.label | p |
|
||||
| tests5.cpp:86:2:86:2 | p | semmle.label | p |
|
||||
| tests5.cpp:88:2:88:2 | p | semmle.label | p |
|
||||
| tests5.cpp:89:2:89:2 | p | semmle.label | p |
|
||||
| tests.cpp:15:23:15:43 | XercesDOMParser output argument | semmle.label | XercesDOMParser output argument |
|
||||
| tests.cpp:17:2:17:2 | p | semmle.label | p |
|
||||
| tests.cpp:28:23:28:43 | XercesDOMParser output argument | semmle.label | XercesDOMParser output argument |
|
||||
| tests.cpp:31:2:31:2 | p | semmle.label | p |
|
||||
| tests.cpp:35:19:35:19 | VariableAddress [post update] | semmle.label | VariableAddress [post update] |
|
||||
| tests.cpp:35:23:35:43 | XercesDOMParser output argument | semmle.label | XercesDOMParser output argument |
|
||||
| tests.cpp:37:2:37:2 | p | semmle.label | p |
|
||||
| tests.cpp:38:2:38:2 | p | semmle.label | p |
|
||||
| tests.cpp:39:2:39:2 | p | semmle.label | p |
|
||||
| tests.cpp:51:19:51:19 | VariableAddress [post update] | semmle.label | VariableAddress [post update] |
|
||||
| tests.cpp:51:23:51:43 | XercesDOMParser output argument | semmle.label | XercesDOMParser output argument |
|
||||
| tests.cpp:53:2:53:2 | p | semmle.label | p |
|
||||
| tests.cpp:54:2:54:2 | p | semmle.label | p |
|
||||
| tests.cpp:55:2:55:2 | p | semmle.label | p |
|
||||
| tests.cpp:56:2:56:2 | p | semmle.label | p |
|
||||
| tests.cpp:56:2:56:2 | p | semmle.label | p |
|
||||
| tests.cpp:57:2:57:2 | p | semmle.label | p |
|
||||
| tests.cpp:69:19:69:19 | VariableAddress [post update] | semmle.label | VariableAddress [post update] |
|
||||
| tests.cpp:69:23:69:43 | XercesDOMParser output argument | semmle.label | XercesDOMParser output argument |
|
||||
| tests.cpp:71:2:71:2 | p | semmle.label | p |
|
||||
| tests.cpp:72:2:72:2 | p | semmle.label | p |
|
||||
| tests.cpp:73:2:73:2 | p | semmle.label | p |
|
||||
| tests.cpp:74:2:74:2 | p | semmle.label | p |
|
||||
| tests.cpp:74:2:74:2 | p | semmle.label | p |
|
||||
| tests.cpp:75:2:75:2 | p | semmle.label | p |
|
||||
| tests.cpp:76:2:76:2 | p | semmle.label | p |
|
||||
| tests.cpp:77:2:77:2 | p | semmle.label | p |
|
||||
| tests.cpp:78:2:78:2 | p | semmle.label | p |
|
||||
| tests.cpp:84:23:84:43 | XercesDOMParser output argument | semmle.label | XercesDOMParser output argument |
|
||||
| tests.cpp:87:2:87:2 | p | semmle.label | p |
|
||||
| tests.cpp:91:23:91:43 | XercesDOMParser output argument | semmle.label | XercesDOMParser output argument |
|
||||
| tests.cpp:98:2:98:2 | p | semmle.label | p |
|
||||
| tests.cpp:103:24:103:44 | XercesDOMParser output argument | semmle.label | XercesDOMParser output argument |
|
||||
| tests.cpp:106:3:106:3 | q | semmle.label | q |
|
||||
| tests.cpp:118:24:118:44 | XercesDOMParser output argument | semmle.label | XercesDOMParser output argument |
|
||||
| tests.cpp:122:3:122:3 | q | semmle.label | q |
|
||||
| tests.cpp:130:39:130:39 | p | semmle.label | p |
|
||||
| tests.cpp:131:2:131:2 | p | semmle.label | p |
|
||||
| tests.cpp:134:39:134:39 | p | semmle.label | p |
|
||||
| tests.cpp:135:2:135:2 | p | semmle.label | p |
|
||||
| tests.cpp:140:23:140:43 | XercesDOMParser output argument | semmle.label | XercesDOMParser output argument |
|
||||
| tests.cpp:144:18:144:18 | q | semmle.label | q |
|
||||
| tests.cpp:146:18:146:18 | q | semmle.label | q |
|
||||
| tests.cpp:150:19:150:32 | call to createLSParser | semmle.label | call to createLSParser |
|
||||
| tests.cpp:152:2:152:2 | p | semmle.label | p |
|
||||
| tests.cpp:58:2:58:2 | p | semmle.label | p |
|
||||
| tests.cpp:59:2:59:2 | p | semmle.label | p |
|
||||
| tests.cpp:60:2:60:2 | p | semmle.label | p |
|
||||
| tests.cpp:66:23:66:43 | XercesDOMParser output argument | semmle.label | XercesDOMParser output argument |
|
||||
| tests.cpp:69:2:69:2 | p | semmle.label | p |
|
||||
| tests.cpp:73:23:73:43 | XercesDOMParser output argument | semmle.label | XercesDOMParser output argument |
|
||||
| tests.cpp:80:2:80:2 | p | semmle.label | p |
|
||||
| tests.cpp:85:24:85:44 | XercesDOMParser output argument | semmle.label | XercesDOMParser output argument |
|
||||
| tests.cpp:88:3:88:3 | q | semmle.label | q |
|
||||
| tests.cpp:100:24:100:44 | XercesDOMParser output argument | semmle.label | XercesDOMParser output argument |
|
||||
| tests.cpp:104:3:104:3 | q | semmle.label | q |
|
||||
| tests.cpp:112:39:112:39 | p | semmle.label | p |
|
||||
| tests.cpp:113:2:113:2 | p | semmle.label | p |
|
||||
| tests.cpp:116:39:116:39 | p | semmle.label | p |
|
||||
| tests.cpp:117:2:117:2 | p | semmle.label | p |
|
||||
| tests.cpp:122:23:122:43 | XercesDOMParser output argument | semmle.label | XercesDOMParser output argument |
|
||||
| tests.cpp:126:18:126:18 | q | semmle.label | q |
|
||||
| tests.cpp:128:18:128:18 | q | semmle.label | q |
|
||||
subpaths
|
||||
#select
|
||||
| tests.cpp:35:2:35:2 | p | tests.cpp:33:23:33:43 | XercesDOMParser output argument | tests.cpp:35:2:35:2 | p | This $@ is not configured to prevent an XML external entity (XXE) attack. | tests.cpp:33:23:33:43 | XercesDOMParser output argument | XML parser |
|
||||
| tests.cpp:49:2:49:2 | p | tests.cpp:46:23:46:43 | XercesDOMParser output argument | tests.cpp:49:2:49:2 | p | This $@ is not configured to prevent an XML external entity (XXE) attack. | tests.cpp:46:23:46:43 | XercesDOMParser output argument | XML parser |
|
||||
| tests.cpp:57:2:57:2 | p | tests.cpp:53:23:53:43 | XercesDOMParser output argument | tests.cpp:57:2:57:2 | p | This $@ is not configured to prevent an XML external entity (XXE) attack. | tests.cpp:53:23:53:43 | XercesDOMParser output argument | XML parser |
|
||||
| tests.cpp:74:2:74:2 | p | tests.cpp:69:23:69:43 | XercesDOMParser output argument | tests.cpp:74:2:74:2 | p | This $@ is not configured to prevent an XML external entity (XXE) attack. | tests.cpp:69:23:69:43 | XercesDOMParser output argument | XML parser |
|
||||
| tests.cpp:78:2:78:2 | p | tests.cpp:69:23:69:43 | XercesDOMParser output argument | tests.cpp:78:2:78:2 | p | This $@ is not configured to prevent an XML external entity (XXE) attack. | tests.cpp:69:23:69:43 | XercesDOMParser output argument | XML parser |
|
||||
| tests.cpp:87:2:87:2 | p | tests.cpp:84:23:84:43 | XercesDOMParser output argument | tests.cpp:87:2:87:2 | p | This $@ is not configured to prevent an XML external entity (XXE) attack. | tests.cpp:84:23:84:43 | XercesDOMParser output argument | XML parser |
|
||||
| tests.cpp:98:2:98:2 | p | tests.cpp:91:23:91:43 | XercesDOMParser output argument | tests.cpp:98:2:98:2 | p | This $@ is not configured to prevent an XML external entity (XXE) attack. | tests.cpp:91:23:91:43 | XercesDOMParser output argument | XML parser |
|
||||
| tests.cpp:106:3:106:3 | q | tests.cpp:103:24:103:44 | XercesDOMParser output argument | tests.cpp:106:3:106:3 | q | This $@ is not configured to prevent an XML external entity (XXE) attack. | tests.cpp:103:24:103:44 | XercesDOMParser output argument | XML parser |
|
||||
| tests.cpp:122:3:122:3 | q | tests.cpp:118:24:118:44 | XercesDOMParser output argument | tests.cpp:122:3:122:3 | q | This $@ is not configured to prevent an XML external entity (XXE) attack. | tests.cpp:118:24:118:44 | XercesDOMParser output argument | XML parser |
|
||||
| tests.cpp:131:2:131:2 | p | tests.cpp:140:23:140:43 | XercesDOMParser output argument | tests.cpp:131:2:131:2 | p | This $@ is not configured to prevent an XML external entity (XXE) attack. | tests.cpp:140:23:140:43 | XercesDOMParser output argument | XML parser |
|
||||
| tests.cpp:135:2:135:2 | p | tests.cpp:140:23:140:43 | XercesDOMParser output argument | tests.cpp:135:2:135:2 | p | This $@ is not configured to prevent an XML external entity (XXE) attack. | tests.cpp:140:23:140:43 | XercesDOMParser output argument | XML parser |
|
||||
| tests.cpp:152:2:152:2 | p | tests.cpp:150:19:150:32 | call to createLSParser | tests.cpp:152:2:152:2 | p | This $@ is not configured to prevent an XML external entity (XXE) attack. | tests.cpp:150:19:150:32 | call to createLSParser | XML parser |
|
||||
| tests2.cpp:22:2:22:2 | p | tests2.cpp:20:17:20:31 | SAXParser output argument | tests2.cpp:22:2:22:2 | p | This $@ is not configured to prevent an XML external entity (XXE) attack. | tests2.cpp:20:17:20:31 | SAXParser output argument | XML parser |
|
||||
| tests2.cpp:37:2:37:2 | p | tests2.cpp:33:17:33:31 | SAXParser output argument | tests2.cpp:37:2:37:2 | p | This $@ is not configured to prevent an XML external entity (XXE) attack. | tests2.cpp:33:17:33:31 | SAXParser output argument | XML parser |
|
||||
| tests3.cpp:25:2:25:2 | p | tests3.cpp:23:21:23:53 | call to createXMLReader | tests3.cpp:25:2:25:2 | p | This $@ is not configured to prevent an XML external entity (XXE) attack. | tests3.cpp:23:21:23:53 | call to createXMLReader | XML parser |
|
||||
| tests3.cpp:63:2:63:2 | p | tests3.cpp:60:21:60:53 | call to createXMLReader | tests3.cpp:63:2:63:2 | p | This $@ is not configured to prevent an XML external entity (XXE) attack. | tests3.cpp:60:21:60:53 | call to createXMLReader | XML parser |
|
||||
| tests3.cpp:70:2:70:2 | p | tests3.cpp:67:21:67:53 | call to createXMLReader | tests3.cpp:70:2:70:2 | p | This $@ is not configured to prevent an XML external entity (XXE) attack. | tests3.cpp:67:21:67:53 | call to createXMLReader | XML parser |
|
||||
| tests4.cpp:26:34:26:48 | (int)... | tests4.cpp:26:34:26:48 | (int)... | tests4.cpp:26:34:26:48 | (int)... | This $@ is not configured to prevent an XML external entity (XXE) attack. | tests4.cpp:26:34:26:48 | (int)... | XML parser |
|
||||
| tests4.cpp:36:34:36:50 | (int)... | tests4.cpp:36:34:36:50 | (int)... | tests4.cpp:36:34:36:50 | (int)... | This $@ is not configured to prevent an XML external entity (XXE) attack. | tests4.cpp:36:34:36:50 | (int)... | XML parser |
|
||||
| tests4.cpp:46:34:46:68 | ... \| ... | tests4.cpp:46:34:46:68 | ... \| ... | tests4.cpp:46:34:46:68 | ... \| ... | This $@ is not configured to prevent an XML external entity (XXE) attack. | tests4.cpp:46:34:46:68 | ... \| ... | XML parser |
|
||||
| tests4.cpp:77:34:77:38 | flags | tests4.cpp:77:34:77:38 | flags | tests4.cpp:77:34:77:38 | flags | This $@ is not configured to prevent an XML external entity (XXE) attack. | tests4.cpp:77:34:77:38 | flags | XML parser |
|
||||
| tests4.cpp:130:39:130:55 | (int)... | tests4.cpp:130:39:130:55 | (int)... | tests4.cpp:130:39:130:55 | (int)... | This $@ is not configured to prevent an XML external entity (XXE) attack. | tests4.cpp:130:39:130:55 | (int)... | XML parser |
|
||||
| tests5.cpp:29:2:29:2 | p | tests5.cpp:27:25:27:38 | call to createLSParser | tests5.cpp:29:2:29:2 | p | This $@ is not configured to prevent an XML external entity (XXE) attack. | tests5.cpp:27:25:27:38 | call to createLSParser | XML parser |
|
||||
| tests5.cpp:43:2:43:2 | p | tests5.cpp:40:25:40:38 | call to createLSParser | tests5.cpp:43:2:43:2 | p | This $@ is not configured to prevent an XML external entity (XXE) attack. | tests5.cpp:40:25:40:38 | call to createLSParser | XML parser |
|
||||
| tests5.cpp:59:2:59:2 | p | tests5.cpp:55:25:55:38 | call to createLSParser | tests5.cpp:59:2:59:2 | p | This $@ is not configured to prevent an XML external entity (XXE) attack. | tests5.cpp:55:25:55:38 | call to createLSParser | XML parser |
|
||||
| tests5.cpp:83:2:83:2 | p | tests5.cpp:81:25:81:38 | call to createLSParser | tests5.cpp:83:2:83:2 | p | This $@ is not configured to prevent an XML external entity (XXE) attack. | tests5.cpp:81:25:81:38 | call to createLSParser | XML parser |
|
||||
| tests5.cpp:89:2:89:2 | p | tests5.cpp:81:25:81:38 | call to createLSParser | tests5.cpp:89:2:89:2 | p | This $@ is not configured to prevent an XML external entity (XXE) attack. | tests5.cpp:81:25:81:38 | call to createLSParser | XML parser |
|
||||
| tests.cpp:17:2:17:2 | p | tests.cpp:15:23:15:43 | XercesDOMParser output argument | tests.cpp:17:2:17:2 | p | This $@ is not configured to prevent an XML external entity (XXE) attack. | tests.cpp:15:23:15:43 | XercesDOMParser output argument | XML parser |
|
||||
| tests.cpp:31:2:31:2 | p | tests.cpp:28:23:28:43 | XercesDOMParser output argument | tests.cpp:31:2:31:2 | p | This $@ is not configured to prevent an XML external entity (XXE) attack. | tests.cpp:28:23:28:43 | XercesDOMParser output argument | XML parser |
|
||||
| tests.cpp:39:2:39:2 | p | tests.cpp:35:23:35:43 | XercesDOMParser output argument | tests.cpp:39:2:39:2 | p | This $@ is not configured to prevent an XML external entity (XXE) attack. | tests.cpp:35:23:35:43 | XercesDOMParser output argument | XML parser |
|
||||
| tests.cpp:56:2:56:2 | p | tests.cpp:51:23:51:43 | XercesDOMParser output argument | tests.cpp:56:2:56:2 | p | This $@ is not configured to prevent an XML external entity (XXE) attack. | tests.cpp:51:23:51:43 | XercesDOMParser output argument | XML parser |
|
||||
| tests.cpp:60:2:60:2 | p | tests.cpp:51:23:51:43 | XercesDOMParser output argument | tests.cpp:60:2:60:2 | p | This $@ is not configured to prevent an XML external entity (XXE) attack. | tests.cpp:51:23:51:43 | XercesDOMParser output argument | XML parser |
|
||||
| tests.cpp:69:2:69:2 | p | tests.cpp:66:23:66:43 | XercesDOMParser output argument | tests.cpp:69:2:69:2 | p | This $@ is not configured to prevent an XML external entity (XXE) attack. | tests.cpp:66:23:66:43 | XercesDOMParser output argument | XML parser |
|
||||
| tests.cpp:80:2:80:2 | p | tests.cpp:73:23:73:43 | XercesDOMParser output argument | tests.cpp:80:2:80:2 | p | This $@ is not configured to prevent an XML external entity (XXE) attack. | tests.cpp:73:23:73:43 | XercesDOMParser output argument | XML parser |
|
||||
| tests.cpp:88:3:88:3 | q | tests.cpp:85:24:85:44 | XercesDOMParser output argument | tests.cpp:88:3:88:3 | q | This $@ is not configured to prevent an XML external entity (XXE) attack. | tests.cpp:85:24:85:44 | XercesDOMParser output argument | XML parser |
|
||||
| tests.cpp:104:3:104:3 | q | tests.cpp:100:24:100:44 | XercesDOMParser output argument | tests.cpp:104:3:104:3 | q | This $@ is not configured to prevent an XML external entity (XXE) attack. | tests.cpp:100:24:100:44 | XercesDOMParser output argument | XML parser |
|
||||
| tests.cpp:113:2:113:2 | p | tests.cpp:122:23:122:43 | XercesDOMParser output argument | tests.cpp:113:2:113:2 | p | This $@ is not configured to prevent an XML external entity (XXE) attack. | tests.cpp:122:23:122:43 | XercesDOMParser output argument | XML parser |
|
||||
| tests.cpp:117:2:117:2 | p | tests.cpp:122:23:122:43 | XercesDOMParser output argument | tests.cpp:117:2:117:2 | p | This $@ is not configured to prevent an XML external entity (XXE) attack. | tests.cpp:122:23:122:43 | XercesDOMParser output argument | XML parser |
|
||||
|
||||
@@ -1,32 +1,14 @@
|
||||
// test cases for rule CWE-611
|
||||
// test cases for rule CWE-611 (XercesDOMParser)
|
||||
|
||||
#include "tests.h"
|
||||
|
||||
// ---
|
||||
|
||||
class SecurityManager;
|
||||
class InputSource;
|
||||
|
||||
class AbstractDOMParser {
|
||||
public:
|
||||
AbstractDOMParser();
|
||||
|
||||
void setDisableDefaultEntityResolution(bool); // default is false
|
||||
void setCreateEntityReferenceNodes(bool); // default is true
|
||||
void setSecurityManager(SecurityManager *const manager);
|
||||
void parse(const InputSource &data);
|
||||
};
|
||||
|
||||
class XercesDOMParser: public AbstractDOMParser {
|
||||
public:
|
||||
XercesDOMParser();
|
||||
};
|
||||
|
||||
class DOMLSParser : public AbstractDOMParser {
|
||||
};
|
||||
|
||||
DOMLSParser *createLSParser();
|
||||
|
||||
// ---
|
||||
|
||||
void test1(InputSource &data) {
|
||||
@@ -145,26 +127,3 @@ void test10(InputSource &data) {
|
||||
test10_doParseC(p, data);
|
||||
test10_doParseC(q, data);
|
||||
}
|
||||
|
||||
void test11(InputSource &data) {
|
||||
DOMLSParser *p = createLSParser();
|
||||
|
||||
p->parse(data); // BAD (parser not correctly configured)
|
||||
}
|
||||
|
||||
void test12(InputSource &data) {
|
||||
DOMLSParser *p = createLSParser();
|
||||
|
||||
p->setDisableDefaultEntityResolution(true);
|
||||
p->parse(data); // GOOD
|
||||
}
|
||||
|
||||
DOMLSParser *g_p1 = createLSParser();
|
||||
DOMLSParser *g_p2 = createLSParser();
|
||||
InputSource *g_data;
|
||||
|
||||
void test13() {
|
||||
g_p1->setDisableDefaultEntityResolution(true);
|
||||
g_p1->parse(*g_data); // GOOD
|
||||
g_p2->parse(*g_data); // BAD (parser not correctly configured) [NOT DETECTED]
|
||||
}
|
||||
|
||||
@@ -1,2 +1,26 @@
|
||||
// library functions for rule CWE-611
|
||||
// library/common functions for rule CWE-611
|
||||
|
||||
#define NULL (0)
|
||||
|
||||
class SecurityManager;
|
||||
class InputSource;
|
||||
|
||||
class AbstractDOMParser {
|
||||
public:
|
||||
AbstractDOMParser();
|
||||
|
||||
void setDisableDefaultEntityResolution(bool); // default is false
|
||||
void setCreateEntityReferenceNodes(bool); // default is true
|
||||
void setSecurityManager(SecurityManager *const manager);
|
||||
void parse(const InputSource &data);
|
||||
};
|
||||
|
||||
typedef unsigned int XMLCh;
|
||||
|
||||
class XMLUni
|
||||
{
|
||||
public:
|
||||
static const XMLCh fgXercesDisableDefaultEntityResolution[];
|
||||
static const XMLCh fgXercesHarmlessOption[];
|
||||
};
|
||||
|
||||
|
||||
46
cpp/ql/test/query-tests/Security/CWE/CWE-611/tests2.cpp
Normal file
46
cpp/ql/test/query-tests/Security/CWE/CWE-611/tests2.cpp
Normal file
@@ -0,0 +1,46 @@
|
||||
// test cases for rule CWE-611 (SAXParser)
|
||||
|
||||
#include "tests.h"
|
||||
|
||||
// ---
|
||||
|
||||
class SAXParser
|
||||
{
|
||||
public:
|
||||
SAXParser();
|
||||
|
||||
void setDisableDefaultEntityResolution(bool); // default is false
|
||||
void setSecurityManager(SecurityManager *const manager);
|
||||
void parse(const InputSource &data);
|
||||
};
|
||||
|
||||
// ---
|
||||
|
||||
void test2_1(InputSource &data) {
|
||||
SAXParser *p = new SAXParser();
|
||||
|
||||
p->parse(data); // BAD (parser not correctly configured)
|
||||
}
|
||||
|
||||
void test2_2(InputSource &data) {
|
||||
SAXParser *p = new SAXParser();
|
||||
|
||||
p->setDisableDefaultEntityResolution(true);
|
||||
p->parse(data); // GOOD
|
||||
}
|
||||
|
||||
void test2_3(InputSource &data) {
|
||||
SAXParser *p = new SAXParser();
|
||||
bool v = false;
|
||||
|
||||
p->setDisableDefaultEntityResolution(v);
|
||||
p->parse(data); // BAD (parser not correctly configured)
|
||||
}
|
||||
|
||||
void test2_4(InputSource &data) {
|
||||
SAXParser *p = new SAXParser();
|
||||
bool v = true;
|
||||
|
||||
p->setDisableDefaultEntityResolution(v);
|
||||
p->parse(data); // GOOD
|
||||
}
|
||||
82
cpp/ql/test/query-tests/Security/CWE/CWE-611/tests3.cpp
Normal file
82
cpp/ql/test/query-tests/Security/CWE/CWE-611/tests3.cpp
Normal file
@@ -0,0 +1,82 @@
|
||||
// test cases for rule CWE-611 (SAX2XMLReader)
|
||||
|
||||
#include "tests.h"
|
||||
|
||||
// ---
|
||||
|
||||
class SAX2XMLReader
|
||||
{
|
||||
public:
|
||||
void setFeature(const XMLCh *feature, bool value);
|
||||
void parse(const InputSource &data);
|
||||
};
|
||||
|
||||
class XMLReaderFactory
|
||||
{
|
||||
public:
|
||||
static SAX2XMLReader *createXMLReader();
|
||||
};
|
||||
|
||||
// ---
|
||||
|
||||
void test3_1(InputSource &data) {
|
||||
SAX2XMLReader *p = XMLReaderFactory::createXMLReader();
|
||||
|
||||
p->parse(data); // BAD (parser not correctly configured) [NOT DETECTED]
|
||||
}
|
||||
|
||||
void test3_2(InputSource &data) {
|
||||
SAX2XMLReader *p = XMLReaderFactory::createXMLReader();
|
||||
|
||||
p->setFeature(XMLUni::fgXercesDisableDefaultEntityResolution, true);
|
||||
p->parse(data); // GOOD
|
||||
}
|
||||
|
||||
SAX2XMLReader *p_3_3 = XMLReaderFactory::createXMLReader();
|
||||
|
||||
void test3_3(InputSource &data) {
|
||||
p_3_3->parse(data); // BAD (parser not correctly configured) [NOT DETECTED]
|
||||
}
|
||||
|
||||
SAX2XMLReader *p_3_4 = XMLReaderFactory::createXMLReader();
|
||||
|
||||
void test3_4(InputSource &data) {
|
||||
p_3_4->setFeature(XMLUni::fgXercesDisableDefaultEntityResolution, true);
|
||||
p_3_4->parse(data); // GOOD
|
||||
}
|
||||
|
||||
SAX2XMLReader *p_3_5 = XMLReaderFactory::createXMLReader();
|
||||
|
||||
void test3_5_init() {
|
||||
p_3_5->setFeature(XMLUni::fgXercesDisableDefaultEntityResolution, true);
|
||||
}
|
||||
|
||||
void test3_5(InputSource &data) {
|
||||
test3_5_init();
|
||||
p_3_5->parse(data); // GOOD
|
||||
}
|
||||
|
||||
void test3_6(InputSource &data) {
|
||||
SAX2XMLReader *p = XMLReaderFactory::createXMLReader();
|
||||
|
||||
p->setFeature(XMLUni::fgXercesDisableDefaultEntityResolution, false);
|
||||
p->parse(data); // BAD (parser not correctly configured)
|
||||
}
|
||||
|
||||
void test3_7(InputSource &data) {
|
||||
SAX2XMLReader *p = XMLReaderFactory::createXMLReader();
|
||||
|
||||
p->setFeature(XMLUni::fgXercesHarmlessOption, true);
|
||||
p->parse(data); // BAD (parser not correctly configured)
|
||||
}
|
||||
|
||||
void test3_8(InputSource &data) {
|
||||
SAX2XMLReader *p = XMLReaderFactory::createXMLReader();
|
||||
const XMLCh *feature = XMLUni::fgXercesDisableDefaultEntityResolution;
|
||||
|
||||
p->setFeature(feature, true);
|
||||
p->parse(data); // GOOD
|
||||
}
|
||||
|
||||
|
||||
|
||||
135
cpp/ql/test/query-tests/Security/CWE/CWE-611/tests4.cpp
Normal file
135
cpp/ql/test/query-tests/Security/CWE/CWE-611/tests4.cpp
Normal file
@@ -0,0 +1,135 @@
|
||||
// test cases for rule CWE-611 (libxml2)
|
||||
|
||||
#include "tests.h"
|
||||
|
||||
// ---
|
||||
|
||||
enum xmlParserOption
|
||||
{
|
||||
XML_PARSE_NOENT = 2,
|
||||
XML_PARSE_DTDLOAD = 4,
|
||||
XML_PARSE_OPTION_HARMLESS = 8
|
||||
};
|
||||
|
||||
class xmlDoc;
|
||||
|
||||
xmlDoc *xmlReadFile(const char *fileName, const char *encoding, int flags);
|
||||
xmlDoc *xmlReadMemory(const char *ptr, int sz, const char *url, const char *encoding, int flags);
|
||||
|
||||
void xmlFreeDoc(xmlDoc *ptr);
|
||||
|
||||
// ---
|
||||
|
||||
void test4_1(const char *fileName) {
|
||||
xmlDoc *p;
|
||||
|
||||
p = xmlReadFile(fileName, NULL, XML_PARSE_NOENT); // BAD (parser not correctly configured)
|
||||
if (p != NULL)
|
||||
{
|
||||
xmlFreeDoc(p);
|
||||
}
|
||||
}
|
||||
|
||||
void test4_2(const char *fileName) {
|
||||
xmlDoc *p;
|
||||
|
||||
p = xmlReadFile(fileName, NULL, XML_PARSE_DTDLOAD); // BAD (parser not correctly configured)
|
||||
if (p != NULL)
|
||||
{
|
||||
xmlFreeDoc(p);
|
||||
}
|
||||
}
|
||||
|
||||
void test4_3(const char *fileName) {
|
||||
xmlDoc *p;
|
||||
|
||||
p = xmlReadFile(fileName, NULL, XML_PARSE_NOENT | XML_PARSE_DTDLOAD); // BAD (parser not correctly configured)
|
||||
if (p != NULL)
|
||||
{
|
||||
xmlFreeDoc(p);
|
||||
}
|
||||
}
|
||||
|
||||
void test4_4(const char *fileName) {
|
||||
xmlDoc *p;
|
||||
|
||||
p = xmlReadFile(fileName, NULL, 0); // GOOD
|
||||
if (p != NULL)
|
||||
{
|
||||
xmlFreeDoc(p);
|
||||
}
|
||||
}
|
||||
|
||||
void test4_5(const char *fileName) {
|
||||
xmlDoc *p;
|
||||
|
||||
p = xmlReadFile(fileName, NULL, XML_PARSE_OPTION_HARMLESS); // GOOD
|
||||
if (p != NULL)
|
||||
{
|
||||
xmlFreeDoc(p);
|
||||
}
|
||||
}
|
||||
|
||||
void test4_6(const char *fileName) {
|
||||
xmlDoc *p;
|
||||
int flags = XML_PARSE_NOENT;
|
||||
|
||||
p = xmlReadFile(fileName, NULL, flags); // BAD (parser not correctly configured)
|
||||
if (p != NULL)
|
||||
{
|
||||
xmlFreeDoc(p);
|
||||
}
|
||||
}
|
||||
|
||||
void test4_7(const char *fileName) {
|
||||
xmlDoc *p;
|
||||
int flags = 0;
|
||||
|
||||
p = xmlReadFile(fileName, NULL, flags); // GOOD
|
||||
if (p != NULL)
|
||||
{
|
||||
xmlFreeDoc(p);
|
||||
}
|
||||
}
|
||||
|
||||
void test4_8(const char *fileName) {
|
||||
xmlDoc *p;
|
||||
int flags = XML_PARSE_OPTION_HARMLESS;
|
||||
|
||||
p = xmlReadFile(fileName, NULL, flags | XML_PARSE_NOENT); // BAD (parser not correctly configured) [NOT DETECTED]
|
||||
if (p != NULL)
|
||||
{
|
||||
xmlFreeDoc(p);
|
||||
}
|
||||
}
|
||||
|
||||
void test4_9(const char *fileName) {
|
||||
xmlDoc *p;
|
||||
int flags = XML_PARSE_NOENT;
|
||||
|
||||
p = xmlReadFile(fileName, NULL, flags | XML_PARSE_OPTION_HARMLESS); // BAD (parser not correctly configured) [NOT DETECTED]
|
||||
if (p != NULL)
|
||||
{
|
||||
xmlFreeDoc(p);
|
||||
}
|
||||
}
|
||||
|
||||
void test4_10(const char *ptr, int sz) {
|
||||
xmlDoc *p;
|
||||
|
||||
p = xmlReadMemory(ptr, sz, "", NULL, 0); // GOOD
|
||||
if (p != NULL)
|
||||
{
|
||||
xmlFreeDoc(p);
|
||||
}
|
||||
}
|
||||
|
||||
void test4_11(const char *ptr, int sz) {
|
||||
xmlDoc *p;
|
||||
|
||||
p = xmlReadMemory(ptr, sz, "", NULL, XML_PARSE_DTDLOAD); // BAD (parser not correctly configured)
|
||||
if (p != NULL)
|
||||
{
|
||||
xmlFreeDoc(p);
|
||||
}
|
||||
}
|
||||
103
cpp/ql/test/query-tests/Security/CWE/CWE-611/tests5.cpp
Normal file
103
cpp/ql/test/query-tests/Security/CWE/CWE-611/tests5.cpp
Normal file
@@ -0,0 +1,103 @@
|
||||
// test cases for rule CWE-611 (createLSParser)
|
||||
|
||||
#include "tests.h"
|
||||
|
||||
// ---
|
||||
|
||||
class DOMConfiguration {
|
||||
public:
|
||||
void setParameter(const XMLCh *parameter, bool value);
|
||||
};
|
||||
|
||||
class DOMLSParser {
|
||||
public:
|
||||
DOMConfiguration *getDomConfig();
|
||||
|
||||
void parse(const InputSource &data);
|
||||
};
|
||||
|
||||
class DOMImplementationLS {
|
||||
public:
|
||||
DOMLSParser *createLSParser();
|
||||
};
|
||||
|
||||
// ---
|
||||
|
||||
void test5_1(DOMImplementationLS *impl, InputSource &data) {
|
||||
DOMLSParser *p = impl->createLSParser();
|
||||
|
||||
p->parse(data); // BAD (parser not correctly configured)
|
||||
}
|
||||
|
||||
void test5_2(DOMImplementationLS *impl, InputSource &data) {
|
||||
DOMLSParser *p = impl->createLSParser();
|
||||
|
||||
p->getDomConfig()->setParameter(XMLUni::fgXercesDisableDefaultEntityResolution, true);
|
||||
p->parse(data); // GOOD
|
||||
}
|
||||
|
||||
void test5_3(DOMImplementationLS *impl, InputSource &data) {
|
||||
DOMLSParser *p = impl->createLSParser();
|
||||
|
||||
p->getDomConfig()->setParameter(XMLUni::fgXercesDisableDefaultEntityResolution, false);
|
||||
p->parse(data); // BAD (parser not correctly configured)
|
||||
}
|
||||
|
||||
void test5_4(DOMImplementationLS *impl, InputSource &data) {
|
||||
DOMLSParser *p = impl->createLSParser();
|
||||
DOMConfiguration *cfg = p->getDomConfig();
|
||||
|
||||
cfg->setParameter(XMLUni::fgXercesDisableDefaultEntityResolution, true);
|
||||
p->parse(data); // GOOD
|
||||
}
|
||||
|
||||
void test5_5(DOMImplementationLS *impl, InputSource &data) {
|
||||
DOMLSParser *p = impl->createLSParser();
|
||||
DOMConfiguration *cfg = p->getDomConfig();
|
||||
|
||||
cfg->setParameter(XMLUni::fgXercesDisableDefaultEntityResolution, false);
|
||||
p->parse(data); // BAD (parser not correctly configured)
|
||||
}
|
||||
|
||||
DOMImplementationLS *g_impl;
|
||||
DOMLSParser *g_p1, *g_p2;
|
||||
InputSource *g_data;
|
||||
|
||||
void test5_6_init() {
|
||||
g_p1 = g_impl->createLSParser();
|
||||
g_p1->getDomConfig()->setParameter(XMLUni::fgXercesDisableDefaultEntityResolution, true);
|
||||
|
||||
g_p2 = g_impl->createLSParser();
|
||||
}
|
||||
|
||||
void test5_6() {
|
||||
test5_6_init();
|
||||
|
||||
g_p1->parse(*g_data); // GOOD
|
||||
g_p2->parse(*g_data); // BAD (parser not correctly configured) [NOT DETECTED]
|
||||
}
|
||||
|
||||
void test5_7(DOMImplementationLS *impl, InputSource &data) {
|
||||
DOMLSParser *p = impl->createLSParser();
|
||||
|
||||
p->parse(data); // BAD (parser not correctly configured)
|
||||
|
||||
p->getDomConfig()->setParameter(XMLUni::fgXercesDisableDefaultEntityResolution, true);
|
||||
p->parse(data); // GOOD
|
||||
|
||||
p->getDomConfig()->setParameter(XMLUni::fgXercesDisableDefaultEntityResolution, false);
|
||||
p->parse(data); // BAD (parser not correctly configured)
|
||||
}
|
||||
|
||||
void test5_8(DOMImplementationLS *impl, InputSource &data) {
|
||||
DOMLSParser *p = impl->createLSParser();
|
||||
DOMConfiguration *cfg = p->getDomConfig();
|
||||
|
||||
p->parse(data); // BAD (parser not correctly configured) [NOT DETECTED]
|
||||
|
||||
cfg->setParameter(XMLUni::fgXercesDisableDefaultEntityResolution, true);
|
||||
p->parse(data); // GOOD
|
||||
|
||||
cfg->setParameter(XMLUni::fgXercesDisableDefaultEntityResolution, false);
|
||||
p->parse(data); // BAD (parser not correctly configured) [NOT DETECTED]
|
||||
}
|
||||
8
cpp/ql/test/successor-tests/ifstmt/ifstmt/ifstmt.cpp
Normal file
8
cpp/ql/test/successor-tests/ifstmt/ifstmt/ifstmt.cpp
Normal file
@@ -0,0 +1,8 @@
|
||||
void normal(int x, int y) {
|
||||
if(int z = y; x == z) {
|
||||
l1:;
|
||||
}
|
||||
l2:;
|
||||
}
|
||||
|
||||
// semmle-extractor-options: -std=c++17
|
||||
@@ -1 +1,2 @@
|
||||
| ifstmt.c:28:6:28:11 | ... == ... | l2 |
|
||||
| ifstmt.cpp:2:17:2:22 | ... == ... | l2 |
|
||||
|
||||
@@ -1 +1,2 @@
|
||||
| ifstmt.c:28:6:28:11 | ... == ... | l1 |
|
||||
| ifstmt.cpp:2:17:2:22 | ... == ... | l1 |
|
||||
|
||||
@@ -1 +1,2 @@
|
||||
| ifstmt.c:29:8:29:8 | ; | l2 |
|
||||
| ifstmt.cpp:3:8:3:8 | ; | l2 |
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user