C++: merge main and accept test changes

This commit is contained in:
Robert Marsh
2022-05-20 14:37:09 -04:00
1952 changed files with 131613 additions and 22440 deletions

View File

@@ -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

View File

@@ -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.

View File

@@ -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.

View File

@@ -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)`.

View File

@@ -1,4 +0,0 @@
---
category: minorAnalysis
---
* More Windows pool allocation functions are now detected as `AllocationFunction`s.

View File

@@ -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.

View 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.

View File

@@ -0,0 +1 @@
## 0.2.1

View File

@@ -1,2 +1,2 @@
---
lastReleaseVersion: 0.1.0
lastReleaseVersion: 0.2.1

View File

@@ -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

View File

@@ -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

View File

@@ -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 |

View File

@@ -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()"

View File

@@ -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()
)

View File

@@ -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)
}
}

View File

@@ -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

View File

@@ -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()

View File

@@ -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()

View File

@@ -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()

View File

@@ -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()

View File

@@ -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) }

View File

@@ -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()

View File

@@ -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

View File

@@ -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()

View File

@@ -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()

View File

@@ -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()

View File

@@ -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()

View File

@@ -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) }

View File

@@ -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

View File

@@ -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

View File

@@ -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;

View File

@@ -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() {

View File

@@ -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 {

View File

@@ -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() {

View File

@@ -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)

View File

@@ -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() {

View File

@@ -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 {

View File

@@ -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) {

View File

@@ -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]

View File

@@ -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

View File

@@ -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() }

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -0,0 +1,4 @@
description: Support C++17 if and switch initializers
compatibility: partial
exprparents.rel: run exprparents.qlo
stmtparents.rel: run stmtparents.qlo

View File

@@ -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"

View File

@@ -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

View File

@@ -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. */

View File

@@ -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. */

View File

@@ -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) {

View File

@@ -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

View File

@@ -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

View File

@@ -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

View 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"
)
}
}

View 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);
}

View File

@@ -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
}
}

View 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)
)
)
}
}

View File

@@ -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.

View File

@@ -0,0 +1,4 @@
---
category: minorAnalysis
---
* The "XML external entity expansion" (`cpp/external-entity-expansion`) query precision has been increased to `high`.

View File

@@ -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.

View 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.

View File

@@ -1,2 +1,2 @@
---
lastReleaseVersion: 0.1.0
lastReleaseVersion: 0.1.2

View File

@@ -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

View File

@@ -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
...

View File

@@ -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>

View File

@@ -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

View File

@@ -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."

View File

@@ -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."

View File

@@ -1,5 +1,5 @@
name: codeql/cpp-queries
version: 0.1.1-dev
version: 0.1.3-dev
groups:
- cpp
- queries

View File

@@ -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

View File

@@ -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 |

View File

@@ -0,0 +1 @@
experimental/Security/CWE/CWE-020/NoCheckBeforeUnsafePutUser.ql

View File

@@ -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();
}
}

View File

@@ -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 |

View File

@@ -0,0 +1 @@
experimental/Security/CWE/CWE-476/DangerousUseOfExceptionBlocks.ql

View File

@@ -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();
}

View File

@@ -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>:

View File

@@ -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

View File

@@ -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

View File

@@ -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;

View File

@@ -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 |

View File

@@ -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

View File

@@ -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()

View File

@@ -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

View File

@@ -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

View File

@@ -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 = "[?]"

View File

@@ -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 |

View File

@@ -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]
}

View File

@@ -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[];
};

View 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
}

View 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
}

View 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);
}
}

View 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]
}

View 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

View File

@@ -1 +1,2 @@
| ifstmt.c:28:6:28:11 | ... == ... | l2 |
| ifstmt.cpp:2:17:2:22 | ... == ... | l2 |

View File

@@ -1 +1,2 @@
| ifstmt.c:28:6:28:11 | ... == ... | l1 |
| ifstmt.cpp:2:17:2:22 | ... == ... | l1 |

View File

@@ -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