Merge branch 'main' into pyMaD

This commit is contained in:
Erik Krogh Kristensen
2022-05-12 14:43:16 +02:00
1508 changed files with 93821 additions and 15450 deletions

2
.github/labeler.yml vendored
View File

@@ -11,7 +11,7 @@ Java:
- change-notes/**/*java.* - change-notes/**/*java.*
JS: JS:
- javascript/**/* - any: [ 'javascript/**/*', '!javascript/ql/experimental/adaptivethreatmodeling/**/*' ]
- change-notes/**/*javascript* - change-notes/**/*javascript*
Python: Python:

View File

@@ -30,7 +30,7 @@ jobs:
- name: Setup dotnet - name: Setup dotnet
uses: actions/setup-dotnet@v2 uses: actions/setup-dotnet@v2
with: with:
dotnet-version: 6.0.101 dotnet-version: 6.0.202
- name: Checkout repository - name: Checkout repository
uses: actions/checkout@v3 uses: actions/checkout@v3

View File

@@ -30,20 +30,15 @@ jobs:
with: with:
python-version: 3.8 python-version: 3.8
- name: Download CodeQL CLI - name: Download CodeQL CLI
uses: dsaltares/fetch-gh-release-asset@aa37ae5c44d3c9820bc12fe675e8670ecd93bd1c # Look under the `codeql` directory, as this is where we checked out the `github/codeql` repo
with: uses: ./codeql/.github/actions/fetch-codeql
repo: "github/codeql-cli-binaries"
version: "latest"
file: "codeql-linux64.zip"
token: ${{ secrets.GITHUB_TOKEN }}
- name: Unzip CodeQL CLI - name: Unzip CodeQL CLI
run: unzip -d codeql-cli codeql-linux64.zip run: unzip -d codeql-cli codeql-linux64.zip
- name: Build code scanning query list - name: Build code scanning query list
run: | run: |
PATH="$PATH:codeql-cli/codeql" python codeql/misc/scripts/generate-code-scanning-query-list.py > code-scanning-query-list.csv python codeql/misc/scripts/generate-code-scanning-query-list.py > code-scanning-query-list.csv
- name: Upload code scanning query list - name: Upload code scanning query list
uses: actions/upload-artifact@v3 uses: actions/upload-artifact@v3
with: with:
name: code-scanning-query-list name: code-scanning-query-list
path: code-scanning-query-list.csv path: code-scanning-query-list.csv

3
.gitignore vendored
View File

@@ -37,5 +37,8 @@ csharp/extractor/Semmle.Extraction.CSharp.Driver/Properties/launchSettings.json
# links created by bazel # links created by bazel
/bazel-* /bazel-*
# local bazel options
/local.bazelrc
# CLion project files # CLion project files
/.clwb /.clwb

View File

@@ -4,6 +4,9 @@
/javascript/ @github/codeql-javascript /javascript/ @github/codeql-javascript
/python/ @github/codeql-python /python/ @github/codeql-python
/ruby/ @github/codeql-ruby /ruby/ @github/codeql-ruby
/swift/ @github/codeql-c
/java/kotlin-extractor/ @github/codeql-kotlin
/java/kotlin-explorer/ @github/codeql-kotlin
# ML-powered queries # ML-powered queries
/javascript/ql/experimental/adaptivethreatmodeling/ @github/codeql-ml-powered-queries-reviewers /javascript/ql/experimental/adaptivethreatmodeling/ @github/codeql-ml-powered-queries-reviewers

View File

@@ -1,3 +1,14 @@
## 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 ## 0.1.0
### Breaking Changes ### Breaking Changes

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

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

View File

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

View File

@@ -38,8 +38,8 @@ class Function extends Declaration, ControlFlowNode, AccessHolder, @function {
* int z = min(5, 7); * int z = min(5, 7);
* ``` * ```
* The full signature of the function called on the last line would be * 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 * `min<int>(int, int) -> int`, and the full signature of the uninstantiated
* template on the first line would be "min<T>(T, T) -> T". * template on the first line would be `min<T>(T, T) -> T`.
*/ */
string getFullSignature() { string getFullSignature() {
exists(string name, string templateArgs, string args | exists(string name, string templateArgs, string args |

View File

@@ -1312,7 +1312,7 @@ class FormatLiteral extends Literal {
len = len =
min(int v | min(int v |
v = this.getPrecision(n) or 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 ) and
reason = TValueFlowAnalysis() reason = TValueFlowAnalysis()
) )

View File

@@ -27,11 +27,14 @@ predicate canValueFlow(Expr fromExpr, Expr toExpr) {
fromExpr = toExpr.(ConditionalExpr).getElse() 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 { class AnalyzedString extends Expr {
AnalysedString() { AnalyzedString() {
this.getUnspecifiedType() instanceof ArrayType or this.getUnspecifiedType() instanceof ArrayType or
this.getUnspecifiedType() instanceof PointerType this.getUnspecifiedType() instanceof PointerType
} }
@@ -41,15 +44,15 @@ class AnalysedString extends Expr {
* can be calculated. * can be calculated.
*/ */
int getMaxLength() { 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. // return a value (this essentially means 'infinity') we can't return a value either.
result = result =
max(AnalysedString expr, int toMax | max(AnalyzedString expr, int toMax |
canValueFlow*(expr, this) and toMax = expr.(StringLiteral).getOriginalLength() canValueFlow*(expr, this) and toMax = expr.(StringLiteral).getOriginalLength()
| |
toMax toMax
) and // maximum length ) 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

@@ -498,23 +498,35 @@ private predicate readSet(NodeEx node1, ContentSet c, NodeEx node2, Configuratio
} }
// inline to reduce fan-out via `getAReadContent` // inline to reduce fan-out via `getAReadContent`
pragma[inline] bindingset[c]
private predicate read(NodeEx node1, Content c, NodeEx node2, Configuration config) { private predicate read(NodeEx node1, Content c, NodeEx node2, Configuration config) {
exists(ContentSet cs | exists(ContentSet cs |
readSet(node1, cs, node2, config) and 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` // inline to reduce fan-out via `getAReadContent`
pragma[inline] bindingset[c]
private predicate clearsContentEx(NodeEx n, Content c) { private predicate clearsContentEx(NodeEx n, Content c) {
exists(ContentSet cs | exists(ContentSet cs |
clearsContentCached(n.asNode(), cs) and 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] pragma[nomagic]
private predicate store( private predicate store(
NodeEx node1, TypedContent tc, NodeEx node2, DataFlowType contentType, Configuration config NodeEx node1, TypedContent tc, NodeEx node2, DataFlowType contentType, Configuration config
@@ -793,7 +805,7 @@ private module Stage1 {
* by `revFlow`. * by `revFlow`.
*/ */
pragma[nomagic] pragma[nomagic]
private predicate revFlowIsReadAndStored(Content c, Configuration conf) { predicate revFlowIsReadAndStored(Content c, Configuration conf) {
revFlowConsCand(c, conf) and revFlowConsCand(c, conf) and
revFlowStore(c, _, _, conf) revFlowStore(c, _, _, conf)
} }
@@ -891,7 +903,7 @@ private module Stage1 {
pragma[nomagic] pragma[nomagic]
predicate readStepCand(NodeEx n1, Content c, NodeEx n2, Configuration config) { 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 read(n1, c, n2, pragma[only_bind_into](config)) and
revFlow(n2, pragma[only_bind_into](config)) revFlow(n2, pragma[only_bind_into](config))
} }
@@ -1181,11 +1193,26 @@ private module Stage2 {
private predicate flowIntoCall = flowIntoCallNodeCand1/5; 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] bindingset[node, state, ap, config]
private predicate filter(NodeEx node, FlowState state, Ap ap, Configuration config) { private predicate filter(NodeEx node, FlowState state, Ap ap, Configuration config) {
PrevStage::revFlowState(state, pragma[only_bind_into](config)) and PrevStage::revFlowState(state, pragma[only_bind_into](config)) and
exists(ap) 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] bindingset[ap, contentType]
@@ -1646,10 +1673,24 @@ private module Stage2 {
storeStepFwd(_, ap, tc, _, _, config) 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) 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] pragma[noinline]
private predicate parameterFlow( private predicate parameterFlow(
ParamNodeEx p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config ParamNodeEx p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config
@@ -1740,7 +1781,8 @@ private module LocalFlowBigStep {
private class FlowCheckNode extends NodeEx { private class FlowCheckNode extends NodeEx {
FlowCheckNode() { FlowCheckNode() {
castNode(this.asNode()) or castNode(this.asNode()) or
clearsContentCached(this.asNode(), _) clearsContentCached(this.asNode(), _) or
expectsContentCached(this.asNode(), _)
} }
} }
@@ -1979,6 +2021,16 @@ private module Stage3 {
clearContent(node, ap.getHead().getContent(), config) 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] pragma[nomagic]
private predicate castingNodeEx(NodeEx node) { node.asNode() instanceof CastingNode } private predicate castingNodeEx(NodeEx node) { node.asNode() instanceof CastingNode }
@@ -1987,7 +2039,12 @@ private module Stage3 {
exists(state) and exists(state) and
exists(config) and exists(config) and
not clear(node, ap, 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] bindingset[ap, contentType]
@@ -2452,10 +2509,24 @@ private module Stage3 {
storeStepFwd(_, ap, tc, _, _, config) 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) 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] pragma[noinline]
private predicate parameterFlow( private predicate parameterFlow(
ParamNodeEx p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config ParamNodeEx p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config
@@ -3279,10 +3350,24 @@ private module Stage4 {
storeStepFwd(_, ap, tc, _, _, config) 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) 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] pragma[noinline]
private predicate parameterFlow( private predicate parameterFlow(
ParamNodeEx p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config ParamNodeEx p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config
@@ -3351,17 +3436,28 @@ private Configuration unbindConf(Configuration conf) {
exists(Configuration c | result = pragma[only_bind_into](c) and conf = pragma[only_bind_into](c)) exists(Configuration c | result = pragma[only_bind_into](c) and conf = pragma[only_bind_into](c))
} }
private predicate nodeMayUseSummary( pragma[nomagic]
NodeEx n, FlowState state, AccessPathApprox apa, Configuration config private predicate nodeMayUseSummary0(
NodeEx n, DataFlowCallable c, FlowState state, AccessPathApprox apa, Configuration config
) { ) {
exists(DataFlowCallable c, AccessPathApprox apa0 | exists(AccessPathApprox apa0 |
Stage4::parameterMayFlowThrough(_, c, apa, _) and Stage4::parameterMayFlowThrough(_, c, _, _) and
Stage4::revFlow(n, state, true, _, apa0, config) and Stage4::revFlow(n, state, true, _, apa0, config) and
Stage4::fwdFlow(n, state, any(CallContextCall ccc), TAccessPathApproxSome(apa), apa0, config) and Stage4::fwdFlow(n, state, any(CallContextCall ccc), TAccessPathApproxSome(apa), apa0, config) and
n.getEnclosingCallable() = c 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 = private newtype TSummaryCtx =
TSummaryCtxNone() or TSummaryCtxNone() or
TSummaryCtxSome(ParamNodeEx p, FlowState state, AccessPath ap) { TSummaryCtxSome(ParamNodeEx p, FlowState state, AccessPath ap) {
@@ -4257,6 +4353,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 * 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 * a subpath between `par` and `ret` with the connecting edges `arg -> par` and
@@ -4264,15 +4366,13 @@ private module Subpaths {
*/ */
predicate subpaths(PathNode arg, PathNodeImpl par, PathNodeImpl ret, PathNode out) { predicate subpaths(PathNode arg, PathNodeImpl par, PathNodeImpl ret, PathNode out) {
exists(ParamNodeEx p, NodeEx o, FlowState sout, AccessPath apout, PathNodeMid out0 | 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() = pragma[only_bind_into](out0) and
pragma[only_bind_into](arg).getASuccessor() = out0 and subpaths03(pragma[only_bind_into](arg), p, localStepToHidden*(ret), o, sout, apout) and
subpaths03(arg, p, localStepToHidden*(ret), o, sout, apout) and hasSuccessor(pragma[only_bind_into](arg), par, p) and
not ret.isHidden() and not ret.isHidden() and
par.getNodeEx() = p and pathNode(out0, o, sout, _, _, apout, _, _)
out0.getNodeEx() = o and |
out0.getState() = sout and out = out0 or out = out0.projectToSink()
out0.getAp() = apout and
(out = out0 or out = out0.projectToSink())
) )
} }
@@ -4609,6 +4709,10 @@ private module FlowExploration {
exists(PartialPathNodeRev mid | exists(PartialPathNodeRev mid |
revPartialPathStep(mid, node, state, sc1, sc2, sc3, ap, config) and revPartialPathStep(mid, node, state, sc1, sc2, sc3, ap, config) and
not clearsContentEx(node, ap.getHead()) and not clearsContentEx(node, ap.getHead()) and
(
notExpectsContent(node) or
expectsContentEx(node, ap.getHead())
) and
not fullBarrier(node, config) and not fullBarrier(node, config) and
not stateBarrier(node, state, config) and not stateBarrier(node, state, config) and
distSink(node.getEnclosingCallable(), config) <= config.explorationLimit() distSink(node.getEnclosingCallable(), config) <= config.explorationLimit()
@@ -4625,6 +4729,10 @@ private module FlowExploration {
not fullBarrier(node, config) and not fullBarrier(node, config) and
not stateBarrier(node, state, config) and not stateBarrier(node, state, config) and
not clearsContentEx(node, ap.getHead().getContent()) and not clearsContentEx(node, ap.getHead().getContent()) and
(
notExpectsContent(node) or
expectsContentEx(node, ap.getHead().getContent())
) and
if node.asNode() instanceof CastingNode if node.asNode() instanceof CastingNode
then compatibleTypes(node.getDataFlowType(), ap.getType()) then compatibleTypes(node.getDataFlowType(), ap.getType())
else any() else any()

View File

@@ -498,23 +498,35 @@ private predicate readSet(NodeEx node1, ContentSet c, NodeEx node2, Configuratio
} }
// inline to reduce fan-out via `getAReadContent` // inline to reduce fan-out via `getAReadContent`
pragma[inline] bindingset[c]
private predicate read(NodeEx node1, Content c, NodeEx node2, Configuration config) { private predicate read(NodeEx node1, Content c, NodeEx node2, Configuration config) {
exists(ContentSet cs | exists(ContentSet cs |
readSet(node1, cs, node2, config) and 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` // inline to reduce fan-out via `getAReadContent`
pragma[inline] bindingset[c]
private predicate clearsContentEx(NodeEx n, Content c) { private predicate clearsContentEx(NodeEx n, Content c) {
exists(ContentSet cs | exists(ContentSet cs |
clearsContentCached(n.asNode(), cs) and 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] pragma[nomagic]
private predicate store( private predicate store(
NodeEx node1, TypedContent tc, NodeEx node2, DataFlowType contentType, Configuration config NodeEx node1, TypedContent tc, NodeEx node2, DataFlowType contentType, Configuration config
@@ -793,7 +805,7 @@ private module Stage1 {
* by `revFlow`. * by `revFlow`.
*/ */
pragma[nomagic] pragma[nomagic]
private predicate revFlowIsReadAndStored(Content c, Configuration conf) { predicate revFlowIsReadAndStored(Content c, Configuration conf) {
revFlowConsCand(c, conf) and revFlowConsCand(c, conf) and
revFlowStore(c, _, _, conf) revFlowStore(c, _, _, conf)
} }
@@ -891,7 +903,7 @@ private module Stage1 {
pragma[nomagic] pragma[nomagic]
predicate readStepCand(NodeEx n1, Content c, NodeEx n2, Configuration config) { 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 read(n1, c, n2, pragma[only_bind_into](config)) and
revFlow(n2, pragma[only_bind_into](config)) revFlow(n2, pragma[only_bind_into](config))
} }
@@ -1181,11 +1193,26 @@ private module Stage2 {
private predicate flowIntoCall = flowIntoCallNodeCand1/5; 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] bindingset[node, state, ap, config]
private predicate filter(NodeEx node, FlowState state, Ap ap, Configuration config) { private predicate filter(NodeEx node, FlowState state, Ap ap, Configuration config) {
PrevStage::revFlowState(state, pragma[only_bind_into](config)) and PrevStage::revFlowState(state, pragma[only_bind_into](config)) and
exists(ap) 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] bindingset[ap, contentType]
@@ -1646,10 +1673,24 @@ private module Stage2 {
storeStepFwd(_, ap, tc, _, _, config) 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) 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] pragma[noinline]
private predicate parameterFlow( private predicate parameterFlow(
ParamNodeEx p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config ParamNodeEx p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config
@@ -1740,7 +1781,8 @@ private module LocalFlowBigStep {
private class FlowCheckNode extends NodeEx { private class FlowCheckNode extends NodeEx {
FlowCheckNode() { FlowCheckNode() {
castNode(this.asNode()) or castNode(this.asNode()) or
clearsContentCached(this.asNode(), _) clearsContentCached(this.asNode(), _) or
expectsContentCached(this.asNode(), _)
} }
} }
@@ -1979,6 +2021,16 @@ private module Stage3 {
clearContent(node, ap.getHead().getContent(), config) 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] pragma[nomagic]
private predicate castingNodeEx(NodeEx node) { node.asNode() instanceof CastingNode } private predicate castingNodeEx(NodeEx node) { node.asNode() instanceof CastingNode }
@@ -1987,7 +2039,12 @@ private module Stage3 {
exists(state) and exists(state) and
exists(config) and exists(config) and
not clear(node, ap, 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] bindingset[ap, contentType]
@@ -2452,10 +2509,24 @@ private module Stage3 {
storeStepFwd(_, ap, tc, _, _, config) 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) 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] pragma[noinline]
private predicate parameterFlow( private predicate parameterFlow(
ParamNodeEx p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config ParamNodeEx p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config
@@ -3279,10 +3350,24 @@ private module Stage4 {
storeStepFwd(_, ap, tc, _, _, config) 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) 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] pragma[noinline]
private predicate parameterFlow( private predicate parameterFlow(
ParamNodeEx p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config ParamNodeEx p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config
@@ -3351,17 +3436,28 @@ private Configuration unbindConf(Configuration conf) {
exists(Configuration c | result = pragma[only_bind_into](c) and conf = pragma[only_bind_into](c)) exists(Configuration c | result = pragma[only_bind_into](c) and conf = pragma[only_bind_into](c))
} }
private predicate nodeMayUseSummary( pragma[nomagic]
NodeEx n, FlowState state, AccessPathApprox apa, Configuration config private predicate nodeMayUseSummary0(
NodeEx n, DataFlowCallable c, FlowState state, AccessPathApprox apa, Configuration config
) { ) {
exists(DataFlowCallable c, AccessPathApprox apa0 | exists(AccessPathApprox apa0 |
Stage4::parameterMayFlowThrough(_, c, apa, _) and Stage4::parameterMayFlowThrough(_, c, _, _) and
Stage4::revFlow(n, state, true, _, apa0, config) and Stage4::revFlow(n, state, true, _, apa0, config) and
Stage4::fwdFlow(n, state, any(CallContextCall ccc), TAccessPathApproxSome(apa), apa0, config) and Stage4::fwdFlow(n, state, any(CallContextCall ccc), TAccessPathApproxSome(apa), apa0, config) and
n.getEnclosingCallable() = c 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 = private newtype TSummaryCtx =
TSummaryCtxNone() or TSummaryCtxNone() or
TSummaryCtxSome(ParamNodeEx p, FlowState state, AccessPath ap) { TSummaryCtxSome(ParamNodeEx p, FlowState state, AccessPath ap) {
@@ -4257,6 +4353,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 * 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 * a subpath between `par` and `ret` with the connecting edges `arg -> par` and
@@ -4264,15 +4366,13 @@ private module Subpaths {
*/ */
predicate subpaths(PathNode arg, PathNodeImpl par, PathNodeImpl ret, PathNode out) { predicate subpaths(PathNode arg, PathNodeImpl par, PathNodeImpl ret, PathNode out) {
exists(ParamNodeEx p, NodeEx o, FlowState sout, AccessPath apout, PathNodeMid out0 | 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() = pragma[only_bind_into](out0) and
pragma[only_bind_into](arg).getASuccessor() = out0 and subpaths03(pragma[only_bind_into](arg), p, localStepToHidden*(ret), o, sout, apout) and
subpaths03(arg, p, localStepToHidden*(ret), o, sout, apout) and hasSuccessor(pragma[only_bind_into](arg), par, p) and
not ret.isHidden() and not ret.isHidden() and
par.getNodeEx() = p and pathNode(out0, o, sout, _, _, apout, _, _)
out0.getNodeEx() = o and |
out0.getState() = sout and out = out0 or out = out0.projectToSink()
out0.getAp() = apout and
(out = out0 or out = out0.projectToSink())
) )
} }
@@ -4609,6 +4709,10 @@ private module FlowExploration {
exists(PartialPathNodeRev mid | exists(PartialPathNodeRev mid |
revPartialPathStep(mid, node, state, sc1, sc2, sc3, ap, config) and revPartialPathStep(mid, node, state, sc1, sc2, sc3, ap, config) and
not clearsContentEx(node, ap.getHead()) and not clearsContentEx(node, ap.getHead()) and
(
notExpectsContent(node) or
expectsContentEx(node, ap.getHead())
) and
not fullBarrier(node, config) and not fullBarrier(node, config) and
not stateBarrier(node, state, config) and not stateBarrier(node, state, config) and
distSink(node.getEnclosingCallable(), config) <= config.explorationLimit() distSink(node.getEnclosingCallable(), config) <= config.explorationLimit()
@@ -4625,6 +4729,10 @@ private module FlowExploration {
not fullBarrier(node, config) and not fullBarrier(node, config) and
not stateBarrier(node, state, config) and not stateBarrier(node, state, config) and
not clearsContentEx(node, ap.getHead().getContent()) and not clearsContentEx(node, ap.getHead().getContent()) and
(
notExpectsContent(node) or
expectsContentEx(node, ap.getHead().getContent())
) and
if node.asNode() instanceof CastingNode if node.asNode() instanceof CastingNode
then compatibleTypes(node.getDataFlowType(), ap.getType()) then compatibleTypes(node.getDataFlowType(), ap.getType())
else any() else any()

View File

@@ -498,23 +498,35 @@ private predicate readSet(NodeEx node1, ContentSet c, NodeEx node2, Configuratio
} }
// inline to reduce fan-out via `getAReadContent` // inline to reduce fan-out via `getAReadContent`
pragma[inline] bindingset[c]
private predicate read(NodeEx node1, Content c, NodeEx node2, Configuration config) { private predicate read(NodeEx node1, Content c, NodeEx node2, Configuration config) {
exists(ContentSet cs | exists(ContentSet cs |
readSet(node1, cs, node2, config) and 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` // inline to reduce fan-out via `getAReadContent`
pragma[inline] bindingset[c]
private predicate clearsContentEx(NodeEx n, Content c) { private predicate clearsContentEx(NodeEx n, Content c) {
exists(ContentSet cs | exists(ContentSet cs |
clearsContentCached(n.asNode(), cs) and 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] pragma[nomagic]
private predicate store( private predicate store(
NodeEx node1, TypedContent tc, NodeEx node2, DataFlowType contentType, Configuration config NodeEx node1, TypedContent tc, NodeEx node2, DataFlowType contentType, Configuration config
@@ -793,7 +805,7 @@ private module Stage1 {
* by `revFlow`. * by `revFlow`.
*/ */
pragma[nomagic] pragma[nomagic]
private predicate revFlowIsReadAndStored(Content c, Configuration conf) { predicate revFlowIsReadAndStored(Content c, Configuration conf) {
revFlowConsCand(c, conf) and revFlowConsCand(c, conf) and
revFlowStore(c, _, _, conf) revFlowStore(c, _, _, conf)
} }
@@ -891,7 +903,7 @@ private module Stage1 {
pragma[nomagic] pragma[nomagic]
predicate readStepCand(NodeEx n1, Content c, NodeEx n2, Configuration config) { 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 read(n1, c, n2, pragma[only_bind_into](config)) and
revFlow(n2, pragma[only_bind_into](config)) revFlow(n2, pragma[only_bind_into](config))
} }
@@ -1181,11 +1193,26 @@ private module Stage2 {
private predicate flowIntoCall = flowIntoCallNodeCand1/5; 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] bindingset[node, state, ap, config]
private predicate filter(NodeEx node, FlowState state, Ap ap, Configuration config) { private predicate filter(NodeEx node, FlowState state, Ap ap, Configuration config) {
PrevStage::revFlowState(state, pragma[only_bind_into](config)) and PrevStage::revFlowState(state, pragma[only_bind_into](config)) and
exists(ap) 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] bindingset[ap, contentType]
@@ -1646,10 +1673,24 @@ private module Stage2 {
storeStepFwd(_, ap, tc, _, _, config) 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) 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] pragma[noinline]
private predicate parameterFlow( private predicate parameterFlow(
ParamNodeEx p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config ParamNodeEx p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config
@@ -1740,7 +1781,8 @@ private module LocalFlowBigStep {
private class FlowCheckNode extends NodeEx { private class FlowCheckNode extends NodeEx {
FlowCheckNode() { FlowCheckNode() {
castNode(this.asNode()) or castNode(this.asNode()) or
clearsContentCached(this.asNode(), _) clearsContentCached(this.asNode(), _) or
expectsContentCached(this.asNode(), _)
} }
} }
@@ -1979,6 +2021,16 @@ private module Stage3 {
clearContent(node, ap.getHead().getContent(), config) 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] pragma[nomagic]
private predicate castingNodeEx(NodeEx node) { node.asNode() instanceof CastingNode } private predicate castingNodeEx(NodeEx node) { node.asNode() instanceof CastingNode }
@@ -1987,7 +2039,12 @@ private module Stage3 {
exists(state) and exists(state) and
exists(config) and exists(config) and
not clear(node, ap, 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] bindingset[ap, contentType]
@@ -2452,10 +2509,24 @@ private module Stage3 {
storeStepFwd(_, ap, tc, _, _, config) 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) 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] pragma[noinline]
private predicate parameterFlow( private predicate parameterFlow(
ParamNodeEx p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config ParamNodeEx p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config
@@ -3279,10 +3350,24 @@ private module Stage4 {
storeStepFwd(_, ap, tc, _, _, config) 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) 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] pragma[noinline]
private predicate parameterFlow( private predicate parameterFlow(
ParamNodeEx p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config ParamNodeEx p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config
@@ -3351,17 +3436,28 @@ private Configuration unbindConf(Configuration conf) {
exists(Configuration c | result = pragma[only_bind_into](c) and conf = pragma[only_bind_into](c)) exists(Configuration c | result = pragma[only_bind_into](c) and conf = pragma[only_bind_into](c))
} }
private predicate nodeMayUseSummary( pragma[nomagic]
NodeEx n, FlowState state, AccessPathApprox apa, Configuration config private predicate nodeMayUseSummary0(
NodeEx n, DataFlowCallable c, FlowState state, AccessPathApprox apa, Configuration config
) { ) {
exists(DataFlowCallable c, AccessPathApprox apa0 | exists(AccessPathApprox apa0 |
Stage4::parameterMayFlowThrough(_, c, apa, _) and Stage4::parameterMayFlowThrough(_, c, _, _) and
Stage4::revFlow(n, state, true, _, apa0, config) and Stage4::revFlow(n, state, true, _, apa0, config) and
Stage4::fwdFlow(n, state, any(CallContextCall ccc), TAccessPathApproxSome(apa), apa0, config) and Stage4::fwdFlow(n, state, any(CallContextCall ccc), TAccessPathApproxSome(apa), apa0, config) and
n.getEnclosingCallable() = c 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 = private newtype TSummaryCtx =
TSummaryCtxNone() or TSummaryCtxNone() or
TSummaryCtxSome(ParamNodeEx p, FlowState state, AccessPath ap) { TSummaryCtxSome(ParamNodeEx p, FlowState state, AccessPath ap) {
@@ -4257,6 +4353,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 * 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 * a subpath between `par` and `ret` with the connecting edges `arg -> par` and
@@ -4264,15 +4366,13 @@ private module Subpaths {
*/ */
predicate subpaths(PathNode arg, PathNodeImpl par, PathNodeImpl ret, PathNode out) { predicate subpaths(PathNode arg, PathNodeImpl par, PathNodeImpl ret, PathNode out) {
exists(ParamNodeEx p, NodeEx o, FlowState sout, AccessPath apout, PathNodeMid out0 | 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() = pragma[only_bind_into](out0) and
pragma[only_bind_into](arg).getASuccessor() = out0 and subpaths03(pragma[only_bind_into](arg), p, localStepToHidden*(ret), o, sout, apout) and
subpaths03(arg, p, localStepToHidden*(ret), o, sout, apout) and hasSuccessor(pragma[only_bind_into](arg), par, p) and
not ret.isHidden() and not ret.isHidden() and
par.getNodeEx() = p and pathNode(out0, o, sout, _, _, apout, _, _)
out0.getNodeEx() = o and |
out0.getState() = sout and out = out0 or out = out0.projectToSink()
out0.getAp() = apout and
(out = out0 or out = out0.projectToSink())
) )
} }
@@ -4609,6 +4709,10 @@ private module FlowExploration {
exists(PartialPathNodeRev mid | exists(PartialPathNodeRev mid |
revPartialPathStep(mid, node, state, sc1, sc2, sc3, ap, config) and revPartialPathStep(mid, node, state, sc1, sc2, sc3, ap, config) and
not clearsContentEx(node, ap.getHead()) and not clearsContentEx(node, ap.getHead()) and
(
notExpectsContent(node) or
expectsContentEx(node, ap.getHead())
) and
not fullBarrier(node, config) and not fullBarrier(node, config) and
not stateBarrier(node, state, config) and not stateBarrier(node, state, config) and
distSink(node.getEnclosingCallable(), config) <= config.explorationLimit() distSink(node.getEnclosingCallable(), config) <= config.explorationLimit()
@@ -4625,6 +4729,10 @@ private module FlowExploration {
not fullBarrier(node, config) and not fullBarrier(node, config) and
not stateBarrier(node, state, config) and not stateBarrier(node, state, config) and
not clearsContentEx(node, ap.getHead().getContent()) and not clearsContentEx(node, ap.getHead().getContent()) and
(
notExpectsContent(node) or
expectsContentEx(node, ap.getHead().getContent())
) and
if node.asNode() instanceof CastingNode if node.asNode() instanceof CastingNode
then compatibleTypes(node.getDataFlowType(), ap.getType()) then compatibleTypes(node.getDataFlowType(), ap.getType())
else any() else any()

View File

@@ -498,23 +498,35 @@ private predicate readSet(NodeEx node1, ContentSet c, NodeEx node2, Configuratio
} }
// inline to reduce fan-out via `getAReadContent` // inline to reduce fan-out via `getAReadContent`
pragma[inline] bindingset[c]
private predicate read(NodeEx node1, Content c, NodeEx node2, Configuration config) { private predicate read(NodeEx node1, Content c, NodeEx node2, Configuration config) {
exists(ContentSet cs | exists(ContentSet cs |
readSet(node1, cs, node2, config) and 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` // inline to reduce fan-out via `getAReadContent`
pragma[inline] bindingset[c]
private predicate clearsContentEx(NodeEx n, Content c) { private predicate clearsContentEx(NodeEx n, Content c) {
exists(ContentSet cs | exists(ContentSet cs |
clearsContentCached(n.asNode(), cs) and 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] pragma[nomagic]
private predicate store( private predicate store(
NodeEx node1, TypedContent tc, NodeEx node2, DataFlowType contentType, Configuration config NodeEx node1, TypedContent tc, NodeEx node2, DataFlowType contentType, Configuration config
@@ -793,7 +805,7 @@ private module Stage1 {
* by `revFlow`. * by `revFlow`.
*/ */
pragma[nomagic] pragma[nomagic]
private predicate revFlowIsReadAndStored(Content c, Configuration conf) { predicate revFlowIsReadAndStored(Content c, Configuration conf) {
revFlowConsCand(c, conf) and revFlowConsCand(c, conf) and
revFlowStore(c, _, _, conf) revFlowStore(c, _, _, conf)
} }
@@ -891,7 +903,7 @@ private module Stage1 {
pragma[nomagic] pragma[nomagic]
predicate readStepCand(NodeEx n1, Content c, NodeEx n2, Configuration config) { 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 read(n1, c, n2, pragma[only_bind_into](config)) and
revFlow(n2, pragma[only_bind_into](config)) revFlow(n2, pragma[only_bind_into](config))
} }
@@ -1181,11 +1193,26 @@ private module Stage2 {
private predicate flowIntoCall = flowIntoCallNodeCand1/5; 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] bindingset[node, state, ap, config]
private predicate filter(NodeEx node, FlowState state, Ap ap, Configuration config) { private predicate filter(NodeEx node, FlowState state, Ap ap, Configuration config) {
PrevStage::revFlowState(state, pragma[only_bind_into](config)) and PrevStage::revFlowState(state, pragma[only_bind_into](config)) and
exists(ap) 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] bindingset[ap, contentType]
@@ -1646,10 +1673,24 @@ private module Stage2 {
storeStepFwd(_, ap, tc, _, _, config) 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) 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] pragma[noinline]
private predicate parameterFlow( private predicate parameterFlow(
ParamNodeEx p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config ParamNodeEx p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config
@@ -1740,7 +1781,8 @@ private module LocalFlowBigStep {
private class FlowCheckNode extends NodeEx { private class FlowCheckNode extends NodeEx {
FlowCheckNode() { FlowCheckNode() {
castNode(this.asNode()) or castNode(this.asNode()) or
clearsContentCached(this.asNode(), _) clearsContentCached(this.asNode(), _) or
expectsContentCached(this.asNode(), _)
} }
} }
@@ -1979,6 +2021,16 @@ private module Stage3 {
clearContent(node, ap.getHead().getContent(), config) 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] pragma[nomagic]
private predicate castingNodeEx(NodeEx node) { node.asNode() instanceof CastingNode } private predicate castingNodeEx(NodeEx node) { node.asNode() instanceof CastingNode }
@@ -1987,7 +2039,12 @@ private module Stage3 {
exists(state) and exists(state) and
exists(config) and exists(config) and
not clear(node, ap, 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] bindingset[ap, contentType]
@@ -2452,10 +2509,24 @@ private module Stage3 {
storeStepFwd(_, ap, tc, _, _, config) 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) 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] pragma[noinline]
private predicate parameterFlow( private predicate parameterFlow(
ParamNodeEx p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config ParamNodeEx p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config
@@ -3279,10 +3350,24 @@ private module Stage4 {
storeStepFwd(_, ap, tc, _, _, config) 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) 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] pragma[noinline]
private predicate parameterFlow( private predicate parameterFlow(
ParamNodeEx p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config ParamNodeEx p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config
@@ -3351,17 +3436,28 @@ private Configuration unbindConf(Configuration conf) {
exists(Configuration c | result = pragma[only_bind_into](c) and conf = pragma[only_bind_into](c)) exists(Configuration c | result = pragma[only_bind_into](c) and conf = pragma[only_bind_into](c))
} }
private predicate nodeMayUseSummary( pragma[nomagic]
NodeEx n, FlowState state, AccessPathApprox apa, Configuration config private predicate nodeMayUseSummary0(
NodeEx n, DataFlowCallable c, FlowState state, AccessPathApprox apa, Configuration config
) { ) {
exists(DataFlowCallable c, AccessPathApprox apa0 | exists(AccessPathApprox apa0 |
Stage4::parameterMayFlowThrough(_, c, apa, _) and Stage4::parameterMayFlowThrough(_, c, _, _) and
Stage4::revFlow(n, state, true, _, apa0, config) and Stage4::revFlow(n, state, true, _, apa0, config) and
Stage4::fwdFlow(n, state, any(CallContextCall ccc), TAccessPathApproxSome(apa), apa0, config) and Stage4::fwdFlow(n, state, any(CallContextCall ccc), TAccessPathApproxSome(apa), apa0, config) and
n.getEnclosingCallable() = c 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 = private newtype TSummaryCtx =
TSummaryCtxNone() or TSummaryCtxNone() or
TSummaryCtxSome(ParamNodeEx p, FlowState state, AccessPath ap) { TSummaryCtxSome(ParamNodeEx p, FlowState state, AccessPath ap) {
@@ -4257,6 +4353,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 * 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 * a subpath between `par` and `ret` with the connecting edges `arg -> par` and
@@ -4264,15 +4366,13 @@ private module Subpaths {
*/ */
predicate subpaths(PathNode arg, PathNodeImpl par, PathNodeImpl ret, PathNode out) { predicate subpaths(PathNode arg, PathNodeImpl par, PathNodeImpl ret, PathNode out) {
exists(ParamNodeEx p, NodeEx o, FlowState sout, AccessPath apout, PathNodeMid out0 | 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() = pragma[only_bind_into](out0) and
pragma[only_bind_into](arg).getASuccessor() = out0 and subpaths03(pragma[only_bind_into](arg), p, localStepToHidden*(ret), o, sout, apout) and
subpaths03(arg, p, localStepToHidden*(ret), o, sout, apout) and hasSuccessor(pragma[only_bind_into](arg), par, p) and
not ret.isHidden() and not ret.isHidden() and
par.getNodeEx() = p and pathNode(out0, o, sout, _, _, apout, _, _)
out0.getNodeEx() = o and |
out0.getState() = sout and out = out0 or out = out0.projectToSink()
out0.getAp() = apout and
(out = out0 or out = out0.projectToSink())
) )
} }
@@ -4609,6 +4709,10 @@ private module FlowExploration {
exists(PartialPathNodeRev mid | exists(PartialPathNodeRev mid |
revPartialPathStep(mid, node, state, sc1, sc2, sc3, ap, config) and revPartialPathStep(mid, node, state, sc1, sc2, sc3, ap, config) and
not clearsContentEx(node, ap.getHead()) and not clearsContentEx(node, ap.getHead()) and
(
notExpectsContent(node) or
expectsContentEx(node, ap.getHead())
) and
not fullBarrier(node, config) and not fullBarrier(node, config) and
not stateBarrier(node, state, config) and not stateBarrier(node, state, config) and
distSink(node.getEnclosingCallable(), config) <= config.explorationLimit() distSink(node.getEnclosingCallable(), config) <= config.explorationLimit()
@@ -4625,6 +4729,10 @@ private module FlowExploration {
not fullBarrier(node, config) and not fullBarrier(node, config) and
not stateBarrier(node, state, config) and not stateBarrier(node, state, config) and
not clearsContentEx(node, ap.getHead().getContent()) and not clearsContentEx(node, ap.getHead().getContent()) and
(
notExpectsContent(node) or
expectsContentEx(node, ap.getHead().getContent())
) and
if node.asNode() instanceof CastingNode if node.asNode() instanceof CastingNode
then compatibleTypes(node.getDataFlowType(), ap.getType()) then compatibleTypes(node.getDataFlowType(), ap.getType())
else any() else any()

View File

@@ -328,6 +328,9 @@ private module Cached {
cached cached
predicate clearsContentCached(Node n, ContentSet c) { clearsContent(n, c) } predicate clearsContentCached(Node n, ContentSet c) { clearsContent(n, c) }
cached
predicate expectsContentCached(Node n, ContentSet c) { expectsContent(n, c) }
cached cached
predicate isUnreachableInCallCached(Node n, DataFlowCall call) { isUnreachableInCall(n, call) } predicate isUnreachableInCallCached(Node n, DataFlowCall call) { isUnreachableInCall(n, call) }

View File

@@ -498,23 +498,35 @@ private predicate readSet(NodeEx node1, ContentSet c, NodeEx node2, Configuratio
} }
// inline to reduce fan-out via `getAReadContent` // inline to reduce fan-out via `getAReadContent`
pragma[inline] bindingset[c]
private predicate read(NodeEx node1, Content c, NodeEx node2, Configuration config) { private predicate read(NodeEx node1, Content c, NodeEx node2, Configuration config) {
exists(ContentSet cs | exists(ContentSet cs |
readSet(node1, cs, node2, config) and 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` // inline to reduce fan-out via `getAReadContent`
pragma[inline] bindingset[c]
private predicate clearsContentEx(NodeEx n, Content c) { private predicate clearsContentEx(NodeEx n, Content c) {
exists(ContentSet cs | exists(ContentSet cs |
clearsContentCached(n.asNode(), cs) and 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] pragma[nomagic]
private predicate store( private predicate store(
NodeEx node1, TypedContent tc, NodeEx node2, DataFlowType contentType, Configuration config NodeEx node1, TypedContent tc, NodeEx node2, DataFlowType contentType, Configuration config
@@ -793,7 +805,7 @@ private module Stage1 {
* by `revFlow`. * by `revFlow`.
*/ */
pragma[nomagic] pragma[nomagic]
private predicate revFlowIsReadAndStored(Content c, Configuration conf) { predicate revFlowIsReadAndStored(Content c, Configuration conf) {
revFlowConsCand(c, conf) and revFlowConsCand(c, conf) and
revFlowStore(c, _, _, conf) revFlowStore(c, _, _, conf)
} }
@@ -891,7 +903,7 @@ private module Stage1 {
pragma[nomagic] pragma[nomagic]
predicate readStepCand(NodeEx n1, Content c, NodeEx n2, Configuration config) { 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 read(n1, c, n2, pragma[only_bind_into](config)) and
revFlow(n2, pragma[only_bind_into](config)) revFlow(n2, pragma[only_bind_into](config))
} }
@@ -1181,11 +1193,26 @@ private module Stage2 {
private predicate flowIntoCall = flowIntoCallNodeCand1/5; 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] bindingset[node, state, ap, config]
private predicate filter(NodeEx node, FlowState state, Ap ap, Configuration config) { private predicate filter(NodeEx node, FlowState state, Ap ap, Configuration config) {
PrevStage::revFlowState(state, pragma[only_bind_into](config)) and PrevStage::revFlowState(state, pragma[only_bind_into](config)) and
exists(ap) 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] bindingset[ap, contentType]
@@ -1646,10 +1673,24 @@ private module Stage2 {
storeStepFwd(_, ap, tc, _, _, config) 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) 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] pragma[noinline]
private predicate parameterFlow( private predicate parameterFlow(
ParamNodeEx p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config ParamNodeEx p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config
@@ -1740,7 +1781,8 @@ private module LocalFlowBigStep {
private class FlowCheckNode extends NodeEx { private class FlowCheckNode extends NodeEx {
FlowCheckNode() { FlowCheckNode() {
castNode(this.asNode()) or castNode(this.asNode()) or
clearsContentCached(this.asNode(), _) clearsContentCached(this.asNode(), _) or
expectsContentCached(this.asNode(), _)
} }
} }
@@ -1979,6 +2021,16 @@ private module Stage3 {
clearContent(node, ap.getHead().getContent(), config) 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] pragma[nomagic]
private predicate castingNodeEx(NodeEx node) { node.asNode() instanceof CastingNode } private predicate castingNodeEx(NodeEx node) { node.asNode() instanceof CastingNode }
@@ -1987,7 +2039,12 @@ private module Stage3 {
exists(state) and exists(state) and
exists(config) and exists(config) and
not clear(node, ap, 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] bindingset[ap, contentType]
@@ -2452,10 +2509,24 @@ private module Stage3 {
storeStepFwd(_, ap, tc, _, _, config) 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) 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] pragma[noinline]
private predicate parameterFlow( private predicate parameterFlow(
ParamNodeEx p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config ParamNodeEx p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config
@@ -3279,10 +3350,24 @@ private module Stage4 {
storeStepFwd(_, ap, tc, _, _, config) 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) 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] pragma[noinline]
private predicate parameterFlow( private predicate parameterFlow(
ParamNodeEx p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config ParamNodeEx p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config
@@ -3351,17 +3436,28 @@ private Configuration unbindConf(Configuration conf) {
exists(Configuration c | result = pragma[only_bind_into](c) and conf = pragma[only_bind_into](c)) exists(Configuration c | result = pragma[only_bind_into](c) and conf = pragma[only_bind_into](c))
} }
private predicate nodeMayUseSummary( pragma[nomagic]
NodeEx n, FlowState state, AccessPathApprox apa, Configuration config private predicate nodeMayUseSummary0(
NodeEx n, DataFlowCallable c, FlowState state, AccessPathApprox apa, Configuration config
) { ) {
exists(DataFlowCallable c, AccessPathApprox apa0 | exists(AccessPathApprox apa0 |
Stage4::parameterMayFlowThrough(_, c, apa, _) and Stage4::parameterMayFlowThrough(_, c, _, _) and
Stage4::revFlow(n, state, true, _, apa0, config) and Stage4::revFlow(n, state, true, _, apa0, config) and
Stage4::fwdFlow(n, state, any(CallContextCall ccc), TAccessPathApproxSome(apa), apa0, config) and Stage4::fwdFlow(n, state, any(CallContextCall ccc), TAccessPathApproxSome(apa), apa0, config) and
n.getEnclosingCallable() = c 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 = private newtype TSummaryCtx =
TSummaryCtxNone() or TSummaryCtxNone() or
TSummaryCtxSome(ParamNodeEx p, FlowState state, AccessPath ap) { TSummaryCtxSome(ParamNodeEx p, FlowState state, AccessPath ap) {
@@ -4257,6 +4353,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 * 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 * a subpath between `par` and `ret` with the connecting edges `arg -> par` and
@@ -4264,15 +4366,13 @@ private module Subpaths {
*/ */
predicate subpaths(PathNode arg, PathNodeImpl par, PathNodeImpl ret, PathNode out) { predicate subpaths(PathNode arg, PathNodeImpl par, PathNodeImpl ret, PathNode out) {
exists(ParamNodeEx p, NodeEx o, FlowState sout, AccessPath apout, PathNodeMid out0 | 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() = pragma[only_bind_into](out0) and
pragma[only_bind_into](arg).getASuccessor() = out0 and subpaths03(pragma[only_bind_into](arg), p, localStepToHidden*(ret), o, sout, apout) and
subpaths03(arg, p, localStepToHidden*(ret), o, sout, apout) and hasSuccessor(pragma[only_bind_into](arg), par, p) and
not ret.isHidden() and not ret.isHidden() and
par.getNodeEx() = p and pathNode(out0, o, sout, _, _, apout, _, _)
out0.getNodeEx() = o and |
out0.getState() = sout and out = out0 or out = out0.projectToSink()
out0.getAp() = apout and
(out = out0 or out = out0.projectToSink())
) )
} }
@@ -4609,6 +4709,10 @@ private module FlowExploration {
exists(PartialPathNodeRev mid | exists(PartialPathNodeRev mid |
revPartialPathStep(mid, node, state, sc1, sc2, sc3, ap, config) and revPartialPathStep(mid, node, state, sc1, sc2, sc3, ap, config) and
not clearsContentEx(node, ap.getHead()) and not clearsContentEx(node, ap.getHead()) and
(
notExpectsContent(node) or
expectsContentEx(node, ap.getHead())
) and
not fullBarrier(node, config) and not fullBarrier(node, config) and
not stateBarrier(node, state, config) and not stateBarrier(node, state, config) and
distSink(node.getEnclosingCallable(), config) <= config.explorationLimit() distSink(node.getEnclosingCallable(), config) <= config.explorationLimit()
@@ -4625,6 +4729,10 @@ private module FlowExploration {
not fullBarrier(node, config) and not fullBarrier(node, config) and
not stateBarrier(node, state, config) and not stateBarrier(node, state, config) and
not clearsContentEx(node, ap.getHead().getContent()) and not clearsContentEx(node, ap.getHead().getContent()) and
(
notExpectsContent(node) or
expectsContentEx(node, ap.getHead().getContent())
) and
if node.asNode() instanceof CastingNode if node.asNode() instanceof CastingNode
then compatibleTypes(node.getDataFlowType(), ap.getType()) then compatibleTypes(node.getDataFlowType(), ap.getType())
else any() else any()

View File

@@ -198,6 +198,12 @@ predicate clearsContent(Node n, Content c) {
none() // stub implementation 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. */ /** Gets the type of `n` used for type pruning. */
Type getNodeType(Node n) { Type getNodeType(Node n) {
suppressUnusedNode(n) and suppressUnusedNode(n) and

View File

@@ -498,23 +498,35 @@ private predicate readSet(NodeEx node1, ContentSet c, NodeEx node2, Configuratio
} }
// inline to reduce fan-out via `getAReadContent` // inline to reduce fan-out via `getAReadContent`
pragma[inline] bindingset[c]
private predicate read(NodeEx node1, Content c, NodeEx node2, Configuration config) { private predicate read(NodeEx node1, Content c, NodeEx node2, Configuration config) {
exists(ContentSet cs | exists(ContentSet cs |
readSet(node1, cs, node2, config) and 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` // inline to reduce fan-out via `getAReadContent`
pragma[inline] bindingset[c]
private predicate clearsContentEx(NodeEx n, Content c) { private predicate clearsContentEx(NodeEx n, Content c) {
exists(ContentSet cs | exists(ContentSet cs |
clearsContentCached(n.asNode(), cs) and 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] pragma[nomagic]
private predicate store( private predicate store(
NodeEx node1, TypedContent tc, NodeEx node2, DataFlowType contentType, Configuration config NodeEx node1, TypedContent tc, NodeEx node2, DataFlowType contentType, Configuration config
@@ -793,7 +805,7 @@ private module Stage1 {
* by `revFlow`. * by `revFlow`.
*/ */
pragma[nomagic] pragma[nomagic]
private predicate revFlowIsReadAndStored(Content c, Configuration conf) { predicate revFlowIsReadAndStored(Content c, Configuration conf) {
revFlowConsCand(c, conf) and revFlowConsCand(c, conf) and
revFlowStore(c, _, _, conf) revFlowStore(c, _, _, conf)
} }
@@ -891,7 +903,7 @@ private module Stage1 {
pragma[nomagic] pragma[nomagic]
predicate readStepCand(NodeEx n1, Content c, NodeEx n2, Configuration config) { 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 read(n1, c, n2, pragma[only_bind_into](config)) and
revFlow(n2, pragma[only_bind_into](config)) revFlow(n2, pragma[only_bind_into](config))
} }
@@ -1181,11 +1193,26 @@ private module Stage2 {
private predicate flowIntoCall = flowIntoCallNodeCand1/5; 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] bindingset[node, state, ap, config]
private predicate filter(NodeEx node, FlowState state, Ap ap, Configuration config) { private predicate filter(NodeEx node, FlowState state, Ap ap, Configuration config) {
PrevStage::revFlowState(state, pragma[only_bind_into](config)) and PrevStage::revFlowState(state, pragma[only_bind_into](config)) and
exists(ap) 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] bindingset[ap, contentType]
@@ -1646,10 +1673,24 @@ private module Stage2 {
storeStepFwd(_, ap, tc, _, _, config) 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) 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] pragma[noinline]
private predicate parameterFlow( private predicate parameterFlow(
ParamNodeEx p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config ParamNodeEx p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config
@@ -1740,7 +1781,8 @@ private module LocalFlowBigStep {
private class FlowCheckNode extends NodeEx { private class FlowCheckNode extends NodeEx {
FlowCheckNode() { FlowCheckNode() {
castNode(this.asNode()) or castNode(this.asNode()) or
clearsContentCached(this.asNode(), _) clearsContentCached(this.asNode(), _) or
expectsContentCached(this.asNode(), _)
} }
} }
@@ -1979,6 +2021,16 @@ private module Stage3 {
clearContent(node, ap.getHead().getContent(), config) 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] pragma[nomagic]
private predicate castingNodeEx(NodeEx node) { node.asNode() instanceof CastingNode } private predicate castingNodeEx(NodeEx node) { node.asNode() instanceof CastingNode }
@@ -1987,7 +2039,12 @@ private module Stage3 {
exists(state) and exists(state) and
exists(config) and exists(config) and
not clear(node, ap, 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] bindingset[ap, contentType]
@@ -2452,10 +2509,24 @@ private module Stage3 {
storeStepFwd(_, ap, tc, _, _, config) 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) 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] pragma[noinline]
private predicate parameterFlow( private predicate parameterFlow(
ParamNodeEx p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config ParamNodeEx p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config
@@ -3279,10 +3350,24 @@ private module Stage4 {
storeStepFwd(_, ap, tc, _, _, config) 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) 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] pragma[noinline]
private predicate parameterFlow( private predicate parameterFlow(
ParamNodeEx p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config ParamNodeEx p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config
@@ -3351,17 +3436,28 @@ private Configuration unbindConf(Configuration conf) {
exists(Configuration c | result = pragma[only_bind_into](c) and conf = pragma[only_bind_into](c)) exists(Configuration c | result = pragma[only_bind_into](c) and conf = pragma[only_bind_into](c))
} }
private predicate nodeMayUseSummary( pragma[nomagic]
NodeEx n, FlowState state, AccessPathApprox apa, Configuration config private predicate nodeMayUseSummary0(
NodeEx n, DataFlowCallable c, FlowState state, AccessPathApprox apa, Configuration config
) { ) {
exists(DataFlowCallable c, AccessPathApprox apa0 | exists(AccessPathApprox apa0 |
Stage4::parameterMayFlowThrough(_, c, apa, _) and Stage4::parameterMayFlowThrough(_, c, _, _) and
Stage4::revFlow(n, state, true, _, apa0, config) and Stage4::revFlow(n, state, true, _, apa0, config) and
Stage4::fwdFlow(n, state, any(CallContextCall ccc), TAccessPathApproxSome(apa), apa0, config) and Stage4::fwdFlow(n, state, any(CallContextCall ccc), TAccessPathApproxSome(apa), apa0, config) and
n.getEnclosingCallable() = c 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 = private newtype TSummaryCtx =
TSummaryCtxNone() or TSummaryCtxNone() or
TSummaryCtxSome(ParamNodeEx p, FlowState state, AccessPath ap) { TSummaryCtxSome(ParamNodeEx p, FlowState state, AccessPath ap) {
@@ -4257,6 +4353,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 * 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 * a subpath between `par` and `ret` with the connecting edges `arg -> par` and
@@ -4264,15 +4366,13 @@ private module Subpaths {
*/ */
predicate subpaths(PathNode arg, PathNodeImpl par, PathNodeImpl ret, PathNode out) { predicate subpaths(PathNode arg, PathNodeImpl par, PathNodeImpl ret, PathNode out) {
exists(ParamNodeEx p, NodeEx o, FlowState sout, AccessPath apout, PathNodeMid out0 | 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() = pragma[only_bind_into](out0) and
pragma[only_bind_into](arg).getASuccessor() = out0 and subpaths03(pragma[only_bind_into](arg), p, localStepToHidden*(ret), o, sout, apout) and
subpaths03(arg, p, localStepToHidden*(ret), o, sout, apout) and hasSuccessor(pragma[only_bind_into](arg), par, p) and
not ret.isHidden() and not ret.isHidden() and
par.getNodeEx() = p and pathNode(out0, o, sout, _, _, apout, _, _)
out0.getNodeEx() = o and |
out0.getState() = sout and out = out0 or out = out0.projectToSink()
out0.getAp() = apout and
(out = out0 or out = out0.projectToSink())
) )
} }
@@ -4609,6 +4709,10 @@ private module FlowExploration {
exists(PartialPathNodeRev mid | exists(PartialPathNodeRev mid |
revPartialPathStep(mid, node, state, sc1, sc2, sc3, ap, config) and revPartialPathStep(mid, node, state, sc1, sc2, sc3, ap, config) and
not clearsContentEx(node, ap.getHead()) and not clearsContentEx(node, ap.getHead()) and
(
notExpectsContent(node) or
expectsContentEx(node, ap.getHead())
) and
not fullBarrier(node, config) and not fullBarrier(node, config) and
not stateBarrier(node, state, config) and not stateBarrier(node, state, config) and
distSink(node.getEnclosingCallable(), config) <= config.explorationLimit() distSink(node.getEnclosingCallable(), config) <= config.explorationLimit()
@@ -4625,6 +4729,10 @@ private module FlowExploration {
not fullBarrier(node, config) and not fullBarrier(node, config) and
not stateBarrier(node, state, config) and not stateBarrier(node, state, config) and
not clearsContentEx(node, ap.getHead().getContent()) and not clearsContentEx(node, ap.getHead().getContent()) and
(
notExpectsContent(node) or
expectsContentEx(node, ap.getHead().getContent())
) and
if node.asNode() instanceof CastingNode if node.asNode() instanceof CastingNode
then compatibleTypes(node.getDataFlowType(), ap.getType()) then compatibleTypes(node.getDataFlowType(), ap.getType())
else any() else any()

View File

@@ -498,23 +498,35 @@ private predicate readSet(NodeEx node1, ContentSet c, NodeEx node2, Configuratio
} }
// inline to reduce fan-out via `getAReadContent` // inline to reduce fan-out via `getAReadContent`
pragma[inline] bindingset[c]
private predicate read(NodeEx node1, Content c, NodeEx node2, Configuration config) { private predicate read(NodeEx node1, Content c, NodeEx node2, Configuration config) {
exists(ContentSet cs | exists(ContentSet cs |
readSet(node1, cs, node2, config) and 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` // inline to reduce fan-out via `getAReadContent`
pragma[inline] bindingset[c]
private predicate clearsContentEx(NodeEx n, Content c) { private predicate clearsContentEx(NodeEx n, Content c) {
exists(ContentSet cs | exists(ContentSet cs |
clearsContentCached(n.asNode(), cs) and 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] pragma[nomagic]
private predicate store( private predicate store(
NodeEx node1, TypedContent tc, NodeEx node2, DataFlowType contentType, Configuration config NodeEx node1, TypedContent tc, NodeEx node2, DataFlowType contentType, Configuration config
@@ -793,7 +805,7 @@ private module Stage1 {
* by `revFlow`. * by `revFlow`.
*/ */
pragma[nomagic] pragma[nomagic]
private predicate revFlowIsReadAndStored(Content c, Configuration conf) { predicate revFlowIsReadAndStored(Content c, Configuration conf) {
revFlowConsCand(c, conf) and revFlowConsCand(c, conf) and
revFlowStore(c, _, _, conf) revFlowStore(c, _, _, conf)
} }
@@ -891,7 +903,7 @@ private module Stage1 {
pragma[nomagic] pragma[nomagic]
predicate readStepCand(NodeEx n1, Content c, NodeEx n2, Configuration config) { 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 read(n1, c, n2, pragma[only_bind_into](config)) and
revFlow(n2, pragma[only_bind_into](config)) revFlow(n2, pragma[only_bind_into](config))
} }
@@ -1181,11 +1193,26 @@ private module Stage2 {
private predicate flowIntoCall = flowIntoCallNodeCand1/5; 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] bindingset[node, state, ap, config]
private predicate filter(NodeEx node, FlowState state, Ap ap, Configuration config) { private predicate filter(NodeEx node, FlowState state, Ap ap, Configuration config) {
PrevStage::revFlowState(state, pragma[only_bind_into](config)) and PrevStage::revFlowState(state, pragma[only_bind_into](config)) and
exists(ap) 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] bindingset[ap, contentType]
@@ -1646,10 +1673,24 @@ private module Stage2 {
storeStepFwd(_, ap, tc, _, _, config) 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) 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] pragma[noinline]
private predicate parameterFlow( private predicate parameterFlow(
ParamNodeEx p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config ParamNodeEx p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config
@@ -1740,7 +1781,8 @@ private module LocalFlowBigStep {
private class FlowCheckNode extends NodeEx { private class FlowCheckNode extends NodeEx {
FlowCheckNode() { FlowCheckNode() {
castNode(this.asNode()) or castNode(this.asNode()) or
clearsContentCached(this.asNode(), _) clearsContentCached(this.asNode(), _) or
expectsContentCached(this.asNode(), _)
} }
} }
@@ -1979,6 +2021,16 @@ private module Stage3 {
clearContent(node, ap.getHead().getContent(), config) 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] pragma[nomagic]
private predicate castingNodeEx(NodeEx node) { node.asNode() instanceof CastingNode } private predicate castingNodeEx(NodeEx node) { node.asNode() instanceof CastingNode }
@@ -1987,7 +2039,12 @@ private module Stage3 {
exists(state) and exists(state) and
exists(config) and exists(config) and
not clear(node, ap, 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] bindingset[ap, contentType]
@@ -2452,10 +2509,24 @@ private module Stage3 {
storeStepFwd(_, ap, tc, _, _, config) 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) 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] pragma[noinline]
private predicate parameterFlow( private predicate parameterFlow(
ParamNodeEx p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config ParamNodeEx p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config
@@ -3279,10 +3350,24 @@ private module Stage4 {
storeStepFwd(_, ap, tc, _, _, config) 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) 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] pragma[noinline]
private predicate parameterFlow( private predicate parameterFlow(
ParamNodeEx p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config ParamNodeEx p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config
@@ -3351,17 +3436,28 @@ private Configuration unbindConf(Configuration conf) {
exists(Configuration c | result = pragma[only_bind_into](c) and conf = pragma[only_bind_into](c)) exists(Configuration c | result = pragma[only_bind_into](c) and conf = pragma[only_bind_into](c))
} }
private predicate nodeMayUseSummary( pragma[nomagic]
NodeEx n, FlowState state, AccessPathApprox apa, Configuration config private predicate nodeMayUseSummary0(
NodeEx n, DataFlowCallable c, FlowState state, AccessPathApprox apa, Configuration config
) { ) {
exists(DataFlowCallable c, AccessPathApprox apa0 | exists(AccessPathApprox apa0 |
Stage4::parameterMayFlowThrough(_, c, apa, _) and Stage4::parameterMayFlowThrough(_, c, _, _) and
Stage4::revFlow(n, state, true, _, apa0, config) and Stage4::revFlow(n, state, true, _, apa0, config) and
Stage4::fwdFlow(n, state, any(CallContextCall ccc), TAccessPathApproxSome(apa), apa0, config) and Stage4::fwdFlow(n, state, any(CallContextCall ccc), TAccessPathApproxSome(apa), apa0, config) and
n.getEnclosingCallable() = c 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 = private newtype TSummaryCtx =
TSummaryCtxNone() or TSummaryCtxNone() or
TSummaryCtxSome(ParamNodeEx p, FlowState state, AccessPath ap) { TSummaryCtxSome(ParamNodeEx p, FlowState state, AccessPath ap) {
@@ -4257,6 +4353,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 * 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 * a subpath between `par` and `ret` with the connecting edges `arg -> par` and
@@ -4264,15 +4366,13 @@ private module Subpaths {
*/ */
predicate subpaths(PathNode arg, PathNodeImpl par, PathNodeImpl ret, PathNode out) { predicate subpaths(PathNode arg, PathNodeImpl par, PathNodeImpl ret, PathNode out) {
exists(ParamNodeEx p, NodeEx o, FlowState sout, AccessPath apout, PathNodeMid out0 | 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() = pragma[only_bind_into](out0) and
pragma[only_bind_into](arg).getASuccessor() = out0 and subpaths03(pragma[only_bind_into](arg), p, localStepToHidden*(ret), o, sout, apout) and
subpaths03(arg, p, localStepToHidden*(ret), o, sout, apout) and hasSuccessor(pragma[only_bind_into](arg), par, p) and
not ret.isHidden() and not ret.isHidden() and
par.getNodeEx() = p and pathNode(out0, o, sout, _, _, apout, _, _)
out0.getNodeEx() = o and |
out0.getState() = sout and out = out0 or out = out0.projectToSink()
out0.getAp() = apout and
(out = out0 or out = out0.projectToSink())
) )
} }
@@ -4609,6 +4709,10 @@ private module FlowExploration {
exists(PartialPathNodeRev mid | exists(PartialPathNodeRev mid |
revPartialPathStep(mid, node, state, sc1, sc2, sc3, ap, config) and revPartialPathStep(mid, node, state, sc1, sc2, sc3, ap, config) and
not clearsContentEx(node, ap.getHead()) and not clearsContentEx(node, ap.getHead()) and
(
notExpectsContent(node) or
expectsContentEx(node, ap.getHead())
) and
not fullBarrier(node, config) and not fullBarrier(node, config) and
not stateBarrier(node, state, config) and not stateBarrier(node, state, config) and
distSink(node.getEnclosingCallable(), config) <= config.explorationLimit() distSink(node.getEnclosingCallable(), config) <= config.explorationLimit()
@@ -4625,6 +4729,10 @@ private module FlowExploration {
not fullBarrier(node, config) and not fullBarrier(node, config) and
not stateBarrier(node, state, config) and not stateBarrier(node, state, config) and
not clearsContentEx(node, ap.getHead().getContent()) and not clearsContentEx(node, ap.getHead().getContent()) and
(
notExpectsContent(node) or
expectsContentEx(node, ap.getHead().getContent())
) and
if node.asNode() instanceof CastingNode if node.asNode() instanceof CastingNode
then compatibleTypes(node.getDataFlowType(), ap.getType()) then compatibleTypes(node.getDataFlowType(), ap.getType())
else any() else any()

View File

@@ -498,23 +498,35 @@ private predicate readSet(NodeEx node1, ContentSet c, NodeEx node2, Configuratio
} }
// inline to reduce fan-out via `getAReadContent` // inline to reduce fan-out via `getAReadContent`
pragma[inline] bindingset[c]
private predicate read(NodeEx node1, Content c, NodeEx node2, Configuration config) { private predicate read(NodeEx node1, Content c, NodeEx node2, Configuration config) {
exists(ContentSet cs | exists(ContentSet cs |
readSet(node1, cs, node2, config) and 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` // inline to reduce fan-out via `getAReadContent`
pragma[inline] bindingset[c]
private predicate clearsContentEx(NodeEx n, Content c) { private predicate clearsContentEx(NodeEx n, Content c) {
exists(ContentSet cs | exists(ContentSet cs |
clearsContentCached(n.asNode(), cs) and 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] pragma[nomagic]
private predicate store( private predicate store(
NodeEx node1, TypedContent tc, NodeEx node2, DataFlowType contentType, Configuration config NodeEx node1, TypedContent tc, NodeEx node2, DataFlowType contentType, Configuration config
@@ -793,7 +805,7 @@ private module Stage1 {
* by `revFlow`. * by `revFlow`.
*/ */
pragma[nomagic] pragma[nomagic]
private predicate revFlowIsReadAndStored(Content c, Configuration conf) { predicate revFlowIsReadAndStored(Content c, Configuration conf) {
revFlowConsCand(c, conf) and revFlowConsCand(c, conf) and
revFlowStore(c, _, _, conf) revFlowStore(c, _, _, conf)
} }
@@ -891,7 +903,7 @@ private module Stage1 {
pragma[nomagic] pragma[nomagic]
predicate readStepCand(NodeEx n1, Content c, NodeEx n2, Configuration config) { 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 read(n1, c, n2, pragma[only_bind_into](config)) and
revFlow(n2, pragma[only_bind_into](config)) revFlow(n2, pragma[only_bind_into](config))
} }
@@ -1181,11 +1193,26 @@ private module Stage2 {
private predicate flowIntoCall = flowIntoCallNodeCand1/5; 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] bindingset[node, state, ap, config]
private predicate filter(NodeEx node, FlowState state, Ap ap, Configuration config) { private predicate filter(NodeEx node, FlowState state, Ap ap, Configuration config) {
PrevStage::revFlowState(state, pragma[only_bind_into](config)) and PrevStage::revFlowState(state, pragma[only_bind_into](config)) and
exists(ap) 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] bindingset[ap, contentType]
@@ -1646,10 +1673,24 @@ private module Stage2 {
storeStepFwd(_, ap, tc, _, _, config) 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) 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] pragma[noinline]
private predicate parameterFlow( private predicate parameterFlow(
ParamNodeEx p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config ParamNodeEx p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config
@@ -1740,7 +1781,8 @@ private module LocalFlowBigStep {
private class FlowCheckNode extends NodeEx { private class FlowCheckNode extends NodeEx {
FlowCheckNode() { FlowCheckNode() {
castNode(this.asNode()) or castNode(this.asNode()) or
clearsContentCached(this.asNode(), _) clearsContentCached(this.asNode(), _) or
expectsContentCached(this.asNode(), _)
} }
} }
@@ -1979,6 +2021,16 @@ private module Stage3 {
clearContent(node, ap.getHead().getContent(), config) 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] pragma[nomagic]
private predicate castingNodeEx(NodeEx node) { node.asNode() instanceof CastingNode } private predicate castingNodeEx(NodeEx node) { node.asNode() instanceof CastingNode }
@@ -1987,7 +2039,12 @@ private module Stage3 {
exists(state) and exists(state) and
exists(config) and exists(config) and
not clear(node, ap, 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] bindingset[ap, contentType]
@@ -2452,10 +2509,24 @@ private module Stage3 {
storeStepFwd(_, ap, tc, _, _, config) 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) 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] pragma[noinline]
private predicate parameterFlow( private predicate parameterFlow(
ParamNodeEx p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config ParamNodeEx p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config
@@ -3279,10 +3350,24 @@ private module Stage4 {
storeStepFwd(_, ap, tc, _, _, config) 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) 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] pragma[noinline]
private predicate parameterFlow( private predicate parameterFlow(
ParamNodeEx p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config ParamNodeEx p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config
@@ -3351,17 +3436,28 @@ private Configuration unbindConf(Configuration conf) {
exists(Configuration c | result = pragma[only_bind_into](c) and conf = pragma[only_bind_into](c)) exists(Configuration c | result = pragma[only_bind_into](c) and conf = pragma[only_bind_into](c))
} }
private predicate nodeMayUseSummary( pragma[nomagic]
NodeEx n, FlowState state, AccessPathApprox apa, Configuration config private predicate nodeMayUseSummary0(
NodeEx n, DataFlowCallable c, FlowState state, AccessPathApprox apa, Configuration config
) { ) {
exists(DataFlowCallable c, AccessPathApprox apa0 | exists(AccessPathApprox apa0 |
Stage4::parameterMayFlowThrough(_, c, apa, _) and Stage4::parameterMayFlowThrough(_, c, _, _) and
Stage4::revFlow(n, state, true, _, apa0, config) and Stage4::revFlow(n, state, true, _, apa0, config) and
Stage4::fwdFlow(n, state, any(CallContextCall ccc), TAccessPathApproxSome(apa), apa0, config) and Stage4::fwdFlow(n, state, any(CallContextCall ccc), TAccessPathApproxSome(apa), apa0, config) and
n.getEnclosingCallable() = c 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 = private newtype TSummaryCtx =
TSummaryCtxNone() or TSummaryCtxNone() or
TSummaryCtxSome(ParamNodeEx p, FlowState state, AccessPath ap) { TSummaryCtxSome(ParamNodeEx p, FlowState state, AccessPath ap) {
@@ -4257,6 +4353,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 * 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 * a subpath between `par` and `ret` with the connecting edges `arg -> par` and
@@ -4264,15 +4366,13 @@ private module Subpaths {
*/ */
predicate subpaths(PathNode arg, PathNodeImpl par, PathNodeImpl ret, PathNode out) { predicate subpaths(PathNode arg, PathNodeImpl par, PathNodeImpl ret, PathNode out) {
exists(ParamNodeEx p, NodeEx o, FlowState sout, AccessPath apout, PathNodeMid out0 | 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() = pragma[only_bind_into](out0) and
pragma[only_bind_into](arg).getASuccessor() = out0 and subpaths03(pragma[only_bind_into](arg), p, localStepToHidden*(ret), o, sout, apout) and
subpaths03(arg, p, localStepToHidden*(ret), o, sout, apout) and hasSuccessor(pragma[only_bind_into](arg), par, p) and
not ret.isHidden() and not ret.isHidden() and
par.getNodeEx() = p and pathNode(out0, o, sout, _, _, apout, _, _)
out0.getNodeEx() = o and |
out0.getState() = sout and out = out0 or out = out0.projectToSink()
out0.getAp() = apout and
(out = out0 or out = out0.projectToSink())
) )
} }
@@ -4609,6 +4709,10 @@ private module FlowExploration {
exists(PartialPathNodeRev mid | exists(PartialPathNodeRev mid |
revPartialPathStep(mid, node, state, sc1, sc2, sc3, ap, config) and revPartialPathStep(mid, node, state, sc1, sc2, sc3, ap, config) and
not clearsContentEx(node, ap.getHead()) and not clearsContentEx(node, ap.getHead()) and
(
notExpectsContent(node) or
expectsContentEx(node, ap.getHead())
) and
not fullBarrier(node, config) and not fullBarrier(node, config) and
not stateBarrier(node, state, config) and not stateBarrier(node, state, config) and
distSink(node.getEnclosingCallable(), config) <= config.explorationLimit() distSink(node.getEnclosingCallable(), config) <= config.explorationLimit()
@@ -4625,6 +4729,10 @@ private module FlowExploration {
not fullBarrier(node, config) and not fullBarrier(node, config) and
not stateBarrier(node, state, config) and not stateBarrier(node, state, config) and
not clearsContentEx(node, ap.getHead().getContent()) and not clearsContentEx(node, ap.getHead().getContent()) and
(
notExpectsContent(node) or
expectsContentEx(node, ap.getHead().getContent())
) and
if node.asNode() instanceof CastingNode if node.asNode() instanceof CastingNode
then compatibleTypes(node.getDataFlowType(), ap.getType()) then compatibleTypes(node.getDataFlowType(), ap.getType())
else any() else any()

View File

@@ -498,23 +498,35 @@ private predicate readSet(NodeEx node1, ContentSet c, NodeEx node2, Configuratio
} }
// inline to reduce fan-out via `getAReadContent` // inline to reduce fan-out via `getAReadContent`
pragma[inline] bindingset[c]
private predicate read(NodeEx node1, Content c, NodeEx node2, Configuration config) { private predicate read(NodeEx node1, Content c, NodeEx node2, Configuration config) {
exists(ContentSet cs | exists(ContentSet cs |
readSet(node1, cs, node2, config) and 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` // inline to reduce fan-out via `getAReadContent`
pragma[inline] bindingset[c]
private predicate clearsContentEx(NodeEx n, Content c) { private predicate clearsContentEx(NodeEx n, Content c) {
exists(ContentSet cs | exists(ContentSet cs |
clearsContentCached(n.asNode(), cs) and 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] pragma[nomagic]
private predicate store( private predicate store(
NodeEx node1, TypedContent tc, NodeEx node2, DataFlowType contentType, Configuration config NodeEx node1, TypedContent tc, NodeEx node2, DataFlowType contentType, Configuration config
@@ -793,7 +805,7 @@ private module Stage1 {
* by `revFlow`. * by `revFlow`.
*/ */
pragma[nomagic] pragma[nomagic]
private predicate revFlowIsReadAndStored(Content c, Configuration conf) { predicate revFlowIsReadAndStored(Content c, Configuration conf) {
revFlowConsCand(c, conf) and revFlowConsCand(c, conf) and
revFlowStore(c, _, _, conf) revFlowStore(c, _, _, conf)
} }
@@ -891,7 +903,7 @@ private module Stage1 {
pragma[nomagic] pragma[nomagic]
predicate readStepCand(NodeEx n1, Content c, NodeEx n2, Configuration config) { 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 read(n1, c, n2, pragma[only_bind_into](config)) and
revFlow(n2, pragma[only_bind_into](config)) revFlow(n2, pragma[only_bind_into](config))
} }
@@ -1181,11 +1193,26 @@ private module Stage2 {
private predicate flowIntoCall = flowIntoCallNodeCand1/5; 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] bindingset[node, state, ap, config]
private predicate filter(NodeEx node, FlowState state, Ap ap, Configuration config) { private predicate filter(NodeEx node, FlowState state, Ap ap, Configuration config) {
PrevStage::revFlowState(state, pragma[only_bind_into](config)) and PrevStage::revFlowState(state, pragma[only_bind_into](config)) and
exists(ap) 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] bindingset[ap, contentType]
@@ -1646,10 +1673,24 @@ private module Stage2 {
storeStepFwd(_, ap, tc, _, _, config) 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) 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] pragma[noinline]
private predicate parameterFlow( private predicate parameterFlow(
ParamNodeEx p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config ParamNodeEx p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config
@@ -1740,7 +1781,8 @@ private module LocalFlowBigStep {
private class FlowCheckNode extends NodeEx { private class FlowCheckNode extends NodeEx {
FlowCheckNode() { FlowCheckNode() {
castNode(this.asNode()) or castNode(this.asNode()) or
clearsContentCached(this.asNode(), _) clearsContentCached(this.asNode(), _) or
expectsContentCached(this.asNode(), _)
} }
} }
@@ -1979,6 +2021,16 @@ private module Stage3 {
clearContent(node, ap.getHead().getContent(), config) 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] pragma[nomagic]
private predicate castingNodeEx(NodeEx node) { node.asNode() instanceof CastingNode } private predicate castingNodeEx(NodeEx node) { node.asNode() instanceof CastingNode }
@@ -1987,7 +2039,12 @@ private module Stage3 {
exists(state) and exists(state) and
exists(config) and exists(config) and
not clear(node, ap, 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] bindingset[ap, contentType]
@@ -2452,10 +2509,24 @@ private module Stage3 {
storeStepFwd(_, ap, tc, _, _, config) 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) 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] pragma[noinline]
private predicate parameterFlow( private predicate parameterFlow(
ParamNodeEx p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config ParamNodeEx p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config
@@ -3279,10 +3350,24 @@ private module Stage4 {
storeStepFwd(_, ap, tc, _, _, config) 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) 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] pragma[noinline]
private predicate parameterFlow( private predicate parameterFlow(
ParamNodeEx p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config ParamNodeEx p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config
@@ -3351,17 +3436,28 @@ private Configuration unbindConf(Configuration conf) {
exists(Configuration c | result = pragma[only_bind_into](c) and conf = pragma[only_bind_into](c)) exists(Configuration c | result = pragma[only_bind_into](c) and conf = pragma[only_bind_into](c))
} }
private predicate nodeMayUseSummary( pragma[nomagic]
NodeEx n, FlowState state, AccessPathApprox apa, Configuration config private predicate nodeMayUseSummary0(
NodeEx n, DataFlowCallable c, FlowState state, AccessPathApprox apa, Configuration config
) { ) {
exists(DataFlowCallable c, AccessPathApprox apa0 | exists(AccessPathApprox apa0 |
Stage4::parameterMayFlowThrough(_, c, apa, _) and Stage4::parameterMayFlowThrough(_, c, _, _) and
Stage4::revFlow(n, state, true, _, apa0, config) and Stage4::revFlow(n, state, true, _, apa0, config) and
Stage4::fwdFlow(n, state, any(CallContextCall ccc), TAccessPathApproxSome(apa), apa0, config) and Stage4::fwdFlow(n, state, any(CallContextCall ccc), TAccessPathApproxSome(apa), apa0, config) and
n.getEnclosingCallable() = c 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 = private newtype TSummaryCtx =
TSummaryCtxNone() or TSummaryCtxNone() or
TSummaryCtxSome(ParamNodeEx p, FlowState state, AccessPath ap) { TSummaryCtxSome(ParamNodeEx p, FlowState state, AccessPath ap) {
@@ -4257,6 +4353,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 * 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 * a subpath between `par` and `ret` with the connecting edges `arg -> par` and
@@ -4264,15 +4366,13 @@ private module Subpaths {
*/ */
predicate subpaths(PathNode arg, PathNodeImpl par, PathNodeImpl ret, PathNode out) { predicate subpaths(PathNode arg, PathNodeImpl par, PathNodeImpl ret, PathNode out) {
exists(ParamNodeEx p, NodeEx o, FlowState sout, AccessPath apout, PathNodeMid out0 | 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() = pragma[only_bind_into](out0) and
pragma[only_bind_into](arg).getASuccessor() = out0 and subpaths03(pragma[only_bind_into](arg), p, localStepToHidden*(ret), o, sout, apout) and
subpaths03(arg, p, localStepToHidden*(ret), o, sout, apout) and hasSuccessor(pragma[only_bind_into](arg), par, p) and
not ret.isHidden() and not ret.isHidden() and
par.getNodeEx() = p and pathNode(out0, o, sout, _, _, apout, _, _)
out0.getNodeEx() = o and |
out0.getState() = sout and out = out0 or out = out0.projectToSink()
out0.getAp() = apout and
(out = out0 or out = out0.projectToSink())
) )
} }
@@ -4609,6 +4709,10 @@ private module FlowExploration {
exists(PartialPathNodeRev mid | exists(PartialPathNodeRev mid |
revPartialPathStep(mid, node, state, sc1, sc2, sc3, ap, config) and revPartialPathStep(mid, node, state, sc1, sc2, sc3, ap, config) and
not clearsContentEx(node, ap.getHead()) and not clearsContentEx(node, ap.getHead()) and
(
notExpectsContent(node) or
expectsContentEx(node, ap.getHead())
) and
not fullBarrier(node, config) and not fullBarrier(node, config) and
not stateBarrier(node, state, config) and not stateBarrier(node, state, config) and
distSink(node.getEnclosingCallable(), config) <= config.explorationLimit() distSink(node.getEnclosingCallable(), config) <= config.explorationLimit()
@@ -4625,6 +4729,10 @@ private module FlowExploration {
not fullBarrier(node, config) and not fullBarrier(node, config) and
not stateBarrier(node, state, config) and not stateBarrier(node, state, config) and
not clearsContentEx(node, ap.getHead().getContent()) and not clearsContentEx(node, ap.getHead().getContent()) and
(
notExpectsContent(node) or
expectsContentEx(node, ap.getHead().getContent())
) and
if node.asNode() instanceof CastingNode if node.asNode() instanceof CastingNode
then compatibleTypes(node.getDataFlowType(), ap.getType()) then compatibleTypes(node.getDataFlowType(), ap.getType())
else any() else any()

View File

@@ -328,6 +328,9 @@ private module Cached {
cached cached
predicate clearsContentCached(Node n, ContentSet c) { clearsContent(n, c) } predicate clearsContentCached(Node n, ContentSet c) { clearsContent(n, c) }
cached
predicate expectsContentCached(Node n, ContentSet c) { expectsContent(n, c) }
cached cached
predicate isUnreachableInCallCached(Node n, DataFlowCall call) { isUnreachableInCall(n, call) } predicate isUnreachableInCallCached(Node n, DataFlowCall call) { isUnreachableInCall(n, call) }

View File

@@ -279,6 +279,12 @@ predicate clearsContent(Node n, Content c) {
none() // stub implementation 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. */ /** Gets the type of `n` used for type pruning. */
IRType getNodeType(Node n) { IRType getNodeType(Node n) {
suppressUnusedNode(n) and suppressUnusedNode(n) and

View File

@@ -155,7 +155,7 @@ class StrCopyBW extends BufferWriteCall {
// when result exists, it is an exact flow analysis // when result exists, it is an exact flow analysis
reason instanceof ValueFlowAnalysis and reason instanceof ValueFlowAnalysis and
result = result =
this.getArgument(this.getParamSrc()).(AnalysedString).getMaxLength() * this.getCharSize() this.getArgument(this.getParamSrc()).(AnalyzedString).getMaxLength() * this.getCharSize()
} }
override int getMaxData(BufferWriteEstimationReason reason) { override int getMaxData(BufferWriteEstimationReason reason) {
@@ -201,7 +201,7 @@ class StrCatBW extends BufferWriteCall {
// when result exists, it is an exact flow analysis // when result exists, it is an exact flow analysis
reason instanceof ValueFlowAnalysis and reason instanceof ValueFlowAnalysis and
result = result =
this.getArgument(this.getParamSrc()).(AnalysedString).getMaxLength() * this.getCharSize() this.getArgument(this.getParamSrc()).(AnalyzedString).getMaxLength() * this.getCharSize()
} }
override int getMaxData(BufferWriteEstimationReason reason) { override int getMaxData(BufferWriteEstimationReason reason) {

View File

@@ -5,8 +5,6 @@
import cpp import cpp
import semmle.code.cpp.controlflow.Dominance 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.SimpleRangeAnalysis
import semmle.code.cpp.rangeanalysis.RangeAnalysisUtils import semmle.code.cpp.rangeanalysis.RangeAnalysisUtils
import semmle.code.cpp.controlflow.Guards import semmle.code.cpp.controlflow.Guards

View File

@@ -1,3 +1,9 @@
## 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 ## 0.1.0
### Minor Analysis Improvements ### Minor Analysis Improvements

View File

@@ -14,9 +14,6 @@
*/ */
import cpp 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.IR
import semmle.code.cpp.ir.dataflow.MustFlow import semmle.code.cpp.ir.dataflow.MustFlow
import PathGraph import PathGraph

View File

@@ -2,7 +2,7 @@
* @name Suspicious call to memset * @name Suspicious call to memset
* @description Use of memset where the size argument is computed as the size of * @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 * 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. * portability.
* @kind problem * @kind problem
* @id cpp/suspicious-call-to-memset * @id cpp/suspicious-call-to-memset

View File

@@ -15,9 +15,6 @@
*/ */
import cpp 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.IR
import semmle.code.cpp.ir.dataflow.MustFlow import semmle.code.cpp.ir.dataflow.MustFlow
import PathGraph import PathGraph

View File

@@ -16,6 +16,7 @@ import cpp
import semmle.code.cpp.ir.dataflow.DataFlow import semmle.code.cpp.ir.dataflow.DataFlow
import DataFlow::PathGraph import DataFlow::PathGraph
import semmle.code.cpp.ir.IR import semmle.code.cpp.ir.IR
import semmle.code.cpp.valuenumbering.GlobalValueNumbering
/** /**
* A flow state representing a possible configuration of an XML object. * A flow state representing a possible configuration of an XML object.
@@ -57,42 +58,58 @@ class XercesDOMParserClass extends Class {
} }
/** /**
* Gets a valid flow state for `XercesDOMParser` flow. * The `SAXParser` class.
*/
class SaxParserClass extends Class {
SaxParserClass() { this.hasName("SAXParser") }
}
/**
* The `SAX2XMLReader` class.
*/
class Sax2XmlReader extends Class {
Sax2XmlReader() { this.hasName("SAX2XMLReader") }
}
/**
* Gets a valid flow state for `AbstractDOMParser` or `SAXParser` flow.
* *
* These flow states take the form `XercesDOM-A-B`, where: * These flow states take the form `Xerces-A-B`, where:
* - A is 1 if `setDisableDefaultEntityResolution` is `true`, 0 otherwise. * - A is 1 if `setDisableDefaultEntityResolution` is `true`, 0 otherwise.
* - B is 1 if `setCreateEntityReferenceNodes` is `true`, 0 otherwise. * - B is 1 if `setCreateEntityReferenceNodes` is `true`, 0 otherwise.
*/ */
predicate encodeXercesDOMFlowState( predicate encodeXercesFlowState(
string flowstate, int disabledDefaultEntityResolution, int createEntityReferenceNodes string flowstate, int disabledDefaultEntityResolution, int createEntityReferenceNodes
) { ) {
flowstate = "XercesDOM-0-0" and flowstate = "Xerces-0-0" and
disabledDefaultEntityResolution = 0 and disabledDefaultEntityResolution = 0 and
createEntityReferenceNodes = 0 createEntityReferenceNodes = 0
or or
flowstate = "XercesDOM-0-1" and flowstate = "Xerces-0-1" and
disabledDefaultEntityResolution = 0 and disabledDefaultEntityResolution = 0 and
createEntityReferenceNodes = 1 createEntityReferenceNodes = 1
or or
flowstate = "XercesDOM-1-0" and flowstate = "Xerces-1-0" and
disabledDefaultEntityResolution = 1 and disabledDefaultEntityResolution = 1 and
createEntityReferenceNodes = 0 createEntityReferenceNodes = 0
or or
flowstate = "XercesDOM-1-1" and flowstate = "Xerces-1-1" and
disabledDefaultEntityResolution = 1 and disabledDefaultEntityResolution = 1 and
createEntityReferenceNodes = 1 createEntityReferenceNodes = 1
} }
/** /**
* A flow state representing the configuration of a `XercesDOMParser` object. * A flow state representing the configuration of an `AbstractDOMParser` or
* `SAXParser` object.
*/ */
class XercesDOMParserFlowState extends XXEFlowState { class XercesFlowState extends XXEFlowState {
XercesDOMParserFlowState() { encodeXercesDOMFlowState(this, _, _) } XercesFlowState() { encodeXercesFlowState(this, _, _) }
} }
/** /**
* A flow state transformer for a call to * A flow state transformer for a call to
* `AbstractDOMParser.setDisableDefaultEntityResolution`. Transforms the flow * `AbstractDOMParser.setDisableDefaultEntityResolution` or
* `SAXParser.setDisableDefaultEntityResolution`. Transforms the flow
* state through the qualifier according to the setting in the parameter. * state through the qualifier according to the setting in the parameter.
*/ */
class DisableDefaultEntityResolutionTranformer extends XXEFlowStateTranformer { class DisableDefaultEntityResolutionTranformer extends XXEFlowStateTranformer {
@@ -101,7 +118,10 @@ class DisableDefaultEntityResolutionTranformer extends XXEFlowStateTranformer {
DisableDefaultEntityResolutionTranformer() { DisableDefaultEntityResolutionTranformer() {
exists(Call call, Function f | exists(Call call, Function f |
call.getTarget() = f and call.getTarget() = f and
f.getDeclaringType() instanceof AbstractDOMParserClass and (
f.getDeclaringType() instanceof AbstractDOMParserClass or
f.getDeclaringType() instanceof SaxParserClass
) and
f.hasName("setDisableDefaultEntityResolution") and f.hasName("setDisableDefaultEntityResolution") and
this = call.getQualifier() and this = call.getQualifier() and
newValue = call.getArgument(0) newValue = call.getArgument(0)
@@ -110,13 +130,13 @@ class DisableDefaultEntityResolutionTranformer extends XXEFlowStateTranformer {
final override XXEFlowState transform(XXEFlowState flowstate) { final override XXEFlowState transform(XXEFlowState flowstate) {
exists(int createEntityReferenceNodes | exists(int createEntityReferenceNodes |
encodeXercesDOMFlowState(flowstate, _, createEntityReferenceNodes) and encodeXercesFlowState(flowstate, _, createEntityReferenceNodes) and
( (
newValue.getValue().toInt() = 1 and // true globalValueNumber(newValue).getAnExpr().getValue().toInt() = 1 and // true
encodeXercesDOMFlowState(result, 1, createEntityReferenceNodes) encodeXercesFlowState(result, 1, createEntityReferenceNodes)
or or
not newValue.getValue().toInt() = 1 and // false or unknown not globalValueNumber(newValue).getAnExpr().getValue().toInt() = 1 and // false or unknown
encodeXercesDOMFlowState(result, 0, createEntityReferenceNodes) encodeXercesFlowState(result, 0, createEntityReferenceNodes)
) )
) )
} }
@@ -133,8 +153,7 @@ class CreateEntityReferenceNodesTranformer extends XXEFlowStateTranformer {
CreateEntityReferenceNodesTranformer() { CreateEntityReferenceNodesTranformer() {
exists(Call call, Function f | exists(Call call, Function f |
call.getTarget() = f and call.getTarget() = f and
f.getDeclaringType() instanceof AbstractDOMParserClass and f.getClassAndName("setCreateEntityReferenceNodes") instanceof AbstractDOMParserClass and
f.hasName("setCreateEntityReferenceNodes") and
this = call.getQualifier() and this = call.getQualifier() and
newValue = call.getArgument(0) newValue = call.getArgument(0)
) )
@@ -142,27 +161,76 @@ class CreateEntityReferenceNodesTranformer extends XXEFlowStateTranformer {
final override XXEFlowState transform(XXEFlowState flowstate) { final override XXEFlowState transform(XXEFlowState flowstate) {
exists(int disabledDefaultEntityResolution | exists(int disabledDefaultEntityResolution |
encodeXercesDOMFlowState(flowstate, disabledDefaultEntityResolution, _) and encodeXercesFlowState(flowstate, disabledDefaultEntityResolution, _) and
( (
newValue.getValue().toInt() = 1 and // true globalValueNumber(newValue).getAnExpr().getValue().toInt() = 1 and // true
encodeXercesDOMFlowState(result, disabledDefaultEntityResolution, 1) encodeXercesFlowState(result, disabledDefaultEntityResolution, 1)
or or
not newValue.getValue().toInt() = 1 and // false or unknown not globalValueNumber(newValue).getAnExpr().getValue().toInt() = 1 and // false or unknown
encodeXercesDOMFlowState(result, disabledDefaultEntityResolution, 0) encodeXercesFlowState(result, disabledDefaultEntityResolution, 0)
) )
) )
} }
} }
/** /**
* The `AbstractDOMParser.parse` method. * The `XMLUni.fgXercesDisableDefaultEntityResolution` constant.
*/ */
class ParseFunction extends Function { class FeatureDisableDefaultEntityResolution extends Variable {
ParseFunction() { this.getClassAndName("parse") instanceof AbstractDOMParserClass } FeatureDisableDefaultEntityResolution() {
this.getName() = "fgXercesDisableDefaultEntityResolution" and
this.getDeclaringType().getName() = "XMLUni"
}
} }
/** /**
* The `createLSParser` function that returns a newly created `LSParser` object. * 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 SetFeatureTranformer extends XXEFlowStateTranformer {
Expr newValue;
SetFeatureTranformer() {
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 `AbstractDOMParser.parse`, `SAXParser.parse` or `SAX2XMLReader.parse`
* method.
*/
class ParseFunction extends Function {
ParseFunction() {
this.getClassAndName("parse") instanceof AbstractDOMParserClass or
this.getClassAndName("parse") instanceof SaxParserClass or
this.getClassAndName("parse") instanceof Sax2XmlReader
}
}
/**
* The `createLSParser` function that returns a newly created `DOMLSParser`
* object.
*/ */
class CreateLSParser extends Function { class CreateLSParser extends Function {
CreateLSParser() { CreateLSParser() {
@@ -171,6 +239,53 @@ class CreateLSParser extends Function {
} }
} }
/**
* 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 *`.
}
}
/**
* 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"] }
}
/** /**
* A configuration for tracking XML objects and their states. * A configuration for tracking XML objects and their states.
*/ */
@@ -184,14 +299,47 @@ class XXEConfiguration extends DataFlow::Configuration {
call.getStaticCallTarget() = any(XercesDOMParserClass c).getAConstructor() and call.getStaticCallTarget() = any(XercesDOMParserClass c).getAConstructor() and
node.asInstruction().(WriteSideEffectInstruction).getDestinationAddress() = node.asInstruction().(WriteSideEffectInstruction).getDestinationAddress() =
call.getThisArgument() and call.getThisArgument() and
encodeXercesDOMFlowState(flowstate, 0, 1) // default configuration encodeXercesFlowState(flowstate, 0, 1) // default configuration
) )
or or
// source is the result of a call to `createLSParser`. // source is the result of a call to `createLSParser`.
exists(Call call | exists(Call call |
call.getTarget() instanceof CreateLSParser and call.getTarget() instanceof CreateLSParser and
call = node.asExpr() and call = node.asExpr() and
encodeXercesDOMFlowState(flowstate, 0, 1) // default configuration encodeXercesFlowState(flowstate, 0, 1) // default configuration
)
or
// 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
)
or
// 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
)
or
// 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
)
) )
} }
@@ -201,8 +349,15 @@ class XXEConfiguration extends DataFlow::Configuration {
call.getTarget() instanceof ParseFunction and call.getTarget() instanceof ParseFunction and
call.getQualifier() = node.asConvertedExpr() call.getQualifier() = node.asConvertedExpr()
) and ) and
flowstate instanceof XercesDOMParserFlowState and flowstate instanceof XercesFlowState and
not encodeXercesDOMFlowState(flowstate, 1, 1) // safe configuration not encodeXercesFlowState(flowstate, 1, 1) // safe configuration
or
// 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"
)
} }
override predicate isAdditionalFlowStep( override predicate isAdditionalFlowStep(

View File

@@ -0,0 +1,4 @@
---
category: minorAnalysis
---
* 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,4 +1,5 @@
--- ## 0.1.1
category: newQuery
--- ### 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. * 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

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

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

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

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

@@ -5,7 +5,7 @@
import cpp import cpp
from AnalysedString s, string str from AnalyzedString s, string str
where where
if s.(StringLiteral).getUnspecifiedType().(DerivedType).getBaseType() instanceof Wchar_t if s.(StringLiteral).getUnspecifiedType().(DerivedType).getBaseType() instanceof Wchar_t
then str = "[?]" then str = "[?]"

View File

@@ -1,80 +1,106 @@
edges edges
| tests.cpp:33:23:33:43 | XercesDOMParser output argument | tests.cpp:35:2:35:2 | p | | tests2.cpp:20:17:20:31 | SAXParser output argument | tests2.cpp:22:2:22:2 | p |
| tests.cpp:46:23:46:43 | XercesDOMParser output argument | tests.cpp:49:2:49:2 | p | | tests2.cpp:33:17:33:31 | SAXParser output argument | tests2.cpp:37:2:37:2 | p |
| tests.cpp:53:19:53:19 | VariableAddress [post update] | tests.cpp:55:2:55:2 | p | | tests3.cpp:23:21:23:53 | call to createXMLReader | tests3.cpp:25:2:25:2 | p |
| tests.cpp:53:23:53:43 | XercesDOMParser output argument | tests.cpp:53:19:53:19 | VariableAddress [post update] | | 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 |
| 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: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: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:57:2:57:2 | p | tests.cpp:58:2:58:2 | p |
| tests.cpp:69:23:69:43 | XercesDOMParser output argument | tests.cpp:69:19:69:19 | VariableAddress [post update] | | tests.cpp:58:2:58:2 | p | tests.cpp:59:2:59:2 | p |
| tests.cpp:71:2:71:2 | p | tests.cpp:72:2:72:2 | p | | tests.cpp:59:2:59:2 | p | tests.cpp:60:2:60:2 | p |
| tests.cpp:72:2:72:2 | p | tests.cpp:73:2:73:2 | p | | tests.cpp:66:23:66:43 | XercesDOMParser output argument | tests.cpp:69:2:69:2 | p |
| tests.cpp:73:2:73:2 | p | tests.cpp:74:2:74:2 | p | | tests.cpp:73:23:73:43 | XercesDOMParser output argument | tests.cpp:80:2:80:2 | p |
| tests.cpp:73:2:73:2 | p | tests.cpp:74:2:74:2 | p | | tests.cpp:85:24:85:44 | XercesDOMParser output argument | tests.cpp:88:3:88:3 | q |
| tests.cpp:74:2:74:2 | p | tests.cpp:75:2:75:2 | p | | tests.cpp:100:24:100:44 | XercesDOMParser output argument | tests.cpp:104:3:104:3 | q |
| tests.cpp:75:2:75:2 | p | tests.cpp:76:2:76:2 | p | | tests.cpp:112:39:112:39 | p | tests.cpp:113:2:113:2 | p |
| tests.cpp:76:2:76:2 | p | tests.cpp:77:2:77:2 | p | | tests.cpp:116:39:116:39 | p | tests.cpp:117:2:117:2 | p |
| tests.cpp:77:2:77:2 | p | tests.cpp:78:2:78:2 | p | | tests.cpp:122:23:122:43 | XercesDOMParser output argument | tests.cpp:126:18:126:18 | q |
| tests.cpp:84:23:84:43 | XercesDOMParser output argument | tests.cpp:87:2:87:2 | p | | tests.cpp:122:23:122:43 | XercesDOMParser output argument | tests.cpp:128:18:128:18 | q |
| tests.cpp:91:23:91:43 | XercesDOMParser output argument | tests.cpp:98:2:98:2 | p | | tests.cpp:126:18:126:18 | q | tests.cpp:112:39:112:39 | p |
| tests.cpp:103:24:103:44 | XercesDOMParser output argument | tests.cpp:106:3:106:3 | q | | tests.cpp:128:18:128:18 | q | tests.cpp:116:39:116:39 | p |
| 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 |
nodes nodes
| tests.cpp:33:23:33:43 | XercesDOMParser output argument | semmle.label | XercesDOMParser output argument | | tests2.cpp:20:17:20:31 | SAXParser output argument | semmle.label | SAXParser output argument |
| tests.cpp:35:2:35:2 | p | semmle.label | p | | tests2.cpp:22:2:22:2 | p | semmle.label | p |
| tests.cpp:46:23:46:43 | XercesDOMParser output argument | semmle.label | XercesDOMParser output argument | | tests2.cpp:33:17:33:31 | SAXParser output argument | semmle.label | SAXParser output argument |
| tests.cpp:49:2:49:2 | p | semmle.label | p | | tests2.cpp:37:2:37:2 | p | semmle.label | p |
| tests.cpp:53:19:53:19 | VariableAddress [post update] | semmle.label | VariableAddress [post update] | | tests3.cpp:23:21:23:53 | call to createXMLReader | semmle.label | call to createXMLReader |
| tests.cpp:53:23:53:43 | XercesDOMParser output argument | semmle.label | XercesDOMParser output argument | | 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)... |
| 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: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:56:2:56:2 | p | semmle.label | p |
| tests.cpp:57:2:57: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:58:2:58:2 | p | semmle.label | p |
| tests.cpp:69:23:69:43 | XercesDOMParser output argument | semmle.label | XercesDOMParser output argument | | tests.cpp:59:2:59:2 | p | semmle.label | p |
| tests.cpp:71:2:71:2 | p | semmle.label | p | | tests.cpp:60:2:60:2 | p | semmle.label | p |
| tests.cpp:72:2:72:2 | p | semmle.label | p | | tests.cpp:66:23:66:43 | XercesDOMParser output argument | semmle.label | XercesDOMParser output argument |
| tests.cpp:73:2:73:2 | p | semmle.label | p | | tests.cpp:69:2:69:2 | p | semmle.label | p |
| tests.cpp:74:2:74:2 | p | semmle.label | p | | tests.cpp:73:23:73:43 | XercesDOMParser output argument | semmle.label | XercesDOMParser output argument |
| tests.cpp:74:2:74:2 | p | semmle.label | p | | tests.cpp:80:2:80:2 | p | semmle.label | p |
| tests.cpp:75:2:75:2 | p | semmle.label | p | | tests.cpp:85:24:85:44 | XercesDOMParser output argument | semmle.label | XercesDOMParser output argument |
| tests.cpp:76:2:76:2 | p | semmle.label | p | | tests.cpp:88:3:88:3 | q | semmle.label | q |
| tests.cpp:77:2:77:2 | p | semmle.label | p | | tests.cpp:100:24:100:44 | XercesDOMParser output argument | semmle.label | XercesDOMParser output argument |
| tests.cpp:78:2:78:2 | p | semmle.label | p | | tests.cpp:104:3:104:3 | q | semmle.label | q |
| tests.cpp:84:23:84:43 | XercesDOMParser output argument | semmle.label | XercesDOMParser output argument | | tests.cpp:112:39:112:39 | p | semmle.label | p |
| tests.cpp:87:2:87:2 | p | semmle.label | p | | tests.cpp:113:2:113:2 | p | semmle.label | p |
| tests.cpp:91:23:91:43 | XercesDOMParser output argument | semmle.label | XercesDOMParser output argument | | tests.cpp:116:39:116:39 | p | semmle.label | p |
| tests.cpp:98:2:98:2 | p | semmle.label | p | | tests.cpp:117:2:117:2 | p | semmle.label | p |
| tests.cpp:103:24:103:44 | XercesDOMParser output argument | semmle.label | XercesDOMParser output argument | | tests.cpp:122:23:122:43 | XercesDOMParser output argument | semmle.label | XercesDOMParser output argument |
| tests.cpp:106:3:106:3 | q | semmle.label | q | | tests.cpp:126:18:126:18 | q | semmle.label | q |
| tests.cpp:118:24:118:44 | XercesDOMParser output argument | semmle.label | XercesDOMParser output argument | | tests.cpp:128:18:128:18 | q | semmle.label | q |
| 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 |
subpaths subpaths
#select #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 | | 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 |
| 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 | | 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 |
| 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 | | 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 |
| 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 | | 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 |
| 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 | | 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 |
| 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 | | 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 |
| 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 | | 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 |
| 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 | | 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 |
| 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 | | 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 |
| 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 | | 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 |
| 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: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: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 | | 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" #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 { class XercesDOMParser: public AbstractDOMParser {
public: public:
XercesDOMParser(); XercesDOMParser();
}; };
class DOMLSParser : public AbstractDOMParser {
};
DOMLSParser *createLSParser();
// --- // ---
void test1(InputSource &data) { void test1(InputSource &data) {
@@ -145,26 +127,3 @@ void test10(InputSource &data) {
test10_doParseC(p, data); test10_doParseC(p, data);
test10_doParseC(q, 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,78 @@
// 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) [NOT DETECTED]
}
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) [NOT DETECTED]
}
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) [NOT DETECTED]
}
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]
}

View File

@@ -1,3 +1,5 @@
## 1.1.1
## 1.1.0 ## 1.1.0
## 1.0.7 ## 1.0.7

View File

@@ -0,0 +1 @@
## 1.1.1

View File

@@ -1,2 +1,2 @@
--- ---
lastReleaseVersion: 1.1.0 lastReleaseVersion: 1.1.1

View File

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

View File

@@ -1,3 +1,5 @@
## 1.1.1
## 1.1.0 ## 1.1.0
## 1.0.7 ## 1.0.7

View File

@@ -0,0 +1 @@
## 1.1.1

View File

@@ -1,2 +1,2 @@
--- ---
lastReleaseVersion: 1.1.0 lastReleaseVersion: 1.1.1

View File

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

View File

@@ -1,3 +1,9 @@
## 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)`.
## 0.1.0 ## 0.1.0
### Breaking Changes ### Breaking Changes

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

@@ -0,0 +1,5 @@
## 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)`.

View File

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

View File

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

View File

@@ -167,9 +167,16 @@ class AccessPathToken extends string {
/** Gets the `n`th argument to this token, such as `x` or `y` from `Member[x,y]`. */ /** Gets the `n`th argument to this token, such as `x` or `y` from `Member[x,y]`. */
string getArgument(int n) { result = this.getArgumentList().splitAt(",", n).trim() } string getArgument(int n) { result = this.getArgumentList().splitAt(",", n).trim() }
/** Gets the `n`th argument to this `name` token, such as `x` or `y` from `Member[x,y]`. */
pragma[nomagic]
string getArgument(string name, int n) { name = this.getName() and result = this.getArgument(n) }
/** Gets an argument to this token, such as `x` or `y` from `Member[x,y]`. */ /** Gets an argument to this token, such as `x` or `y` from `Member[x,y]`. */
string getAnArgument() { result = this.getArgument(_) } string getAnArgument() { result = this.getArgument(_) }
/** Gets an argument to this `name` token, such as `x` or `y` from `Member[x,y]`. */
string getAnArgument(string name) { result = this.getArgument(name, _) }
/** Gets the number of arguments to this token, such as 2 for `Member[x,y]` or zero for `ReturnValue`. */ /** Gets the number of arguments to this token, such as 2 for `Member[x,y]` or zero for `ReturnValue`. */
int getNumArgument() { result = count(int n | exists(this.getArgument(n))) } int getNumArgument() { result = count(int n | exists(this.getArgument(n))) }
} }

View File

@@ -498,23 +498,35 @@ private predicate readSet(NodeEx node1, ContentSet c, NodeEx node2, Configuratio
} }
// inline to reduce fan-out via `getAReadContent` // inline to reduce fan-out via `getAReadContent`
pragma[inline] bindingset[c]
private predicate read(NodeEx node1, Content c, NodeEx node2, Configuration config) { private predicate read(NodeEx node1, Content c, NodeEx node2, Configuration config) {
exists(ContentSet cs | exists(ContentSet cs |
readSet(node1, cs, node2, config) and 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` // inline to reduce fan-out via `getAReadContent`
pragma[inline] bindingset[c]
private predicate clearsContentEx(NodeEx n, Content c) { private predicate clearsContentEx(NodeEx n, Content c) {
exists(ContentSet cs | exists(ContentSet cs |
clearsContentCached(n.asNode(), cs) and 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] pragma[nomagic]
private predicate store( private predicate store(
NodeEx node1, TypedContent tc, NodeEx node2, DataFlowType contentType, Configuration config NodeEx node1, TypedContent tc, NodeEx node2, DataFlowType contentType, Configuration config
@@ -793,7 +805,7 @@ private module Stage1 {
* by `revFlow`. * by `revFlow`.
*/ */
pragma[nomagic] pragma[nomagic]
private predicate revFlowIsReadAndStored(Content c, Configuration conf) { predicate revFlowIsReadAndStored(Content c, Configuration conf) {
revFlowConsCand(c, conf) and revFlowConsCand(c, conf) and
revFlowStore(c, _, _, conf) revFlowStore(c, _, _, conf)
} }
@@ -891,7 +903,7 @@ private module Stage1 {
pragma[nomagic] pragma[nomagic]
predicate readStepCand(NodeEx n1, Content c, NodeEx n2, Configuration config) { 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 read(n1, c, n2, pragma[only_bind_into](config)) and
revFlow(n2, pragma[only_bind_into](config)) revFlow(n2, pragma[only_bind_into](config))
} }
@@ -1181,11 +1193,26 @@ private module Stage2 {
private predicate flowIntoCall = flowIntoCallNodeCand1/5; 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] bindingset[node, state, ap, config]
private predicate filter(NodeEx node, FlowState state, Ap ap, Configuration config) { private predicate filter(NodeEx node, FlowState state, Ap ap, Configuration config) {
PrevStage::revFlowState(state, pragma[only_bind_into](config)) and PrevStage::revFlowState(state, pragma[only_bind_into](config)) and
exists(ap) 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] bindingset[ap, contentType]
@@ -1646,10 +1673,24 @@ private module Stage2 {
storeStepFwd(_, ap, tc, _, _, config) 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) 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] pragma[noinline]
private predicate parameterFlow( private predicate parameterFlow(
ParamNodeEx p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config ParamNodeEx p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config
@@ -1740,7 +1781,8 @@ private module LocalFlowBigStep {
private class FlowCheckNode extends NodeEx { private class FlowCheckNode extends NodeEx {
FlowCheckNode() { FlowCheckNode() {
castNode(this.asNode()) or castNode(this.asNode()) or
clearsContentCached(this.asNode(), _) clearsContentCached(this.asNode(), _) or
expectsContentCached(this.asNode(), _)
} }
} }
@@ -1979,6 +2021,16 @@ private module Stage3 {
clearContent(node, ap.getHead().getContent(), config) 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] pragma[nomagic]
private predicate castingNodeEx(NodeEx node) { node.asNode() instanceof CastingNode } private predicate castingNodeEx(NodeEx node) { node.asNode() instanceof CastingNode }
@@ -1987,7 +2039,12 @@ private module Stage3 {
exists(state) and exists(state) and
exists(config) and exists(config) and
not clear(node, ap, 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] bindingset[ap, contentType]
@@ -2452,10 +2509,24 @@ private module Stage3 {
storeStepFwd(_, ap, tc, _, _, config) 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) 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] pragma[noinline]
private predicate parameterFlow( private predicate parameterFlow(
ParamNodeEx p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config ParamNodeEx p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config
@@ -3279,10 +3350,24 @@ private module Stage4 {
storeStepFwd(_, ap, tc, _, _, config) 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) 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] pragma[noinline]
private predicate parameterFlow( private predicate parameterFlow(
ParamNodeEx p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config ParamNodeEx p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config
@@ -3351,17 +3436,28 @@ private Configuration unbindConf(Configuration conf) {
exists(Configuration c | result = pragma[only_bind_into](c) and conf = pragma[only_bind_into](c)) exists(Configuration c | result = pragma[only_bind_into](c) and conf = pragma[only_bind_into](c))
} }
private predicate nodeMayUseSummary( pragma[nomagic]
NodeEx n, FlowState state, AccessPathApprox apa, Configuration config private predicate nodeMayUseSummary0(
NodeEx n, DataFlowCallable c, FlowState state, AccessPathApprox apa, Configuration config
) { ) {
exists(DataFlowCallable c, AccessPathApprox apa0 | exists(AccessPathApprox apa0 |
Stage4::parameterMayFlowThrough(_, c, apa, _) and Stage4::parameterMayFlowThrough(_, c, _, _) and
Stage4::revFlow(n, state, true, _, apa0, config) and Stage4::revFlow(n, state, true, _, apa0, config) and
Stage4::fwdFlow(n, state, any(CallContextCall ccc), TAccessPathApproxSome(apa), apa0, config) and Stage4::fwdFlow(n, state, any(CallContextCall ccc), TAccessPathApproxSome(apa), apa0, config) and
n.getEnclosingCallable() = c 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 = private newtype TSummaryCtx =
TSummaryCtxNone() or TSummaryCtxNone() or
TSummaryCtxSome(ParamNodeEx p, FlowState state, AccessPath ap) { TSummaryCtxSome(ParamNodeEx p, FlowState state, AccessPath ap) {
@@ -4257,6 +4353,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 * 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 * a subpath between `par` and `ret` with the connecting edges `arg -> par` and
@@ -4264,15 +4366,13 @@ private module Subpaths {
*/ */
predicate subpaths(PathNode arg, PathNodeImpl par, PathNodeImpl ret, PathNode out) { predicate subpaths(PathNode arg, PathNodeImpl par, PathNodeImpl ret, PathNode out) {
exists(ParamNodeEx p, NodeEx o, FlowState sout, AccessPath apout, PathNodeMid out0 | 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() = pragma[only_bind_into](out0) and
pragma[only_bind_into](arg).getASuccessor() = out0 and subpaths03(pragma[only_bind_into](arg), p, localStepToHidden*(ret), o, sout, apout) and
subpaths03(arg, p, localStepToHidden*(ret), o, sout, apout) and hasSuccessor(pragma[only_bind_into](arg), par, p) and
not ret.isHidden() and not ret.isHidden() and
par.getNodeEx() = p and pathNode(out0, o, sout, _, _, apout, _, _)
out0.getNodeEx() = o and |
out0.getState() = sout and out = out0 or out = out0.projectToSink()
out0.getAp() = apout and
(out = out0 or out = out0.projectToSink())
) )
} }
@@ -4609,6 +4709,10 @@ private module FlowExploration {
exists(PartialPathNodeRev mid | exists(PartialPathNodeRev mid |
revPartialPathStep(mid, node, state, sc1, sc2, sc3, ap, config) and revPartialPathStep(mid, node, state, sc1, sc2, sc3, ap, config) and
not clearsContentEx(node, ap.getHead()) and not clearsContentEx(node, ap.getHead()) and
(
notExpectsContent(node) or
expectsContentEx(node, ap.getHead())
) and
not fullBarrier(node, config) and not fullBarrier(node, config) and
not stateBarrier(node, state, config) and not stateBarrier(node, state, config) and
distSink(node.getEnclosingCallable(), config) <= config.explorationLimit() distSink(node.getEnclosingCallable(), config) <= config.explorationLimit()
@@ -4625,6 +4729,10 @@ private module FlowExploration {
not fullBarrier(node, config) and not fullBarrier(node, config) and
not stateBarrier(node, state, config) and not stateBarrier(node, state, config) and
not clearsContentEx(node, ap.getHead().getContent()) and not clearsContentEx(node, ap.getHead().getContent()) and
(
notExpectsContent(node) or
expectsContentEx(node, ap.getHead().getContent())
) and
if node.asNode() instanceof CastingNode if node.asNode() instanceof CastingNode
then compatibleTypes(node.getDataFlowType(), ap.getType()) then compatibleTypes(node.getDataFlowType(), ap.getType())
else any() else any()

View File

@@ -498,23 +498,35 @@ private predicate readSet(NodeEx node1, ContentSet c, NodeEx node2, Configuratio
} }
// inline to reduce fan-out via `getAReadContent` // inline to reduce fan-out via `getAReadContent`
pragma[inline] bindingset[c]
private predicate read(NodeEx node1, Content c, NodeEx node2, Configuration config) { private predicate read(NodeEx node1, Content c, NodeEx node2, Configuration config) {
exists(ContentSet cs | exists(ContentSet cs |
readSet(node1, cs, node2, config) and 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` // inline to reduce fan-out via `getAReadContent`
pragma[inline] bindingset[c]
private predicate clearsContentEx(NodeEx n, Content c) { private predicate clearsContentEx(NodeEx n, Content c) {
exists(ContentSet cs | exists(ContentSet cs |
clearsContentCached(n.asNode(), cs) and 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] pragma[nomagic]
private predicate store( private predicate store(
NodeEx node1, TypedContent tc, NodeEx node2, DataFlowType contentType, Configuration config NodeEx node1, TypedContent tc, NodeEx node2, DataFlowType contentType, Configuration config
@@ -793,7 +805,7 @@ private module Stage1 {
* by `revFlow`. * by `revFlow`.
*/ */
pragma[nomagic] pragma[nomagic]
private predicate revFlowIsReadAndStored(Content c, Configuration conf) { predicate revFlowIsReadAndStored(Content c, Configuration conf) {
revFlowConsCand(c, conf) and revFlowConsCand(c, conf) and
revFlowStore(c, _, _, conf) revFlowStore(c, _, _, conf)
} }
@@ -891,7 +903,7 @@ private module Stage1 {
pragma[nomagic] pragma[nomagic]
predicate readStepCand(NodeEx n1, Content c, NodeEx n2, Configuration config) { 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 read(n1, c, n2, pragma[only_bind_into](config)) and
revFlow(n2, pragma[only_bind_into](config)) revFlow(n2, pragma[only_bind_into](config))
} }
@@ -1181,11 +1193,26 @@ private module Stage2 {
private predicate flowIntoCall = flowIntoCallNodeCand1/5; 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] bindingset[node, state, ap, config]
private predicate filter(NodeEx node, FlowState state, Ap ap, Configuration config) { private predicate filter(NodeEx node, FlowState state, Ap ap, Configuration config) {
PrevStage::revFlowState(state, pragma[only_bind_into](config)) and PrevStage::revFlowState(state, pragma[only_bind_into](config)) and
exists(ap) 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] bindingset[ap, contentType]
@@ -1646,10 +1673,24 @@ private module Stage2 {
storeStepFwd(_, ap, tc, _, _, config) 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) 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] pragma[noinline]
private predicate parameterFlow( private predicate parameterFlow(
ParamNodeEx p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config ParamNodeEx p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config
@@ -1740,7 +1781,8 @@ private module LocalFlowBigStep {
private class FlowCheckNode extends NodeEx { private class FlowCheckNode extends NodeEx {
FlowCheckNode() { FlowCheckNode() {
castNode(this.asNode()) or castNode(this.asNode()) or
clearsContentCached(this.asNode(), _) clearsContentCached(this.asNode(), _) or
expectsContentCached(this.asNode(), _)
} }
} }
@@ -1979,6 +2021,16 @@ private module Stage3 {
clearContent(node, ap.getHead().getContent(), config) 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] pragma[nomagic]
private predicate castingNodeEx(NodeEx node) { node.asNode() instanceof CastingNode } private predicate castingNodeEx(NodeEx node) { node.asNode() instanceof CastingNode }
@@ -1987,7 +2039,12 @@ private module Stage3 {
exists(state) and exists(state) and
exists(config) and exists(config) and
not clear(node, ap, 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] bindingset[ap, contentType]
@@ -2452,10 +2509,24 @@ private module Stage3 {
storeStepFwd(_, ap, tc, _, _, config) 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) 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] pragma[noinline]
private predicate parameterFlow( private predicate parameterFlow(
ParamNodeEx p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config ParamNodeEx p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config
@@ -3279,10 +3350,24 @@ private module Stage4 {
storeStepFwd(_, ap, tc, _, _, config) 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) 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] pragma[noinline]
private predicate parameterFlow( private predicate parameterFlow(
ParamNodeEx p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config ParamNodeEx p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config
@@ -3351,17 +3436,28 @@ private Configuration unbindConf(Configuration conf) {
exists(Configuration c | result = pragma[only_bind_into](c) and conf = pragma[only_bind_into](c)) exists(Configuration c | result = pragma[only_bind_into](c) and conf = pragma[only_bind_into](c))
} }
private predicate nodeMayUseSummary( pragma[nomagic]
NodeEx n, FlowState state, AccessPathApprox apa, Configuration config private predicate nodeMayUseSummary0(
NodeEx n, DataFlowCallable c, FlowState state, AccessPathApprox apa, Configuration config
) { ) {
exists(DataFlowCallable c, AccessPathApprox apa0 | exists(AccessPathApprox apa0 |
Stage4::parameterMayFlowThrough(_, c, apa, _) and Stage4::parameterMayFlowThrough(_, c, _, _) and
Stage4::revFlow(n, state, true, _, apa0, config) and Stage4::revFlow(n, state, true, _, apa0, config) and
Stage4::fwdFlow(n, state, any(CallContextCall ccc), TAccessPathApproxSome(apa), apa0, config) and Stage4::fwdFlow(n, state, any(CallContextCall ccc), TAccessPathApproxSome(apa), apa0, config) and
n.getEnclosingCallable() = c 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 = private newtype TSummaryCtx =
TSummaryCtxNone() or TSummaryCtxNone() or
TSummaryCtxSome(ParamNodeEx p, FlowState state, AccessPath ap) { TSummaryCtxSome(ParamNodeEx p, FlowState state, AccessPath ap) {
@@ -4257,6 +4353,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 * 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 * a subpath between `par` and `ret` with the connecting edges `arg -> par` and
@@ -4264,15 +4366,13 @@ private module Subpaths {
*/ */
predicate subpaths(PathNode arg, PathNodeImpl par, PathNodeImpl ret, PathNode out) { predicate subpaths(PathNode arg, PathNodeImpl par, PathNodeImpl ret, PathNode out) {
exists(ParamNodeEx p, NodeEx o, FlowState sout, AccessPath apout, PathNodeMid out0 | 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() = pragma[only_bind_into](out0) and
pragma[only_bind_into](arg).getASuccessor() = out0 and subpaths03(pragma[only_bind_into](arg), p, localStepToHidden*(ret), o, sout, apout) and
subpaths03(arg, p, localStepToHidden*(ret), o, sout, apout) and hasSuccessor(pragma[only_bind_into](arg), par, p) and
not ret.isHidden() and not ret.isHidden() and
par.getNodeEx() = p and pathNode(out0, o, sout, _, _, apout, _, _)
out0.getNodeEx() = o and |
out0.getState() = sout and out = out0 or out = out0.projectToSink()
out0.getAp() = apout and
(out = out0 or out = out0.projectToSink())
) )
} }
@@ -4609,6 +4709,10 @@ private module FlowExploration {
exists(PartialPathNodeRev mid | exists(PartialPathNodeRev mid |
revPartialPathStep(mid, node, state, sc1, sc2, sc3, ap, config) and revPartialPathStep(mid, node, state, sc1, sc2, sc3, ap, config) and
not clearsContentEx(node, ap.getHead()) and not clearsContentEx(node, ap.getHead()) and
(
notExpectsContent(node) or
expectsContentEx(node, ap.getHead())
) and
not fullBarrier(node, config) and not fullBarrier(node, config) and
not stateBarrier(node, state, config) and not stateBarrier(node, state, config) and
distSink(node.getEnclosingCallable(), config) <= config.explorationLimit() distSink(node.getEnclosingCallable(), config) <= config.explorationLimit()
@@ -4625,6 +4729,10 @@ private module FlowExploration {
not fullBarrier(node, config) and not fullBarrier(node, config) and
not stateBarrier(node, state, config) and not stateBarrier(node, state, config) and
not clearsContentEx(node, ap.getHead().getContent()) and not clearsContentEx(node, ap.getHead().getContent()) and
(
notExpectsContent(node) or
expectsContentEx(node, ap.getHead().getContent())
) and
if node.asNode() instanceof CastingNode if node.asNode() instanceof CastingNode
then compatibleTypes(node.getDataFlowType(), ap.getType()) then compatibleTypes(node.getDataFlowType(), ap.getType())
else any() else any()

View File

@@ -498,23 +498,35 @@ private predicate readSet(NodeEx node1, ContentSet c, NodeEx node2, Configuratio
} }
// inline to reduce fan-out via `getAReadContent` // inline to reduce fan-out via `getAReadContent`
pragma[inline] bindingset[c]
private predicate read(NodeEx node1, Content c, NodeEx node2, Configuration config) { private predicate read(NodeEx node1, Content c, NodeEx node2, Configuration config) {
exists(ContentSet cs | exists(ContentSet cs |
readSet(node1, cs, node2, config) and 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` // inline to reduce fan-out via `getAReadContent`
pragma[inline] bindingset[c]
private predicate clearsContentEx(NodeEx n, Content c) { private predicate clearsContentEx(NodeEx n, Content c) {
exists(ContentSet cs | exists(ContentSet cs |
clearsContentCached(n.asNode(), cs) and 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] pragma[nomagic]
private predicate store( private predicate store(
NodeEx node1, TypedContent tc, NodeEx node2, DataFlowType contentType, Configuration config NodeEx node1, TypedContent tc, NodeEx node2, DataFlowType contentType, Configuration config
@@ -793,7 +805,7 @@ private module Stage1 {
* by `revFlow`. * by `revFlow`.
*/ */
pragma[nomagic] pragma[nomagic]
private predicate revFlowIsReadAndStored(Content c, Configuration conf) { predicate revFlowIsReadAndStored(Content c, Configuration conf) {
revFlowConsCand(c, conf) and revFlowConsCand(c, conf) and
revFlowStore(c, _, _, conf) revFlowStore(c, _, _, conf)
} }
@@ -891,7 +903,7 @@ private module Stage1 {
pragma[nomagic] pragma[nomagic]
predicate readStepCand(NodeEx n1, Content c, NodeEx n2, Configuration config) { 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 read(n1, c, n2, pragma[only_bind_into](config)) and
revFlow(n2, pragma[only_bind_into](config)) revFlow(n2, pragma[only_bind_into](config))
} }
@@ -1181,11 +1193,26 @@ private module Stage2 {
private predicate flowIntoCall = flowIntoCallNodeCand1/5; 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] bindingset[node, state, ap, config]
private predicate filter(NodeEx node, FlowState state, Ap ap, Configuration config) { private predicate filter(NodeEx node, FlowState state, Ap ap, Configuration config) {
PrevStage::revFlowState(state, pragma[only_bind_into](config)) and PrevStage::revFlowState(state, pragma[only_bind_into](config)) and
exists(ap) 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] bindingset[ap, contentType]
@@ -1646,10 +1673,24 @@ private module Stage2 {
storeStepFwd(_, ap, tc, _, _, config) 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) 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] pragma[noinline]
private predicate parameterFlow( private predicate parameterFlow(
ParamNodeEx p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config ParamNodeEx p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config
@@ -1740,7 +1781,8 @@ private module LocalFlowBigStep {
private class FlowCheckNode extends NodeEx { private class FlowCheckNode extends NodeEx {
FlowCheckNode() { FlowCheckNode() {
castNode(this.asNode()) or castNode(this.asNode()) or
clearsContentCached(this.asNode(), _) clearsContentCached(this.asNode(), _) or
expectsContentCached(this.asNode(), _)
} }
} }
@@ -1979,6 +2021,16 @@ private module Stage3 {
clearContent(node, ap.getHead().getContent(), config) 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] pragma[nomagic]
private predicate castingNodeEx(NodeEx node) { node.asNode() instanceof CastingNode } private predicate castingNodeEx(NodeEx node) { node.asNode() instanceof CastingNode }
@@ -1987,7 +2039,12 @@ private module Stage3 {
exists(state) and exists(state) and
exists(config) and exists(config) and
not clear(node, ap, 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] bindingset[ap, contentType]
@@ -2452,10 +2509,24 @@ private module Stage3 {
storeStepFwd(_, ap, tc, _, _, config) 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) 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] pragma[noinline]
private predicate parameterFlow( private predicate parameterFlow(
ParamNodeEx p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config ParamNodeEx p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config
@@ -3279,10 +3350,24 @@ private module Stage4 {
storeStepFwd(_, ap, tc, _, _, config) 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) 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] pragma[noinline]
private predicate parameterFlow( private predicate parameterFlow(
ParamNodeEx p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config ParamNodeEx p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config
@@ -3351,17 +3436,28 @@ private Configuration unbindConf(Configuration conf) {
exists(Configuration c | result = pragma[only_bind_into](c) and conf = pragma[only_bind_into](c)) exists(Configuration c | result = pragma[only_bind_into](c) and conf = pragma[only_bind_into](c))
} }
private predicate nodeMayUseSummary( pragma[nomagic]
NodeEx n, FlowState state, AccessPathApprox apa, Configuration config private predicate nodeMayUseSummary0(
NodeEx n, DataFlowCallable c, FlowState state, AccessPathApprox apa, Configuration config
) { ) {
exists(DataFlowCallable c, AccessPathApprox apa0 | exists(AccessPathApprox apa0 |
Stage4::parameterMayFlowThrough(_, c, apa, _) and Stage4::parameterMayFlowThrough(_, c, _, _) and
Stage4::revFlow(n, state, true, _, apa0, config) and Stage4::revFlow(n, state, true, _, apa0, config) and
Stage4::fwdFlow(n, state, any(CallContextCall ccc), TAccessPathApproxSome(apa), apa0, config) and Stage4::fwdFlow(n, state, any(CallContextCall ccc), TAccessPathApproxSome(apa), apa0, config) and
n.getEnclosingCallable() = c 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 = private newtype TSummaryCtx =
TSummaryCtxNone() or TSummaryCtxNone() or
TSummaryCtxSome(ParamNodeEx p, FlowState state, AccessPath ap) { TSummaryCtxSome(ParamNodeEx p, FlowState state, AccessPath ap) {
@@ -4257,6 +4353,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 * 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 * a subpath between `par` and `ret` with the connecting edges `arg -> par` and
@@ -4264,15 +4366,13 @@ private module Subpaths {
*/ */
predicate subpaths(PathNode arg, PathNodeImpl par, PathNodeImpl ret, PathNode out) { predicate subpaths(PathNode arg, PathNodeImpl par, PathNodeImpl ret, PathNode out) {
exists(ParamNodeEx p, NodeEx o, FlowState sout, AccessPath apout, PathNodeMid out0 | 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() = pragma[only_bind_into](out0) and
pragma[only_bind_into](arg).getASuccessor() = out0 and subpaths03(pragma[only_bind_into](arg), p, localStepToHidden*(ret), o, sout, apout) and
subpaths03(arg, p, localStepToHidden*(ret), o, sout, apout) and hasSuccessor(pragma[only_bind_into](arg), par, p) and
not ret.isHidden() and not ret.isHidden() and
par.getNodeEx() = p and pathNode(out0, o, sout, _, _, apout, _, _)
out0.getNodeEx() = o and |
out0.getState() = sout and out = out0 or out = out0.projectToSink()
out0.getAp() = apout and
(out = out0 or out = out0.projectToSink())
) )
} }
@@ -4609,6 +4709,10 @@ private module FlowExploration {
exists(PartialPathNodeRev mid | exists(PartialPathNodeRev mid |
revPartialPathStep(mid, node, state, sc1, sc2, sc3, ap, config) and revPartialPathStep(mid, node, state, sc1, sc2, sc3, ap, config) and
not clearsContentEx(node, ap.getHead()) and not clearsContentEx(node, ap.getHead()) and
(
notExpectsContent(node) or
expectsContentEx(node, ap.getHead())
) and
not fullBarrier(node, config) and not fullBarrier(node, config) and
not stateBarrier(node, state, config) and not stateBarrier(node, state, config) and
distSink(node.getEnclosingCallable(), config) <= config.explorationLimit() distSink(node.getEnclosingCallable(), config) <= config.explorationLimit()
@@ -4625,6 +4729,10 @@ private module FlowExploration {
not fullBarrier(node, config) and not fullBarrier(node, config) and
not stateBarrier(node, state, config) and not stateBarrier(node, state, config) and
not clearsContentEx(node, ap.getHead().getContent()) and not clearsContentEx(node, ap.getHead().getContent()) and
(
notExpectsContent(node) or
expectsContentEx(node, ap.getHead().getContent())
) and
if node.asNode() instanceof CastingNode if node.asNode() instanceof CastingNode
then compatibleTypes(node.getDataFlowType(), ap.getType()) then compatibleTypes(node.getDataFlowType(), ap.getType())
else any() else any()

View File

@@ -498,23 +498,35 @@ private predicate readSet(NodeEx node1, ContentSet c, NodeEx node2, Configuratio
} }
// inline to reduce fan-out via `getAReadContent` // inline to reduce fan-out via `getAReadContent`
pragma[inline] bindingset[c]
private predicate read(NodeEx node1, Content c, NodeEx node2, Configuration config) { private predicate read(NodeEx node1, Content c, NodeEx node2, Configuration config) {
exists(ContentSet cs | exists(ContentSet cs |
readSet(node1, cs, node2, config) and 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` // inline to reduce fan-out via `getAReadContent`
pragma[inline] bindingset[c]
private predicate clearsContentEx(NodeEx n, Content c) { private predicate clearsContentEx(NodeEx n, Content c) {
exists(ContentSet cs | exists(ContentSet cs |
clearsContentCached(n.asNode(), cs) and 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] pragma[nomagic]
private predicate store( private predicate store(
NodeEx node1, TypedContent tc, NodeEx node2, DataFlowType contentType, Configuration config NodeEx node1, TypedContent tc, NodeEx node2, DataFlowType contentType, Configuration config
@@ -793,7 +805,7 @@ private module Stage1 {
* by `revFlow`. * by `revFlow`.
*/ */
pragma[nomagic] pragma[nomagic]
private predicate revFlowIsReadAndStored(Content c, Configuration conf) { predicate revFlowIsReadAndStored(Content c, Configuration conf) {
revFlowConsCand(c, conf) and revFlowConsCand(c, conf) and
revFlowStore(c, _, _, conf) revFlowStore(c, _, _, conf)
} }
@@ -891,7 +903,7 @@ private module Stage1 {
pragma[nomagic] pragma[nomagic]
predicate readStepCand(NodeEx n1, Content c, NodeEx n2, Configuration config) { 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 read(n1, c, n2, pragma[only_bind_into](config)) and
revFlow(n2, pragma[only_bind_into](config)) revFlow(n2, pragma[only_bind_into](config))
} }
@@ -1181,11 +1193,26 @@ private module Stage2 {
private predicate flowIntoCall = flowIntoCallNodeCand1/5; 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] bindingset[node, state, ap, config]
private predicate filter(NodeEx node, FlowState state, Ap ap, Configuration config) { private predicate filter(NodeEx node, FlowState state, Ap ap, Configuration config) {
PrevStage::revFlowState(state, pragma[only_bind_into](config)) and PrevStage::revFlowState(state, pragma[only_bind_into](config)) and
exists(ap) 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] bindingset[ap, contentType]
@@ -1646,10 +1673,24 @@ private module Stage2 {
storeStepFwd(_, ap, tc, _, _, config) 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) 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] pragma[noinline]
private predicate parameterFlow( private predicate parameterFlow(
ParamNodeEx p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config ParamNodeEx p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config
@@ -1740,7 +1781,8 @@ private module LocalFlowBigStep {
private class FlowCheckNode extends NodeEx { private class FlowCheckNode extends NodeEx {
FlowCheckNode() { FlowCheckNode() {
castNode(this.asNode()) or castNode(this.asNode()) or
clearsContentCached(this.asNode(), _) clearsContentCached(this.asNode(), _) or
expectsContentCached(this.asNode(), _)
} }
} }
@@ -1979,6 +2021,16 @@ private module Stage3 {
clearContent(node, ap.getHead().getContent(), config) 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] pragma[nomagic]
private predicate castingNodeEx(NodeEx node) { node.asNode() instanceof CastingNode } private predicate castingNodeEx(NodeEx node) { node.asNode() instanceof CastingNode }
@@ -1987,7 +2039,12 @@ private module Stage3 {
exists(state) and exists(state) and
exists(config) and exists(config) and
not clear(node, ap, 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] bindingset[ap, contentType]
@@ -2452,10 +2509,24 @@ private module Stage3 {
storeStepFwd(_, ap, tc, _, _, config) 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) 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] pragma[noinline]
private predicate parameterFlow( private predicate parameterFlow(
ParamNodeEx p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config ParamNodeEx p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config
@@ -3279,10 +3350,24 @@ private module Stage4 {
storeStepFwd(_, ap, tc, _, _, config) 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) 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] pragma[noinline]
private predicate parameterFlow( private predicate parameterFlow(
ParamNodeEx p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config ParamNodeEx p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config
@@ -3351,17 +3436,28 @@ private Configuration unbindConf(Configuration conf) {
exists(Configuration c | result = pragma[only_bind_into](c) and conf = pragma[only_bind_into](c)) exists(Configuration c | result = pragma[only_bind_into](c) and conf = pragma[only_bind_into](c))
} }
private predicate nodeMayUseSummary( pragma[nomagic]
NodeEx n, FlowState state, AccessPathApprox apa, Configuration config private predicate nodeMayUseSummary0(
NodeEx n, DataFlowCallable c, FlowState state, AccessPathApprox apa, Configuration config
) { ) {
exists(DataFlowCallable c, AccessPathApprox apa0 | exists(AccessPathApprox apa0 |
Stage4::parameterMayFlowThrough(_, c, apa, _) and Stage4::parameterMayFlowThrough(_, c, _, _) and
Stage4::revFlow(n, state, true, _, apa0, config) and Stage4::revFlow(n, state, true, _, apa0, config) and
Stage4::fwdFlow(n, state, any(CallContextCall ccc), TAccessPathApproxSome(apa), apa0, config) and Stage4::fwdFlow(n, state, any(CallContextCall ccc), TAccessPathApproxSome(apa), apa0, config) and
n.getEnclosingCallable() = c 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 = private newtype TSummaryCtx =
TSummaryCtxNone() or TSummaryCtxNone() or
TSummaryCtxSome(ParamNodeEx p, FlowState state, AccessPath ap) { TSummaryCtxSome(ParamNodeEx p, FlowState state, AccessPath ap) {
@@ -4257,6 +4353,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 * 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 * a subpath between `par` and `ret` with the connecting edges `arg -> par` and
@@ -4264,15 +4366,13 @@ private module Subpaths {
*/ */
predicate subpaths(PathNode arg, PathNodeImpl par, PathNodeImpl ret, PathNode out) { predicate subpaths(PathNode arg, PathNodeImpl par, PathNodeImpl ret, PathNode out) {
exists(ParamNodeEx p, NodeEx o, FlowState sout, AccessPath apout, PathNodeMid out0 | 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() = pragma[only_bind_into](out0) and
pragma[only_bind_into](arg).getASuccessor() = out0 and subpaths03(pragma[only_bind_into](arg), p, localStepToHidden*(ret), o, sout, apout) and
subpaths03(arg, p, localStepToHidden*(ret), o, sout, apout) and hasSuccessor(pragma[only_bind_into](arg), par, p) and
not ret.isHidden() and not ret.isHidden() and
par.getNodeEx() = p and pathNode(out0, o, sout, _, _, apout, _, _)
out0.getNodeEx() = o and |
out0.getState() = sout and out = out0 or out = out0.projectToSink()
out0.getAp() = apout and
(out = out0 or out = out0.projectToSink())
) )
} }
@@ -4609,6 +4709,10 @@ private module FlowExploration {
exists(PartialPathNodeRev mid | exists(PartialPathNodeRev mid |
revPartialPathStep(mid, node, state, sc1, sc2, sc3, ap, config) and revPartialPathStep(mid, node, state, sc1, sc2, sc3, ap, config) and
not clearsContentEx(node, ap.getHead()) and not clearsContentEx(node, ap.getHead()) and
(
notExpectsContent(node) or
expectsContentEx(node, ap.getHead())
) and
not fullBarrier(node, config) and not fullBarrier(node, config) and
not stateBarrier(node, state, config) and not stateBarrier(node, state, config) and
distSink(node.getEnclosingCallable(), config) <= config.explorationLimit() distSink(node.getEnclosingCallable(), config) <= config.explorationLimit()
@@ -4625,6 +4729,10 @@ private module FlowExploration {
not fullBarrier(node, config) and not fullBarrier(node, config) and
not stateBarrier(node, state, config) and not stateBarrier(node, state, config) and
not clearsContentEx(node, ap.getHead().getContent()) and not clearsContentEx(node, ap.getHead().getContent()) and
(
notExpectsContent(node) or
expectsContentEx(node, ap.getHead().getContent())
) and
if node.asNode() instanceof CastingNode if node.asNode() instanceof CastingNode
then compatibleTypes(node.getDataFlowType(), ap.getType()) then compatibleTypes(node.getDataFlowType(), ap.getType())
else any() else any()

View File

@@ -498,23 +498,35 @@ private predicate readSet(NodeEx node1, ContentSet c, NodeEx node2, Configuratio
} }
// inline to reduce fan-out via `getAReadContent` // inline to reduce fan-out via `getAReadContent`
pragma[inline] bindingset[c]
private predicate read(NodeEx node1, Content c, NodeEx node2, Configuration config) { private predicate read(NodeEx node1, Content c, NodeEx node2, Configuration config) {
exists(ContentSet cs | exists(ContentSet cs |
readSet(node1, cs, node2, config) and 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` // inline to reduce fan-out via `getAReadContent`
pragma[inline] bindingset[c]
private predicate clearsContentEx(NodeEx n, Content c) { private predicate clearsContentEx(NodeEx n, Content c) {
exists(ContentSet cs | exists(ContentSet cs |
clearsContentCached(n.asNode(), cs) and 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] pragma[nomagic]
private predicate store( private predicate store(
NodeEx node1, TypedContent tc, NodeEx node2, DataFlowType contentType, Configuration config NodeEx node1, TypedContent tc, NodeEx node2, DataFlowType contentType, Configuration config
@@ -793,7 +805,7 @@ private module Stage1 {
* by `revFlow`. * by `revFlow`.
*/ */
pragma[nomagic] pragma[nomagic]
private predicate revFlowIsReadAndStored(Content c, Configuration conf) { predicate revFlowIsReadAndStored(Content c, Configuration conf) {
revFlowConsCand(c, conf) and revFlowConsCand(c, conf) and
revFlowStore(c, _, _, conf) revFlowStore(c, _, _, conf)
} }
@@ -891,7 +903,7 @@ private module Stage1 {
pragma[nomagic] pragma[nomagic]
predicate readStepCand(NodeEx n1, Content c, NodeEx n2, Configuration config) { 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 read(n1, c, n2, pragma[only_bind_into](config)) and
revFlow(n2, pragma[only_bind_into](config)) revFlow(n2, pragma[only_bind_into](config))
} }
@@ -1181,11 +1193,26 @@ private module Stage2 {
private predicate flowIntoCall = flowIntoCallNodeCand1/5; 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] bindingset[node, state, ap, config]
private predicate filter(NodeEx node, FlowState state, Ap ap, Configuration config) { private predicate filter(NodeEx node, FlowState state, Ap ap, Configuration config) {
PrevStage::revFlowState(state, pragma[only_bind_into](config)) and PrevStage::revFlowState(state, pragma[only_bind_into](config)) and
exists(ap) 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] bindingset[ap, contentType]
@@ -1646,10 +1673,24 @@ private module Stage2 {
storeStepFwd(_, ap, tc, _, _, config) 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) 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] pragma[noinline]
private predicate parameterFlow( private predicate parameterFlow(
ParamNodeEx p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config ParamNodeEx p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config
@@ -1740,7 +1781,8 @@ private module LocalFlowBigStep {
private class FlowCheckNode extends NodeEx { private class FlowCheckNode extends NodeEx {
FlowCheckNode() { FlowCheckNode() {
castNode(this.asNode()) or castNode(this.asNode()) or
clearsContentCached(this.asNode(), _) clearsContentCached(this.asNode(), _) or
expectsContentCached(this.asNode(), _)
} }
} }
@@ -1979,6 +2021,16 @@ private module Stage3 {
clearContent(node, ap.getHead().getContent(), config) 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] pragma[nomagic]
private predicate castingNodeEx(NodeEx node) { node.asNode() instanceof CastingNode } private predicate castingNodeEx(NodeEx node) { node.asNode() instanceof CastingNode }
@@ -1987,7 +2039,12 @@ private module Stage3 {
exists(state) and exists(state) and
exists(config) and exists(config) and
not clear(node, ap, 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] bindingset[ap, contentType]
@@ -2452,10 +2509,24 @@ private module Stage3 {
storeStepFwd(_, ap, tc, _, _, config) 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) 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] pragma[noinline]
private predicate parameterFlow( private predicate parameterFlow(
ParamNodeEx p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config ParamNodeEx p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config
@@ -3279,10 +3350,24 @@ private module Stage4 {
storeStepFwd(_, ap, tc, _, _, config) 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) 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] pragma[noinline]
private predicate parameterFlow( private predicate parameterFlow(
ParamNodeEx p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config ParamNodeEx p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config
@@ -3351,17 +3436,28 @@ private Configuration unbindConf(Configuration conf) {
exists(Configuration c | result = pragma[only_bind_into](c) and conf = pragma[only_bind_into](c)) exists(Configuration c | result = pragma[only_bind_into](c) and conf = pragma[only_bind_into](c))
} }
private predicate nodeMayUseSummary( pragma[nomagic]
NodeEx n, FlowState state, AccessPathApprox apa, Configuration config private predicate nodeMayUseSummary0(
NodeEx n, DataFlowCallable c, FlowState state, AccessPathApprox apa, Configuration config
) { ) {
exists(DataFlowCallable c, AccessPathApprox apa0 | exists(AccessPathApprox apa0 |
Stage4::parameterMayFlowThrough(_, c, apa, _) and Stage4::parameterMayFlowThrough(_, c, _, _) and
Stage4::revFlow(n, state, true, _, apa0, config) and Stage4::revFlow(n, state, true, _, apa0, config) and
Stage4::fwdFlow(n, state, any(CallContextCall ccc), TAccessPathApproxSome(apa), apa0, config) and Stage4::fwdFlow(n, state, any(CallContextCall ccc), TAccessPathApproxSome(apa), apa0, config) and
n.getEnclosingCallable() = c 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 = private newtype TSummaryCtx =
TSummaryCtxNone() or TSummaryCtxNone() or
TSummaryCtxSome(ParamNodeEx p, FlowState state, AccessPath ap) { TSummaryCtxSome(ParamNodeEx p, FlowState state, AccessPath ap) {
@@ -4257,6 +4353,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 * 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 * a subpath between `par` and `ret` with the connecting edges `arg -> par` and
@@ -4264,15 +4366,13 @@ private module Subpaths {
*/ */
predicate subpaths(PathNode arg, PathNodeImpl par, PathNodeImpl ret, PathNode out) { predicate subpaths(PathNode arg, PathNodeImpl par, PathNodeImpl ret, PathNode out) {
exists(ParamNodeEx p, NodeEx o, FlowState sout, AccessPath apout, PathNodeMid out0 | 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() = pragma[only_bind_into](out0) and
pragma[only_bind_into](arg).getASuccessor() = out0 and subpaths03(pragma[only_bind_into](arg), p, localStepToHidden*(ret), o, sout, apout) and
subpaths03(arg, p, localStepToHidden*(ret), o, sout, apout) and hasSuccessor(pragma[only_bind_into](arg), par, p) and
not ret.isHidden() and not ret.isHidden() and
par.getNodeEx() = p and pathNode(out0, o, sout, _, _, apout, _, _)
out0.getNodeEx() = o and |
out0.getState() = sout and out = out0 or out = out0.projectToSink()
out0.getAp() = apout and
(out = out0 or out = out0.projectToSink())
) )
} }
@@ -4609,6 +4709,10 @@ private module FlowExploration {
exists(PartialPathNodeRev mid | exists(PartialPathNodeRev mid |
revPartialPathStep(mid, node, state, sc1, sc2, sc3, ap, config) and revPartialPathStep(mid, node, state, sc1, sc2, sc3, ap, config) and
not clearsContentEx(node, ap.getHead()) and not clearsContentEx(node, ap.getHead()) and
(
notExpectsContent(node) or
expectsContentEx(node, ap.getHead())
) and
not fullBarrier(node, config) and not fullBarrier(node, config) and
not stateBarrier(node, state, config) and not stateBarrier(node, state, config) and
distSink(node.getEnclosingCallable(), config) <= config.explorationLimit() distSink(node.getEnclosingCallable(), config) <= config.explorationLimit()
@@ -4625,6 +4729,10 @@ private module FlowExploration {
not fullBarrier(node, config) and not fullBarrier(node, config) and
not stateBarrier(node, state, config) and not stateBarrier(node, state, config) and
not clearsContentEx(node, ap.getHead().getContent()) and not clearsContentEx(node, ap.getHead().getContent()) and
(
notExpectsContent(node) or
expectsContentEx(node, ap.getHead().getContent())
) and
if node.asNode() instanceof CastingNode if node.asNode() instanceof CastingNode
then compatibleTypes(node.getDataFlowType(), ap.getType()) then compatibleTypes(node.getDataFlowType(), ap.getType())
else any() else any()

View File

@@ -328,6 +328,9 @@ private module Cached {
cached cached
predicate clearsContentCached(Node n, ContentSet c) { clearsContent(n, c) } predicate clearsContentCached(Node n, ContentSet c) { clearsContent(n, c) }
cached
predicate expectsContentCached(Node n, ContentSet c) { expectsContent(n, c) }
cached cached
predicate isUnreachableInCallCached(Node n, DataFlowCall call) { isUnreachableInCall(n, call) } predicate isUnreachableInCallCached(Node n, DataFlowCall call) { isUnreachableInCall(n, call) }

View File

@@ -421,7 +421,7 @@ predicate simpleLocalFlowStep(Node nodeFrom, Node nodeTo) {
or or
exists(Ssa::Definition def | exists(Ssa::Definition def |
LocalFlow::localSsaFlowStepUseUse(def, nodeFrom, nodeTo) and LocalFlow::localSsaFlowStepUseUse(def, nodeFrom, nodeTo) and
not FlowSummaryImpl::Private::Steps::summaryClearsContentArg(nodeFrom, _) and not FlowSummaryImpl::Private::Steps::prohibitsUseUseFlow(nodeFrom) and
not LocalFlow::usesInstanceField(def) not LocalFlow::usesInstanceField(def)
) )
or or
@@ -1714,6 +1714,14 @@ predicate clearsContent(Node n, Content c) {
) )
} }
/**
* 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) {
FlowSummaryImpl::Private::Steps::summaryExpectsContent(n, c)
}
/** /**
* Holds if the node `n` is unreachable when the call context is `call`. * Holds if the node `n` is unreachable when the call context is `call`.
*/ */

View File

@@ -26,6 +26,10 @@ module Public {
string toString() { string toString() {
exists(ContentSet c | this = TContentSummaryComponent(c) and result = c.toString()) exists(ContentSet c | this = TContentSummaryComponent(c) and result = c.toString())
or or
exists(ContentSet c | this = TWithoutContentSummaryComponent(c) and result = "without " + c)
or
exists(ContentSet c | this = TWithContentSummaryComponent(c) and result = "with " + c)
or
exists(ArgumentPosition pos | exists(ArgumentPosition pos |
this = TParameterSummaryComponent(pos) and result = "parameter " + pos this = TParameterSummaryComponent(pos) and result = "parameter " + pos
) )
@@ -43,6 +47,12 @@ module Public {
/** Gets a summary component for content `c`. */ /** Gets a summary component for content `c`. */
SummaryComponent content(ContentSet c) { result = TContentSummaryComponent(c) } SummaryComponent content(ContentSet c) { result = TContentSummaryComponent(c) }
/** Gets a summary component where data is not allowed to be stored in `c`. */
SummaryComponent withoutContent(ContentSet c) { result = TWithoutContentSummaryComponent(c) }
/** Gets a summary component where data must be stored in `c`. */
SummaryComponent withContent(ContentSet c) { result = TWithContentSummaryComponent(c) }
/** Gets a summary component for a parameter at position `pos`. */ /** Gets a summary component for a parameter at position `pos`. */
SummaryComponent parameter(ArgumentPosition pos) { result = TParameterSummaryComponent(pos) } SummaryComponent parameter(ArgumentPosition pos) { result = TParameterSummaryComponent(pos) }
@@ -216,9 +226,16 @@ module Public {
/** /**
* Holds if values stored inside `content` are cleared on objects passed as * Holds if values stored inside `content` are cleared on objects passed as
* arguments at position `pos` to this callable. * arguments at position `pos` to this callable.
*
* TODO: Remove once all languages support `WithoutContent` tokens.
*/ */
pragma[nomagic] pragma[nomagic]
predicate clearsContent(ParameterPosition pos, ContentSet content) { none() } predicate clearsContent(ParameterPosition pos, ContentSet content) { none() }
/**
* Holds if the summary is auto generated.
*/
predicate isAutoGenerated() { none() }
} }
} }
@@ -234,7 +251,9 @@ module Private {
TContentSummaryComponent(ContentSet c) or TContentSummaryComponent(ContentSet c) or
TParameterSummaryComponent(ArgumentPosition pos) or TParameterSummaryComponent(ArgumentPosition pos) or
TArgumentSummaryComponent(ParameterPosition pos) or TArgumentSummaryComponent(ParameterPosition pos) or
TReturnSummaryComponent(ReturnKind rk) TReturnSummaryComponent(ReturnKind rk) or
TWithoutContentSummaryComponent(ContentSet c) or
TWithContentSummaryComponent(ContentSet c)
private TParameterSummaryComponent thisParam() { private TParameterSummaryComponent thisParam() {
result = TParameterSummaryComponent(instanceParameterPosition()) result = TParameterSummaryComponent(instanceParameterPosition())
@@ -296,6 +315,23 @@ module Private {
SummaryComponentStack::singleton(TArgumentSummaryComponent(_))) and SummaryComponentStack::singleton(TArgumentSummaryComponent(_))) and
preservesValue = preservesValue1.booleanAnd(preservesValue2) preservesValue = preservesValue1.booleanAnd(preservesValue2)
) )
or
exists(ParameterPosition ppos, ContentSet cs |
c.clearsContent(ppos, cs) and
input = SummaryComponentStack::push(SummaryComponent::withoutContent(cs), output) and
output = SummaryComponentStack::argument(ppos) and
preservesValue = true
)
}
private class MkClearStack extends RequiredSummaryComponentStack {
override predicate required(SummaryComponent head, SummaryComponentStack tail) {
exists(SummarizedCallable sc, ParameterPosition ppos, ContentSet cs |
sc.clearsContent(ppos, cs) and
head = SummaryComponent::withoutContent(cs) and
tail = SummaryComponentStack::argument(ppos)
)
}
} }
/** /**
@@ -378,10 +414,7 @@ module Private {
private newtype TSummaryNodeState = private newtype TSummaryNodeState =
TSummaryNodeInputState(SummaryComponentStack s) { inputState(_, s) } or TSummaryNodeInputState(SummaryComponentStack s) { inputState(_, s) } or
TSummaryNodeOutputState(SummaryComponentStack s) { outputState(_, s) } or TSummaryNodeOutputState(SummaryComponentStack s) { outputState(_, s) }
TSummaryNodeClearsContentState(ParameterPosition pos, boolean post) {
any(SummarizedCallable sc).clearsContent(pos, _) and post in [false, true]
}
/** /**
* A state used to break up (complex) flow summaries into atomic flow steps. * A state used to break up (complex) flow summaries into atomic flow steps.
@@ -428,12 +461,6 @@ module Private {
this = TSummaryNodeOutputState(s) and this = TSummaryNodeOutputState(s) and
result = "to write: " + s result = "to write: " + s
) )
or
exists(ParameterPosition pos, boolean post, string postStr |
this = TSummaryNodeClearsContentState(pos, post) and
(if post = true then postStr = " (post)" else postStr = "") and
result = "clear: " + pos + postStr
)
} }
} }
@@ -457,11 +484,6 @@ module Private {
not parameterReadState(c, state, _) not parameterReadState(c, state, _)
or or
state.isOutputState(c, _) state.isOutputState(c, _)
or
exists(ParameterPosition pos |
c.clearsContent(pos, _) and
state = TSummaryNodeClearsContentState(pos, _)
)
} }
pragma[noinline] pragma[noinline]
@@ -497,8 +519,6 @@ module Private {
parameterReadState(c, _, pos) parameterReadState(c, _, pos)
or or
isParameterPostUpdate(_, c, pos) isParameterPostUpdate(_, c, pos)
or
c.clearsContent(pos, _)
} }
private predicate callbackOutput( private predicate callbackOutput(
@@ -506,7 +526,7 @@ module Private {
) { ) {
any(SummaryNodeState state).isInputState(c, s) and any(SummaryNodeState state).isInputState(c, s) and
s.head() = TReturnSummaryComponent(rk) and s.head() = TReturnSummaryComponent(rk) and
receiver = summaryNodeInputState(c, s.drop(1)) receiver = summaryNodeInputState(c, s.tail())
} }
private predicate callbackInput( private predicate callbackInput(
@@ -514,7 +534,7 @@ module Private {
) { ) {
any(SummaryNodeState state).isOutputState(c, s) and any(SummaryNodeState state).isOutputState(c, s) and
s.head() = TParameterSummaryComponent(pos) and s.head() = TParameterSummaryComponent(pos) and
receiver = summaryNodeInputState(c, s.drop(1)) receiver = summaryNodeInputState(c, s.tail())
} }
/** Holds if a call targeting `receiver` should be synthesized inside `c`. */ /** Holds if a call targeting `receiver` should be synthesized inside `c`. */
@@ -540,15 +560,21 @@ module Private {
exists(SummarizedCallable c, SummaryComponentStack s, SummaryComponent head | head = s.head() | exists(SummarizedCallable c, SummaryComponentStack s, SummaryComponent head | head = s.head() |
n = summaryNodeInputState(c, s) and n = summaryNodeInputState(c, s) and
( (
exists(ContentSet cont | result = getContentType(cont) |
head = TContentSummaryComponent(cont) or
head = TWithContentSummaryComponent(cont)
)
or
exists(ContentSet cont | exists(ContentSet cont |
head = TContentSummaryComponent(cont) and result = getContentType(cont) head = TWithoutContentSummaryComponent(cont) and
result = getNodeType(summaryNodeInputState(c, s.tail()))
) )
or or
exists(ReturnKind rk | exists(ReturnKind rk |
head = TReturnSummaryComponent(rk) and head = TReturnSummaryComponent(rk) and
result = result =
getCallbackReturnType(getNodeType(summaryNodeInputState(pragma[only_bind_out](c), getCallbackReturnType(getNodeType(summaryNodeInputState(pragma[only_bind_out](c),
s.drop(1))), rk) s.tail())), rk)
) )
) )
or or
@@ -567,16 +593,10 @@ module Private {
exists(ArgumentPosition pos | head = TParameterSummaryComponent(pos) | exists(ArgumentPosition pos | head = TParameterSummaryComponent(pos) |
result = result =
getCallbackParameterType(getNodeType(summaryNodeInputState(pragma[only_bind_out](c), getCallbackParameterType(getNodeType(summaryNodeInputState(pragma[only_bind_out](c),
s.drop(1))), pos) s.tail())), pos)
) )
) )
) )
or
exists(SummarizedCallable c, ParameterPosition pos, ParamNode p |
n = summaryNode(c, TSummaryNodeClearsContentState(pos, false)) and
p.isParameterOf(c, pos) and
result = getNodeType(p)
)
} }
/** Holds if summary node `out` contains output of kind `rk` from call `c`. */ /** Holds if summary node `out` contains output of kind `rk` from call `c`. */
@@ -602,9 +622,6 @@ module Private {
exists(SummarizedCallable c, ParameterPosition pos | exists(SummarizedCallable c, ParameterPosition pos |
isParameterPostUpdate(post, c, pos) and isParameterPostUpdate(post, c, pos) and
pre.(ParamNode).isParameterOf(c, pos) pre.(ParamNode).isParameterOf(c, pos)
or
pre = summaryNode(c, TSummaryNodeClearsContentState(pos, false)) and
post = summaryNode(c, TSummaryNodeClearsContentState(pos, true))
) )
or or
exists(SummarizedCallable callable, SummaryComponentStack s | exists(SummarizedCallable callable, SummaryComponentStack s |
@@ -628,8 +645,6 @@ module Private {
*/ */
predicate summaryAllowParameterReturnInSelf(ParamNode p) { predicate summaryAllowParameterReturnInSelf(ParamNode p) {
exists(SummarizedCallable c, ParameterPosition ppos | p.isParameterOf(c, ppos) | exists(SummarizedCallable c, ParameterPosition ppos | p.isParameterOf(c, ppos) |
c.clearsContent(ppos, _)
or
exists(SummaryComponentStack inputContents, SummaryComponentStack outputContents | exists(SummaryComponentStack inputContents, SummaryComponentStack outputContents |
summary(c, inputContents, outputContents, _) and summary(c, inputContents, outputContents, _) and
inputContents.bottom() = pragma[only_bind_into](TArgumentSummaryComponent(ppos)) and inputContents.bottom() = pragma[only_bind_into](TArgumentSummaryComponent(ppos)) and
@@ -658,9 +673,10 @@ module Private {
preservesValue = false and not summary(c, inputContents, outputContents, true) preservesValue = false and not summary(c, inputContents, outputContents, true)
) )
or or
exists(SummarizedCallable c, ParameterPosition pos | exists(SummarizedCallable c, SummaryComponentStack s |
pred.(ParamNode).isParameterOf(c, pos) and pred = summaryNodeInputState(c, s.tail()) and
succ = summaryNode(c, TSummaryNodeClearsContentState(pos, _)) and succ = summaryNodeInputState(c, s) and
s.head() = [SummaryComponent::withContent(_), SummaryComponent::withoutContent(_)] and
preservesValue = true preservesValue = true
) )
} }
@@ -671,7 +687,7 @@ module Private {
*/ */
predicate summaryReadStep(Node pred, ContentSet c, Node succ) { predicate summaryReadStep(Node pred, ContentSet c, Node succ) {
exists(SummarizedCallable sc, SummaryComponentStack s | exists(SummarizedCallable sc, SummaryComponentStack s |
pred = summaryNodeInputState(sc, s.drop(1)) and pred = summaryNodeInputState(sc, s.tail()) and
succ = summaryNodeInputState(sc, s) and succ = summaryNodeInputState(sc, s) and
SummaryComponent::content(c) = s.head() SummaryComponent::content(c) = s.head()
) )
@@ -684,7 +700,7 @@ module Private {
predicate summaryStoreStep(Node pred, ContentSet c, Node succ) { predicate summaryStoreStep(Node pred, ContentSet c, Node succ) {
exists(SummarizedCallable sc, SummaryComponentStack s | exists(SummarizedCallable sc, SummaryComponentStack s |
pred = summaryNodeOutputState(sc, s) and pred = summaryNodeOutputState(sc, s) and
succ = summaryNodeOutputState(sc, s.drop(1)) and succ = summaryNodeOutputState(sc, s.tail()) and
SummaryComponent::content(c) = s.head() SummaryComponent::content(c) = s.head()
) )
} }
@@ -709,9 +725,22 @@ module Private {
* node where field `b` is cleared). * node where field `b` is cleared).
*/ */
predicate summaryClearsContent(Node n, ContentSet c) { predicate summaryClearsContent(Node n, ContentSet c) {
exists(SummarizedCallable sc, ParameterPosition pos | exists(SummarizedCallable sc, SummaryNodeState state, SummaryComponentStack stack |
n = summaryNode(sc, TSummaryNodeClearsContentState(pos, true)) and n = summaryNode(sc, state) and
sc.clearsContent(pos, c) state.isInputState(sc, stack) and
stack.head() = SummaryComponent::withoutContent(c)
)
}
/**
* Holds if the value that is being tracked is expected to be stored inside
* content `c` at `n`.
*/
predicate summaryExpectsContent(Node n, ContentSet c) {
exists(SummarizedCallable sc, SummaryNodeState state, SummaryComponentStack stack |
n = summaryNode(sc, state) and
state.isInputState(sc, stack) and
stack.head() = SummaryComponent::withContent(c)
) )
} }
@@ -723,22 +752,6 @@ module Private {
sc = viableCallable(call) sc = viableCallable(call)
} }
/**
* Holds if values stored inside content `c` are cleared inside a
* callable to which `arg` is an argument.
*
* In such cases, it is important to prevent use-use flow out of
* `arg` (see comment for `summaryClearsContent`).
*/
pragma[nomagic]
predicate summaryClearsContentArg(ArgNode arg, ContentSet c) {
exists(DataFlowCall call, SummarizedCallable sc, ParameterPosition ppos |
argumentPositionMatch(call, arg, ppos) and
viableParam(call, sc, ppos, _) and
sc.clearsContent(ppos, c)
)
}
pragma[nomagic] pragma[nomagic]
private ParamNode summaryArgParam0(DataFlowCall call, ArgNode arg) { private ParamNode summaryArgParam0(DataFlowCall call, ArgNode arg) {
exists(ParameterPosition ppos, SummarizedCallable sc | exists(ParameterPosition ppos, SummarizedCallable sc |
@@ -747,6 +760,27 @@ module Private {
) )
} }
/**
* Holds if use-use flow starting from `arg` should be prohibited.
*
* This is the case when `arg` is the argument of a call that targets a
* flow summary where the corresponding parameter either clears contents
* or expects contents.
*/
pragma[nomagic]
predicate prohibitsUseUseFlow(ArgNode arg) {
exists(ParamNode p, Node mid, ParameterPosition ppos, Node ret |
p = summaryArgParam0(_, arg) and
p.isParameterOf(_, ppos) and
summaryLocalStep(p, mid, true) and
summaryLocalStep(mid, ret, true) and
isParameterPostUpdate(ret, _, ppos)
|
summaryClearsContent(mid, _) or
summaryExpectsContent(mid, _)
)
}
pragma[nomagic] pragma[nomagic]
private ParamNode summaryArgParam(ArgNode arg, ReturnKindExt rk, OutNodeExt out) { private ParamNode summaryArgParam(ArgNode arg, ReturnKindExt rk, OutNodeExt out) {
exists(DataFlowCall call | exists(DataFlowCall call |
@@ -898,6 +932,8 @@ module Private {
kind = "taint" and preservesValue = false kind = "taint" and preservesValue = false
) )
} }
override predicate isAutoGenerated() { summaryElement(this, _, _, _, true) }
} }
/** Holds if component `c` of specification `spec` cannot be parsed. */ /** Holds if component `c` of specification `spec` cannot be parsed. */
@@ -1052,9 +1088,13 @@ module Private {
preservesValue = false and result = "taint" preservesValue = false and result = "taint"
} }
private string renderGenerated(RelevantSummarizedCallable c) {
if c.isAutoGenerated() then result = "generated:" else result = ""
}
/** /**
* A query predicate for outputting flow summaries in semi-colon separated format in QL tests. * A query predicate for outputting flow summaries in semi-colon separated format in QL tests.
* The syntax is: "namespace;type;overrides;name;signature;ext;inputspec;outputspec;kind", * The syntax is: "namespace;type;overrides;name;signature;ext;inputspec;outputspec;(generated:)?kind",
* ext is hardcoded to empty. * ext is hardcoded to empty.
*/ */
query predicate summary(string csv) { query predicate summary(string csv) {
@@ -1065,7 +1105,7 @@ module Private {
c.relevantSummary(input, output, preservesValue) and c.relevantSummary(input, output, preservesValue) and
csv = csv =
c.getCallableCsv() + getComponentStackCsv(input) + ";" + getComponentStackCsv(output) + c.getCallableCsv() + getComponentStackCsv(input) + ";" + getComponentStackCsv(output) +
";" + renderKind(preservesValue) ";" + renderGenerated(c) + renderKind(preservesValue)
) )
} }
} }
@@ -1141,6 +1181,10 @@ module Private {
Private::Steps::summaryClearsContent(a.asNode(), c) and Private::Steps::summaryClearsContent(a.asNode(), c) and
b = a and b = a and
value = "clear (" + c + ")" value = "clear (" + c + ")"
or
Private::Steps::summaryExpectsContent(a.asNode(), c) and
b = a and
value = "expect (" + c + ")"
) )
or or
summaryPostUpdateNode(b.asNode(), a.asNode()) and summaryPostUpdateNode(b.asNode(), a.asNode()) and

View File

@@ -49,7 +49,7 @@ private predicate unknownSign(Expr e) {
or or
exists(LongLiteral lit | lit = e and not exists(lit.getValue().toFloat())) exists(LongLiteral lit | lit = e and not exists(lit.getValue().toFloat()))
or or
exists(CastExpr cast, Type fromtyp | exists(CastingExpr cast, Type fromtyp |
cast = e and cast = e and
fromtyp = cast.getSourceType() and fromtyp = cast.getSourceType() and
not fromtyp instanceof NumericOrCharType not fromtyp instanceof NumericOrCharType

View File

@@ -29,6 +29,8 @@ module Private {
class CastExpr = RU::ExprNode::CastExpr; class CastExpr = RU::ExprNode::CastExpr;
class CastingExpr = CastExpr;
class Type = CS::Type; class Type = CS::Type;
class Expr = CS::ControlFlow::Nodes::ExprNode; class Expr = CS::ControlFlow::Nodes::ExprNode;

View File

@@ -175,8 +175,8 @@ class Call extends DotNet::Call, Expr, @call {
* - Line 10: The static target is `Type.InvokeMember()`, whereas the run-time targets * - Line 10: The static target is `Type.InvokeMember()`, whereas the run-time targets
* are both `A.M()` and `B.M()`. * are both `A.M()` and `B.M()`.
* *
* - Line 16: There is no static target (delegate call) but the delegate `i => { }` (line * - Line 16: There is no static target (delegate call) but the delegate `i => { }`
* 20) is a run-time target. * (line 20) is a run-time target.
*/ */
override Callable getARuntimeTarget() { override Callable getARuntimeTarget() {
exists(DispatchCall dc | dc.getCall() = this | result = dc.getADynamicTarget()) exists(DispatchCall dc | dc.getCall() = this | result = dc.getADynamicTarget())

View File

@@ -55,7 +55,7 @@ private class SystemCollectionsIEnumerableClearFlow extends SummarizedCallable {
} }
override predicate clearsContent(ParameterPosition pos, DataFlow::ContentSet content) { override predicate clearsContent(ParameterPosition pos, DataFlow::ContentSet content) {
pos.isThisParameter() and (if this.(Modifiable).isStatic() then pos.getPosition() = 0 else pos.isThisParameter()) and
content instanceof DataFlow::ElementContent content instanceof DataFlow::ElementContent
} }
} }

View File

@@ -1,6 +1,6 @@
/** /**
* @name Potentially incorrect CompareTo(...) signature * @name Potentially incorrect CompareTo(...) signature
* @description The declaring type of a method with signature 'CompareTo(T)' does not implement 'IComparable<T>'. * @description The declaring type of a method with signature `CompareTo(T)` does not implement `IComparable<T>`.
* @kind problem * @kind problem
* @problem.severity warning * @problem.severity warning
* @precision medium * @precision medium

View File

@@ -1,3 +1,5 @@
## 0.1.1
## 0.1.0 ## 0.1.0
## 0.0.13 ## 0.0.13

View File

@@ -1,6 +1,6 @@
/** /**
* @name Incorrect parameter name in documentation * @name Incorrect parameter name in documentation
* @description The parameter name given in a '<param>' tag does not exist. Rename the parameter or * @description The parameter name given in a `<param>` tag does not exist. Rename the parameter or
* change the name in the documentation to ensure that they are the same. * change the name in the documentation to ensure that they are the same.
* @kind problem * @kind problem
* @problem.severity recommendation * @problem.severity recommendation

View File

@@ -1,6 +1,6 @@
/** /**
* @name Incorrect type parameter name in documentation * @name Incorrect type parameter name in documentation
* @description The type parameter name given in a '<typeparam>' tag does not exist. Rename the parameter or * @description The type parameter name given in a `<typeparam>` tag does not exist. Rename the parameter or
* change the name in the documentation to ensure that they are the same. * change the name in the documentation to ensure that they are the same.
* @kind problem * @kind problem
* @problem.severity recommendation * @problem.severity recommendation

View File

@@ -1,6 +1,6 @@
/** /**
* @name Missing documentation for exception * @name Missing documentation for exception
* @description Exceptions thrown by the method should be documented using '<exception cref="..."> </exception>' tags. * @description Exceptions thrown by the method should be documented using `<exception cref="..."> </exception>` tags.
* Ensure that the correct type of the exception is given in the 'cref' attribute. * Ensure that the correct type of the exception is given in the 'cref' attribute.
* @kind problem * @kind problem
* @problem.severity recommendation * @problem.severity recommendation

View File

@@ -1,6 +1,6 @@
/** /**
* @name Missing documentation for parameter * @name Missing documentation for parameter
* @description All parameters should be documented using '<param name="..."> </param>' tags. * @description All parameters should be documented using `<param name="..."> </param>` tags.
* Ensure that the name attribute matches the name of the parameter. * Ensure that the name attribute matches the name of the parameter.
* @kind problem * @kind problem
* @problem.severity recommendation * @problem.severity recommendation

View File

@@ -1,7 +1,7 @@
/** /**
* @name Missing documentation for return value * @name Missing documentation for return value
* @description The method returns a value, but the return value is not documented using * @description The method returns a value, but the return value is not documented using
* a '<returns>' tag. * a `<returns>` tag.
* @kind problem * @kind problem
* @problem.severity recommendation * @problem.severity recommendation
* @precision low * @precision low

View File

@@ -1,6 +1,6 @@
/** /**
* @name Missing a summary in documentation comment * @name Missing a summary in documentation comment
* @description The documentation comment does not contain a '<summary>' tag. * @description The documentation comment does not contain a `<summary>` tag.
* @kind problem * @kind problem
* @problem.severity recommendation * @problem.severity recommendation
* @precision high * @precision high

View File

@@ -1,6 +1,6 @@
/** /**
* @name Missing documentation for type parameter * @name Missing documentation for type parameter
* @description All type parameters should be documented using '<typeparam name="..."> </typeparam>' tags. * @description All type parameters should be documented using `<typeparam name="..."> </typeparam>` tags.
* Ensure that the 'name' attribute matches the name of the type parameter. * Ensure that the 'name' attribute matches the name of the type parameter.
* @kind problem * @kind problem
* @problem.severity recommendation * @problem.severity recommendation

View File

@@ -2,7 +2,7 @@
* @name XML injection * @name XML injection
* @description Building an XML document from user-controlled sources is vulnerable to insertion of * @description Building an XML document from user-controlled sources is vulnerable to insertion of
* malicious code by the user. * malicious code by the user.
* @kind problem * @kind path-problem
* @id cs/xml-injection * @id cs/xml-injection
* @problem.severity error * @problem.severity error
* @security-severity 8.8 * @security-severity 8.8
@@ -12,6 +12,7 @@
*/ */
import csharp import csharp
import DataFlow::PathGraph
import semmle.code.csharp.security.dataflow.flowsources.Remote import semmle.code.csharp.security.dataflow.flowsources.Remote
import semmle.code.csharp.frameworks.system.Xml import semmle.code.csharp.frameworks.system.Xml
@@ -45,6 +46,6 @@ class TaintTrackingConfiguration extends TaintTracking::Configuration {
} }
} }
from TaintTrackingConfiguration c, DataFlow::Node source, DataFlow::Node sink from TaintTrackingConfiguration c, DataFlow::PathNode source, DataFlow::PathNode sink
where c.hasFlow(source, sink) where c.hasFlowPath(source, sink)
select sink, "$@ flows to here and is inserted as XML.", source, "User-provided value" select sink, source, sink, "$@ flows to here and is inserted as XML.", source, "User-provided value"

View File

@@ -71,7 +71,7 @@ if (version != "latest"):
cmd.append(version) cmd.append(version)
run_cmd(cmd) run_cmd(cmd)
sdk_version = '6.0.101' sdk_version = '6.0.202'
print("\n* Creating new global.json file and setting SDK to " + sdk_version) print("\n* Creating new global.json file and setting SDK to " + sdk_version)
run_cmd(['dotnet', 'new', 'globaljson', '--force', '--sdk-version', sdk_version, '--output', workDir]) run_cmd(['dotnet', 'new', 'globaljson', '--force', '--sdk-version', sdk_version, '--output', workDir])

View File

@@ -2,7 +2,7 @@
* @name External libraries * @name External libraries
* @description A list of external libraries used in the code * @description A list of external libraries used in the code
* @kind metric * @kind metric
* @tags summary * @tags summary telemetry
* @id csharp/telemetry/external-libs * @id csharp/telemetry/external-libs
*/ */

View File

@@ -2,7 +2,7 @@
* @name Supported sinks in external libraries * @name Supported sinks in external libraries
* @description A list of 3rd party APIs detected as sinks. Excludes APIs exposed by test libraries. * @description A list of 3rd party APIs detected as sinks. Excludes APIs exposed by test libraries.
* @kind metric * @kind metric
* @tags summary * @tags summary telemetry
* @id csharp/telemetry/supported-external-api-sinks * @id csharp/telemetry/supported-external-api-sinks
*/ */

View File

@@ -2,7 +2,7 @@
* @name Supported sources in external libraries * @name Supported sources in external libraries
* @description A list of 3rd party APIs detected as sources. Excludes APIs exposed by test libraries. * @description A list of 3rd party APIs detected as sources. Excludes APIs exposed by test libraries.
* @kind metric * @kind metric
* @tags summary * @tags summary telemetry
* @id csharp/telemetry/supported-external-api-sources * @id csharp/telemetry/supported-external-api-sources
*/ */

View File

@@ -2,7 +2,7 @@
* @name Supported flow steps in external libraries * @name Supported flow steps in external libraries
* @description A list of 3rd party APIs detected as flow steps. Excludes APIs exposed by test libraries. * @description A list of 3rd party APIs detected as flow steps. Excludes APIs exposed by test libraries.
* @kind metric * @kind metric
* @tags summary * @tags summary telemetry
* @id csharp/telemetry/supported-external-api-taint * @id csharp/telemetry/supported-external-api-taint
*/ */

View File

@@ -2,7 +2,7 @@
* @name Usage of unsupported APIs coming from external libraries * @name Usage of unsupported APIs coming from external libraries
* @description A list of 3rd party APIs used in the codebase. Excludes APIs exposed by test libraries. * @description A list of 3rd party APIs used in the codebase. Excludes APIs exposed by test libraries.
* @kind metric * @kind metric
* @tags summary * @tags summary telemetry
* @id csharp/telemetry/unsupported-external-api * @id csharp/telemetry/unsupported-external-api
*/ */

View File

@@ -0,0 +1 @@
## 0.1.1

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