mirror of
https://github.com/github/codeql.git
synced 2026-05-26 17:11:24 +02:00
Compare commits
14 Commits
turbo/expe
...
tiferet/co
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
3af4e65695 | ||
|
|
a67886e0aa | ||
|
|
f8336ce9be | ||
|
|
68da966732 | ||
|
|
61360577ba | ||
|
|
099916f88f | ||
|
|
fbcfd523f4 | ||
|
|
7a8715d1ef | ||
|
|
c92bc77b59 | ||
|
|
3f1ca89bd3 | ||
|
|
2a324f5c5d | ||
|
|
9a8b0d7fb2 | ||
|
|
dfbfa5d27d | ||
|
|
4a2046476a |
21
.github/workflows/check-query-ids.yml
vendored
21
.github/workflows/check-query-ids.yml
vendored
@@ -1,21 +0,0 @@
|
||||
name: Check query IDs
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
paths:
|
||||
- "**/src/**/*.ql"
|
||||
- misc/scripts/check-query-ids.py
|
||||
- .github/workflows/check-query-ids.yml
|
||||
branches:
|
||||
- main
|
||||
- "rc/*"
|
||||
workflow_dispatch:
|
||||
|
||||
jobs:
|
||||
check:
|
||||
name: Check query IDs
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- name: Check for duplicate query IDs
|
||||
run: python3 misc/scripts/check-query-ids.py
|
||||
1
.github/workflows/swift.yml
vendored
1
.github/workflows/swift.yml
vendored
@@ -65,7 +65,6 @@ jobs:
|
||||
if : ${{ github.event_name == 'pull_request' }}
|
||||
needs: build-and-test-macos
|
||||
runs-on: macos-12-xl
|
||||
timeout-minutes: 60
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: ./swift/actions/run-integration-tests
|
||||
|
||||
@@ -470,10 +470,6 @@
|
||||
"javascript/ql/src/Comments/CommentedOutCodeReferences.inc.qhelp",
|
||||
"python/ql/src/Lexical/CommentedOutCodeReferences.inc.qhelp"
|
||||
],
|
||||
"ThreadResourceAbuse qhelp": [
|
||||
"java/ql/src/experimental/Security/CWE/CWE-400/LocalThreadResourceAbuse.qhelp",
|
||||
"java/ql/src/experimental/Security/CWE/CWE-400/ThreadResourceAbuse.qhelp"
|
||||
],
|
||||
"IDE Contextual Queries": [
|
||||
"cpp/ql/lib/IDEContextual.qll",
|
||||
"csharp/ql/lib/IDEContextual.qll",
|
||||
@@ -541,11 +537,6 @@
|
||||
"ruby/ql/lib/codeql/ruby/frameworks/data/internal/ApiGraphModels.qll",
|
||||
"python/ql/lib/semmle/python/frameworks/data/internal/ApiGraphModels.qll"
|
||||
],
|
||||
"ApiGraphModelsExtensions": [
|
||||
"javascript/ql/lib/semmle/javascript/frameworks/data/internal/ApiGraphModelsExtensions.qll",
|
||||
"ruby/ql/lib/codeql/ruby/frameworks/data/internal/ApiGraphModelsExtensions.qll",
|
||||
"python/ql/lib/semmle/python/frameworks/data/internal/ApiGraphModelsExtensions.qll"
|
||||
],
|
||||
"TaintedFormatStringQuery Ruby/JS": [
|
||||
"javascript/ql/lib/semmle/javascript/security/dataflow/TaintedFormatStringQuery.qll",
|
||||
"ruby/ql/lib/codeql/ruby/security/TaintedFormatStringQuery.qll"
|
||||
@@ -589,9 +580,5 @@
|
||||
"IncompleteMultiCharacterSanitization JS/Ruby": [
|
||||
"javascript/ql/lib/semmle/javascript/security/IncompleteMultiCharacterSanitizationQuery.qll",
|
||||
"ruby/ql/lib/codeql/ruby/security/IncompleteMultiCharacterSanitizationQuery.qll"
|
||||
],
|
||||
"EncryptionKeySizes Python/Java": [
|
||||
"python/ql/lib/semmle/python/security/internal/EncryptionKeySizes.qll",
|
||||
"java/ql/lib/semmle/code/java/security/internal/EncryptionKeySizes.qll"
|
||||
]
|
||||
}
|
||||
|
||||
@@ -1,7 +1,3 @@
|
||||
## 0.4.5
|
||||
|
||||
No user-facing changes.
|
||||
|
||||
## 0.4.4
|
||||
|
||||
No user-facing changes.
|
||||
|
||||
@@ -1,6 +0,0 @@
|
||||
---
|
||||
category: deprecated
|
||||
---
|
||||
|
||||
* Deprecated `semmle.code.cpp.ir.dataflow.DefaultTaintTracking`. Use `semmle.code.cpp.ir.dataflow.TaintTracking`.
|
||||
* Deprecated `semmle.code.cpp.security.TaintTrackingImpl`. Use `semmle.code.cpp.ir.dataflow.TaintTracking`.
|
||||
@@ -1,4 +0,0 @@
|
||||
---
|
||||
category: minorAnalysis
|
||||
---
|
||||
* The `getaddrinfo` function is now recognized as a flow source.
|
||||
@@ -1,4 +0,0 @@
|
||||
---
|
||||
category: minorAnalysis
|
||||
---
|
||||
* The `secure_getenv` and `_wgetenv` functions are now recognized as local flow sources.
|
||||
@@ -1,4 +0,0 @@
|
||||
---
|
||||
category: minorAnalysis
|
||||
---
|
||||
* The `scanf` and `fscanf` functions and their variants are now recognized as flow sources.
|
||||
@@ -1,4 +0,0 @@
|
||||
---
|
||||
category: minorAnalysis
|
||||
---
|
||||
* The `ArgvSource` flow source has been generalized to handle cases where the argument vector of `main` is not named `argv`.
|
||||
@@ -1,3 +0,0 @@
|
||||
## 0.4.5
|
||||
|
||||
No user-facing changes.
|
||||
@@ -1,2 +1,2 @@
|
||||
---
|
||||
lastReleaseVersion: 0.4.5
|
||||
lastReleaseVersion: 0.4.4
|
||||
|
||||
@@ -621,6 +621,23 @@ private predicate parameterFlowThroughAllowed(ParamNodeEx p, ReturnKindExt kind)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if flow from a parameter at position `pos` inside `c` to a return node of
|
||||
* kind `kind` is allowed.
|
||||
*
|
||||
* We don't expect a parameter to return stored in itself, unless
|
||||
* explicitly allowed
|
||||
*/
|
||||
bindingset[c, pos, kind]
|
||||
private predicate parameterFlowThroughAllowed(
|
||||
DataFlowCallable c, ParameterPosition pos, ReturnKindExt kind
|
||||
) {
|
||||
exists(ParamNodeEx p |
|
||||
p.isParameterOf(c, pos) and
|
||||
parameterFlowThroughAllowed(p, kind)
|
||||
)
|
||||
}
|
||||
|
||||
private module Stage1 implements StageSig {
|
||||
class Ap = Unit;
|
||||
|
||||
@@ -963,12 +980,6 @@ private module Stage1 implements StageSig {
|
||||
pragma[nomagic]
|
||||
predicate revFlow(NodeEx node, Configuration config) { revFlow(node, _, config) }
|
||||
|
||||
pragma[nomagic]
|
||||
predicate revFlowAp(NodeEx node, Ap ap, Configuration config) {
|
||||
revFlow(node, config) and
|
||||
exists(ap)
|
||||
}
|
||||
|
||||
bindingset[node, state, config]
|
||||
predicate revFlow(NodeEx node, FlowState state, Ap ap, Configuration config) {
|
||||
revFlow(node, _, pragma[only_bind_into](config)) and
|
||||
@@ -1011,13 +1022,9 @@ private module Stage1 implements StageSig {
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
predicate returnMayFlowThrough(
|
||||
RetNodeEx ret, Ap argAp, Ap ap, ReturnKindExt kind, Configuration config
|
||||
) {
|
||||
predicate returnMayFlowThrough(RetNodeEx ret, ReturnKindExt kind, Configuration config) {
|
||||
throughFlowNodeCand(ret, config) and
|
||||
kind = ret.getKind() and
|
||||
exists(argAp) and
|
||||
exists(ap)
|
||||
kind = ret.getKind()
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
@@ -1182,8 +1189,6 @@ private signature module StageSig {
|
||||
|
||||
predicate revFlow(NodeEx node, Configuration config);
|
||||
|
||||
predicate revFlowAp(NodeEx node, Ap ap, Configuration config);
|
||||
|
||||
bindingset[node, state, config]
|
||||
predicate revFlow(NodeEx node, FlowState state, Ap ap, Configuration config);
|
||||
|
||||
@@ -1191,9 +1196,7 @@ private signature module StageSig {
|
||||
|
||||
predicate parameterMayFlowThrough(ParamNodeEx p, Ap ap, Configuration config);
|
||||
|
||||
predicate returnMayFlowThrough(
|
||||
RetNodeEx ret, Ap argAp, Ap ap, ReturnKindExt kind, Configuration config
|
||||
);
|
||||
predicate returnMayFlowThrough(RetNodeEx ret, ReturnKindExt kind, Configuration config);
|
||||
|
||||
predicate storeStepCand(
|
||||
NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, DataFlowType contentType,
|
||||
@@ -1278,43 +1281,21 @@ private module MkStage<StageSig PrevStage> {
|
||||
import Param
|
||||
|
||||
/* Begin: Stage logic. */
|
||||
// use an alias as a workaround for bad functionality-induced joins
|
||||
pragma[nomagic]
|
||||
private predicate revFlowApAlias(NodeEx node, ApApprox apa, Configuration config) {
|
||||
PrevStage::revFlowAp(node, apa, config)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate flowIntoCallApa(
|
||||
DataFlowCall call, ArgNodeEx arg, ParamNodeEx p, boolean allowsFieldFlow, ApApprox apa,
|
||||
Configuration config
|
||||
) {
|
||||
flowIntoCall(call, arg, p, allowsFieldFlow, config) and
|
||||
PrevStage::revFlowAp(p, pragma[only_bind_into](apa), pragma[only_bind_into](config)) and
|
||||
revFlowApAlias(arg, pragma[only_bind_into](apa), pragma[only_bind_into](config))
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate flowOutOfCallApa(
|
||||
DataFlowCall call, RetNodeEx ret, ReturnKindExt kind, NodeEx out, boolean allowsFieldFlow,
|
||||
ApApprox apa, Configuration config
|
||||
) {
|
||||
flowOutOfCall(call, ret, kind, out, allowsFieldFlow, config) and
|
||||
PrevStage::revFlowAp(out, pragma[only_bind_into](apa), pragma[only_bind_into](config)) and
|
||||
revFlowApAlias(ret, pragma[only_bind_into](apa), pragma[only_bind_into](config))
|
||||
bindingset[result, apa]
|
||||
private ApApprox unbindApa(ApApprox apa) {
|
||||
pragma[only_bind_out](apa) = pragma[only_bind_out](result)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate flowThroughOutOfCall(
|
||||
DataFlowCall call, CcCall ccc, RetNodeEx ret, NodeEx out, boolean allowsFieldFlow,
|
||||
ApApprox argApa, ApApprox apa, Configuration config
|
||||
DataFlowCall call, DataFlowCallable c, CcCall ccc, RetNodeEx ret, ReturnKindExt kind,
|
||||
NodeEx out, boolean allowsFieldFlow, Configuration config
|
||||
) {
|
||||
exists(ReturnKindExt kind |
|
||||
flowOutOfCallApa(call, ret, kind, out, allowsFieldFlow, apa, pragma[only_bind_into](config)) and
|
||||
PrevStage::callMayFlowThroughRev(call, pragma[only_bind_into](config)) and
|
||||
PrevStage::returnMayFlowThrough(ret, argApa, apa, kind, pragma[only_bind_into](config)) and
|
||||
matchesCall(ccc, call)
|
||||
)
|
||||
flowOutOfCall(call, ret, kind, out, allowsFieldFlow, pragma[only_bind_into](config)) and
|
||||
PrevStage::callMayFlowThroughRev(call, pragma[only_bind_into](config)) and
|
||||
PrevStage::returnMayFlowThrough(ret, kind, pragma[only_bind_into](config)) and
|
||||
matchesCall(ccc, call) and
|
||||
c = ret.getEnclosingCallable()
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1326,50 +1307,39 @@ private module MkStage<StageSig PrevStage> {
|
||||
* corresponding parameter position and access path of that argument, respectively.
|
||||
*/
|
||||
pragma[nomagic]
|
||||
additional predicate fwdFlow(
|
||||
NodeEx node, FlowState state, Cc cc, ParameterPositionOption summaryCtx, ApOption argAp,
|
||||
Ap ap, ApApprox apa, Configuration config
|
||||
) {
|
||||
fwdFlow0(node, state, cc, summaryCtx, argAp, ap, apa, config) and
|
||||
PrevStage::revFlow(node, state, apa, config) and
|
||||
filter(node, state, ap, config)
|
||||
}
|
||||
|
||||
pragma[inline]
|
||||
additional predicate fwdFlow(
|
||||
NodeEx node, FlowState state, Cc cc, ParameterPositionOption summaryCtx, ApOption argAp,
|
||||
Ap ap, Configuration config
|
||||
) {
|
||||
fwdFlow(node, state, cc, summaryCtx, argAp, ap, _, config)
|
||||
fwdFlow0(node, state, cc, summaryCtx, argAp, ap, config) and
|
||||
PrevStage::revFlow(node, state, unbindApa(getApprox(ap)), config) and
|
||||
filter(node, state, ap, config)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate fwdFlow0(
|
||||
NodeEx node, FlowState state, Cc cc, ParameterPositionOption summaryCtx, ApOption argAp,
|
||||
Ap ap, ApApprox apa, Configuration config
|
||||
Ap ap, Configuration config
|
||||
) {
|
||||
sourceNode(node, state, config) and
|
||||
(if hasSourceCallCtx(config) then cc = ccSomeCall() else cc = ccNone()) and
|
||||
argAp = apNone() and
|
||||
summaryCtx = TParameterPositionNone() and
|
||||
ap = getApNil(node) and
|
||||
apa = getApprox(ap)
|
||||
ap = getApNil(node)
|
||||
or
|
||||
exists(NodeEx mid, FlowState state0, Ap ap0, ApApprox apa0, LocalCc localCc |
|
||||
fwdFlow(mid, state0, cc, summaryCtx, argAp, ap0, apa0, config) and
|
||||
exists(NodeEx mid, FlowState state0, Ap ap0, LocalCc localCc |
|
||||
fwdFlow(mid, state0, cc, summaryCtx, argAp, ap0, config) and
|
||||
localCc = getLocalCc(mid, cc)
|
||||
|
|
||||
localStep(mid, state0, node, state, true, _, config, localCc) and
|
||||
ap = ap0 and
|
||||
apa = apa0
|
||||
ap = ap0
|
||||
or
|
||||
localStep(mid, state0, node, state, false, ap, config, localCc) and
|
||||
ap0 instanceof ApNil and
|
||||
apa = getApprox(ap)
|
||||
ap0 instanceof ApNil
|
||||
)
|
||||
or
|
||||
exists(NodeEx mid |
|
||||
fwdFlow(mid, pragma[only_bind_into](state), _, _, _, ap, apa, pragma[only_bind_into](config)) and
|
||||
fwdFlow(mid, pragma[only_bind_into](state), _, _, _, ap, pragma[only_bind_into](config)) and
|
||||
jumpStep(mid, node, config) and
|
||||
cc = ccNone() and
|
||||
summaryCtx = TParameterPositionNone() and
|
||||
@@ -1382,8 +1352,7 @@ private module MkStage<StageSig PrevStage> {
|
||||
cc = ccNone() and
|
||||
summaryCtx = TParameterPositionNone() and
|
||||
argAp = apNone() and
|
||||
ap = getApNil(node) and
|
||||
apa = getApprox(ap)
|
||||
ap = getApNil(node)
|
||||
)
|
||||
or
|
||||
exists(NodeEx mid, FlowState state0, ApNil nil |
|
||||
@@ -1392,32 +1361,32 @@ private module MkStage<StageSig PrevStage> {
|
||||
cc = ccNone() and
|
||||
summaryCtx = TParameterPositionNone() and
|
||||
argAp = apNone() and
|
||||
ap = getApNil(node) and
|
||||
apa = getApprox(ap)
|
||||
ap = getApNil(node)
|
||||
)
|
||||
or
|
||||
// store
|
||||
exists(TypedContent tc, Ap ap0 |
|
||||
fwdFlowStore(_, ap0, tc, node, state, cc, summaryCtx, argAp, config) and
|
||||
ap = apCons(tc, ap0) and
|
||||
apa = getApprox(ap)
|
||||
ap = apCons(tc, ap0)
|
||||
)
|
||||
or
|
||||
// read
|
||||
exists(Ap ap0, Content c |
|
||||
fwdFlowRead(ap0, c, _, node, state, cc, summaryCtx, argAp, config) and
|
||||
fwdFlowConsCand(ap0, c, ap, config) and
|
||||
apa = getApprox(ap)
|
||||
fwdFlowConsCand(ap0, c, ap, config)
|
||||
)
|
||||
or
|
||||
// flow into a callable
|
||||
fwdFlowIn(_, node, state, _, cc, _, _, ap, apa, config) and
|
||||
if PrevStage::parameterMayFlowThrough(node, apa, config)
|
||||
then (
|
||||
summaryCtx = TParameterPositionSome(node.(ParamNodeEx).getPosition()) and
|
||||
argAp = apSome(ap)
|
||||
) else (
|
||||
summaryCtx = TParameterPositionNone() and argAp = apNone()
|
||||
exists(ApApprox apa |
|
||||
fwdFlowIn(_, node, state, _, cc, _, _, ap, config) and
|
||||
apa = getApprox(ap) and
|
||||
if PrevStage::parameterMayFlowThrough(node, apa, config)
|
||||
then (
|
||||
summaryCtx = TParameterPositionSome(node.(ParamNodeEx).getPosition()) and
|
||||
argAp = apSome(ap)
|
||||
) else (
|
||||
summaryCtx = TParameterPositionNone() and argAp = apNone()
|
||||
)
|
||||
)
|
||||
or
|
||||
// flow out of a callable
|
||||
@@ -1425,8 +1394,8 @@ private module MkStage<StageSig PrevStage> {
|
||||
DataFlowCall call, RetNodeEx ret, boolean allowsFieldFlow, CcNoCall innercc,
|
||||
DataFlowCallable inner
|
||||
|
|
||||
fwdFlow(ret, state, innercc, summaryCtx, argAp, ap, apa, config) and
|
||||
flowOutOfCallApa(call, ret, _, node, allowsFieldFlow, apa, config) and
|
||||
fwdFlow(ret, state, innercc, summaryCtx, argAp, ap, config) and
|
||||
flowOutOfCall(call, ret, _, node, allowsFieldFlow, config) and
|
||||
inner = ret.getEnclosingCallable() and
|
||||
cc = getCallContextReturn(inner, call, innercc) and
|
||||
if allowsFieldFlow = false then ap instanceof ApNil else any()
|
||||
@@ -1434,7 +1403,7 @@ private module MkStage<StageSig PrevStage> {
|
||||
or
|
||||
// flow through a callable
|
||||
exists(DataFlowCall call, ParameterPosition summaryCtx0, Ap argAp0 |
|
||||
fwdFlowOutFromArg(call, node, state, summaryCtx0, argAp0, ap, apa, config) and
|
||||
fwdFlowOutFromArg(call, node, state, summaryCtx0, argAp0, ap, config) and
|
||||
fwdFlowIsEntered(call, cc, summaryCtx, argAp, summaryCtx0, argAp0, config)
|
||||
)
|
||||
}
|
||||
@@ -1444,9 +1413,9 @@ private module MkStage<StageSig PrevStage> {
|
||||
NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, FlowState state, Cc cc,
|
||||
ParameterPositionOption summaryCtx, ApOption argAp, Configuration config
|
||||
) {
|
||||
exists(DataFlowType contentType, ApApprox apa1 |
|
||||
fwdFlow(node1, state, cc, summaryCtx, argAp, ap1, apa1, config) and
|
||||
PrevStage::storeStepCand(node1, apa1, tc, node2, contentType, config) and
|
||||
exists(DataFlowType contentType |
|
||||
fwdFlow(node1, state, cc, summaryCtx, argAp, ap1, config) and
|
||||
PrevStage::storeStepCand(node1, unbindApa(getApprox(ap1)), tc, node2, contentType, config) and
|
||||
typecheckStore(ap1, contentType)
|
||||
)
|
||||
}
|
||||
@@ -1464,104 +1433,43 @@ private module MkStage<StageSig PrevStage> {
|
||||
)
|
||||
}
|
||||
|
||||
private class ApNonNil instanceof Ap {
|
||||
pragma[nomagic]
|
||||
ApNonNil() { not this instanceof ApNil }
|
||||
|
||||
string toString() { result = "" }
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate fwdFlowRead0(
|
||||
NodeEx node1, FlowState state, Cc cc, ParameterPositionOption summaryCtx, ApOption argAp,
|
||||
ApNonNil ap, Configuration config
|
||||
) {
|
||||
fwdFlow(node1, state, cc, summaryCtx, argAp, ap, config) and
|
||||
PrevStage::readStepCand(node1, _, _, config)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate fwdFlowRead(
|
||||
Ap ap, Content c, NodeEx node1, NodeEx node2, FlowState state, Cc cc,
|
||||
ParameterPositionOption summaryCtx, ApOption argAp, Configuration config
|
||||
) {
|
||||
fwdFlowRead0(node1, state, cc, summaryCtx, argAp, ap, config) and
|
||||
fwdFlow(node1, state, cc, summaryCtx, argAp, ap, config) and
|
||||
PrevStage::readStepCand(node1, c, node2, config) and
|
||||
getHeadContent(ap) = c
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate fwdFlowIn(
|
||||
DataFlowCall call, ParamNodeEx p, FlowState state, Cc outercc, CcCall innercc,
|
||||
ParameterPositionOption summaryCtx, ApOption argAp, Ap ap, ApApprox apa, Configuration config
|
||||
DataFlowCall call, ParamNodeEx p, FlowState state, Cc outercc, Cc innercc,
|
||||
ParameterPositionOption summaryCtx, ApOption argAp, Ap ap, Configuration config
|
||||
) {
|
||||
exists(ArgNodeEx arg, boolean allowsFieldFlow |
|
||||
fwdFlow(arg, state, outercc, summaryCtx, argAp, ap, apa, config) and
|
||||
flowIntoCallApa(call, arg, p, allowsFieldFlow, apa, config) and
|
||||
fwdFlow(arg, state, outercc, summaryCtx, argAp, ap, config) and
|
||||
flowIntoCall(call, arg, p, allowsFieldFlow, config) and
|
||||
innercc = getCallContextCall(call, p.getEnclosingCallable(), outercc) and
|
||||
if allowsFieldFlow = false then ap instanceof ApNil else any()
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate fwdFlowRetFromArg(
|
||||
RetNodeEx ret, FlowState state, CcCall ccc, ParameterPosition summaryCtx, ParamNodeEx p,
|
||||
Ap argAp, ApApprox argApa, Ap ap, ApApprox apa, Configuration config
|
||||
) {
|
||||
exists(DataFlowCallable c, ReturnKindExt kind |
|
||||
fwdFlow(pragma[only_bind_into](ret), state, ccc,
|
||||
TParameterPositionSome(pragma[only_bind_into](summaryCtx)), apSome(argAp), ap, apa, config) and
|
||||
getApprox(argAp) = argApa and
|
||||
c = ret.getEnclosingCallable() and
|
||||
kind = ret.getKind() and
|
||||
p.isParameterOf(c, pragma[only_bind_into](summaryCtx)) and
|
||||
parameterFlowThroughAllowed(p, kind)
|
||||
)
|
||||
}
|
||||
|
||||
pragma[inline]
|
||||
private predicate fwdFlowInMayFlowThrough(
|
||||
DataFlowCall call, Cc cc, CcCall innerCc, ParameterPositionOption summaryCtx, ApOption argAp,
|
||||
ParamNodeEx param, Ap ap, ApApprox apa, Configuration config
|
||||
) {
|
||||
fwdFlowIn(call, pragma[only_bind_into](param), _, cc, innerCc, summaryCtx, argAp, ap,
|
||||
pragma[only_bind_into](apa), pragma[only_bind_into](config)) and
|
||||
PrevStage::parameterMayFlowThrough(param, apa, config)
|
||||
}
|
||||
|
||||
// dedup before joining with `flowThroughOutOfCall`
|
||||
pragma[nomagic]
|
||||
private predicate fwdFlowInMayFlowThroughProj(
|
||||
DataFlowCall call, CcCall innerCc, ApApprox apa, Configuration config
|
||||
) {
|
||||
fwdFlowInMayFlowThrough(call, _, innerCc, _, _, _, _, apa, config)
|
||||
}
|
||||
|
||||
/**
|
||||
* Same as `flowThroughOutOfCall`, but restricted to calls that are reached
|
||||
* in the flow covered by `fwdFlow`, where data might flow through the target
|
||||
* callable and back out at `call`.
|
||||
*/
|
||||
pragma[nomagic]
|
||||
private predicate fwdFlowThroughOutOfCall(
|
||||
DataFlowCall call, CcCall ccc, RetNodeEx ret, NodeEx out, boolean allowsFieldFlow,
|
||||
ApApprox argApa, ApApprox apa, Configuration config
|
||||
) {
|
||||
fwdFlowInMayFlowThroughProj(call, ccc, argApa, config) and
|
||||
flowThroughOutOfCall(call, ccc, ret, out, allowsFieldFlow, argApa, apa, config)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate fwdFlowOutFromArg(
|
||||
DataFlowCall call, NodeEx out, FlowState state, ParameterPosition summaryCtx, Ap argAp, Ap ap,
|
||||
ApApprox apa, Configuration config
|
||||
Configuration config
|
||||
) {
|
||||
exists(RetNodeEx ret, boolean allowsFieldFlow, CcCall ccc, ApApprox argApa |
|
||||
fwdFlowRetFromArg(pragma[only_bind_into](ret), state, pragma[only_bind_into](ccc),
|
||||
summaryCtx, _, argAp, pragma[only_bind_into](argApa), ap, pragma[only_bind_into](apa),
|
||||
exists(
|
||||
DataFlowCallable c, RetNodeEx ret, ReturnKindExt kind, boolean allowsFieldFlow, CcCall ccc
|
||||
|
|
||||
fwdFlow(pragma[only_bind_into](ret), state, pragma[only_bind_into](ccc),
|
||||
TParameterPositionSome(pragma[only_bind_into](summaryCtx)), apSome(argAp), ap, config) and
|
||||
flowThroughOutOfCall(call, pragma[only_bind_into](c), ccc, ret, kind, out, allowsFieldFlow,
|
||||
config) and
|
||||
fwdFlowThroughOutOfCall(call, ccc, ret, out, allowsFieldFlow, argApa, apa, config) and
|
||||
(if allowsFieldFlow = false then ap instanceof ApNil else any())
|
||||
(if allowsFieldFlow = false then ap instanceof ApNil else any()) and
|
||||
parameterFlowThroughAllowed(c, pragma[only_bind_into](summaryCtx), kind)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1575,7 +1483,8 @@ private module MkStage<StageSig PrevStage> {
|
||||
ParameterPosition pos, Ap ap, Configuration config
|
||||
) {
|
||||
exists(ParamNodeEx param |
|
||||
fwdFlowInMayFlowThrough(call, cc, _, summaryCtx, argAp, param, ap, _, config) and
|
||||
fwdFlowIn(call, param, _, cc, _, summaryCtx, argAp, ap, config) and
|
||||
PrevStage::parameterMayFlowThrough(param, unbindApa(getApprox(ap)), config) and
|
||||
pos = param.getPosition()
|
||||
)
|
||||
}
|
||||
@@ -1596,55 +1505,41 @@ private module MkStage<StageSig PrevStage> {
|
||||
fwdFlowConsCand(ap1, c, ap2, config)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate returnFlowsThrough0(
|
||||
RetNodeEx ret, ReturnKindExt kind, FlowState state, CcCall ccc, DataFlowCallable c,
|
||||
ParameterPosition ppos, Ap argAp, Ap ap, Configuration config
|
||||
) {
|
||||
exists(boolean allowsFieldFlow |
|
||||
fwdFlow(ret, state, ccc, TParameterPositionSome(ppos), apSome(argAp), ap, config) and
|
||||
flowThroughOutOfCall(_, c, _, pragma[only_bind_into](ret), kind, _, allowsFieldFlow,
|
||||
pragma[only_bind_into](config)) and
|
||||
if allowsFieldFlow = false then ap instanceof ApNil else any()
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate returnFlowsThrough(
|
||||
RetNodeEx ret, ReturnKindExt kind, FlowState state, CcCall ccc, ParamNodeEx p, Ap argAp,
|
||||
Ap ap, Configuration config
|
||||
) {
|
||||
exists(boolean allowsFieldFlow, ApApprox argApa, ApApprox apa |
|
||||
fwdFlowRetFromArg(pragma[only_bind_into](ret), state, pragma[only_bind_into](ccc), _, p,
|
||||
argAp, pragma[only_bind_into](argApa), ap, pragma[only_bind_into](apa), config) and
|
||||
kind = ret.getKind() and
|
||||
fwdFlowThroughOutOfCall(_, ccc, ret, _, allowsFieldFlow, argApa, apa, config) and
|
||||
(if allowsFieldFlow = false then ap instanceof ApNil else any())
|
||||
exists(DataFlowCallable c, ParameterPosition ppos |
|
||||
returnFlowsThrough0(ret, kind, state, ccc, c, ppos, argAp, ap, config) and
|
||||
p.isParameterOf(c, ppos) and
|
||||
parameterFlowThroughAllowed(p, kind)
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate flowThroughIntoCall(
|
||||
DataFlowCall call, ArgNodeEx arg, ParamNodeEx p, boolean allowsFieldFlow, Ap argAp,
|
||||
Configuration config
|
||||
DataFlowCall call, ArgNodeEx arg, ParamNodeEx p, boolean allowsFieldFlow, Configuration config
|
||||
) {
|
||||
exists(ApApprox argApa |
|
||||
flowIntoCallApa(call, pragma[only_bind_into](arg), pragma[only_bind_into](p),
|
||||
allowsFieldFlow, argApa, pragma[only_bind_into](config)) and
|
||||
fwdFlow(arg, _, _, _, _, pragma[only_bind_into](argAp), argApa,
|
||||
exists(Ap argAp |
|
||||
flowIntoCall(call, pragma[only_bind_into](arg), pragma[only_bind_into](p), allowsFieldFlow,
|
||||
pragma[only_bind_into](config)) and
|
||||
fwdFlow(arg, _, _, _, _, pragma[only_bind_into](argAp), pragma[only_bind_into](config)) and
|
||||
returnFlowsThrough(_, _, _, _, p, pragma[only_bind_into](argAp), _,
|
||||
pragma[only_bind_into](config)) and
|
||||
if allowsFieldFlow = false then argAp instanceof ApNil else any()
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate flowIntoCallAp(
|
||||
DataFlowCall call, ArgNodeEx arg, ParamNodeEx p, boolean allowsFieldFlow, Ap ap,
|
||||
Configuration config
|
||||
) {
|
||||
exists(ApApprox apa |
|
||||
flowIntoCallApa(call, arg, p, allowsFieldFlow, apa, config) and
|
||||
fwdFlow(arg, _, _, _, _, ap, apa, config)
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate flowOutOfCallAp(
|
||||
DataFlowCall call, RetNodeEx ret, ReturnKindExt kind, NodeEx out, boolean allowsFieldFlow,
|
||||
Ap ap, Configuration config
|
||||
) {
|
||||
exists(ApApprox apa |
|
||||
flowOutOfCallApa(call, ret, kind, out, allowsFieldFlow, apa, config) and
|
||||
fwdFlow(ret, _, _, _, _, ap, apa, config)
|
||||
pragma[only_bind_into](config))
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1733,7 +1628,7 @@ private module MkStage<StageSig PrevStage> {
|
||||
// flow into a callable
|
||||
exists(ParamNodeEx p, boolean allowsFieldFlow |
|
||||
revFlow(p, state, TReturnCtxNone(), returnAp, ap, config) and
|
||||
flowIntoCallAp(_, node, p, allowsFieldFlow, ap, config) and
|
||||
flowIntoCall(_, node, p, allowsFieldFlow, config) and
|
||||
(if allowsFieldFlow = false then ap instanceof ApNil else any()) and
|
||||
returnCtx = TReturnCtxNone()
|
||||
)
|
||||
@@ -1787,41 +1682,22 @@ private module MkStage<StageSig PrevStage> {
|
||||
) {
|
||||
exists(NodeEx out, boolean allowsFieldFlow |
|
||||
revFlow(out, state, returnCtx, returnAp, ap, config) and
|
||||
flowOutOfCallAp(call, ret, kind, out, allowsFieldFlow, ap, config) and
|
||||
flowOutOfCall(call, ret, kind, out, allowsFieldFlow, config) and
|
||||
if allowsFieldFlow = false then ap instanceof ApNil else any()
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Same as `flowThroughIntoCall`, but restricted to calls that are reached
|
||||
* in the flow covered by `revFlow`, where data might flow through the target
|
||||
* callable and back out at `call`.
|
||||
*/
|
||||
pragma[nomagic]
|
||||
private predicate revFlowThroughIntoCall(
|
||||
DataFlowCall call, ArgNodeEx arg, ParamNodeEx p, boolean allowsFieldFlow, Ap argAp,
|
||||
Configuration config
|
||||
) {
|
||||
flowThroughIntoCall(call, arg, p, allowsFieldFlow, argAp, config) and
|
||||
revFlowIsReturned(call, _, _, _, _, config)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate revFlowParamToReturn(
|
||||
ParamNodeEx p, FlowState state, ReturnKindExt kind, Ap returnAp, Ap ap, Configuration config
|
||||
) {
|
||||
revFlow(p, state, TReturnCtxMaybeFlowThrough(kind), apSome(returnAp), ap, config) and
|
||||
parameterFlowThroughAllowed(p, kind)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate revFlowInToReturn(
|
||||
DataFlowCall call, ArgNodeEx arg, FlowState state, ReturnKindExt kind, Ap returnAp, Ap ap,
|
||||
Configuration config
|
||||
) {
|
||||
exists(ParamNodeEx p, boolean allowsFieldFlow |
|
||||
revFlowParamToReturn(p, state, kind, returnAp, ap, config) and
|
||||
revFlowThroughIntoCall(call, arg, p, allowsFieldFlow, ap, config)
|
||||
revFlow(pragma[only_bind_into](p), state,
|
||||
TReturnCtxMaybeFlowThrough(pragma[only_bind_into](kind)), apSome(returnAp), ap, config) and
|
||||
flowThroughIntoCall(call, arg, p, allowsFieldFlow, config) and
|
||||
(if allowsFieldFlow = false then ap instanceof ApNil else any()) and
|
||||
parameterFlowThroughAllowed(p, kind)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1874,11 +1750,6 @@ private module MkStage<StageSig PrevStage> {
|
||||
pragma[nomagic]
|
||||
predicate revFlow(NodeEx node, Configuration config) { revFlow(node, _, _, _, _, config) }
|
||||
|
||||
pragma[nomagic]
|
||||
predicate revFlowAp(NodeEx node, Ap ap, Configuration config) {
|
||||
revFlow(node, _, _, _, ap, config)
|
||||
}
|
||||
|
||||
// use an alias as a workaround for bad functionality-induced joins
|
||||
pragma[nomagic]
|
||||
additional predicate revFlowAlias(NodeEx node, Configuration config) {
|
||||
@@ -1915,9 +1786,9 @@ private module MkStage<StageSig PrevStage> {
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate parameterFlowsThroughRev(
|
||||
ParamNodeEx p, Ap ap, ReturnKindExt kind, Ap returnAp, Configuration config
|
||||
ParamNodeEx p, Ap ap, ReturnKindExt kind, Configuration config
|
||||
) {
|
||||
revFlow(p, _, TReturnCtxMaybeFlowThrough(kind), apSome(returnAp), ap, config) and
|
||||
revFlow(p, _, TReturnCtxMaybeFlowThrough(kind), apSome(_), ap, config) and
|
||||
parameterFlowThroughAllowed(p, kind)
|
||||
}
|
||||
|
||||
@@ -1925,36 +1796,27 @@ private module MkStage<StageSig PrevStage> {
|
||||
predicate parameterMayFlowThrough(ParamNodeEx p, Ap ap, Configuration config) {
|
||||
exists(RetNodeEx ret, ReturnKindExt kind |
|
||||
returnFlowsThrough(ret, kind, _, _, p, ap, _, config) and
|
||||
parameterFlowsThroughRev(p, ap, kind, _, config)
|
||||
parameterFlowsThroughRev(p, ap, kind, config)
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
predicate returnMayFlowThrough(
|
||||
RetNodeEx ret, Ap argAp, Ap ap, ReturnKindExt kind, Configuration config
|
||||
) {
|
||||
exists(ParamNodeEx p |
|
||||
returnFlowsThrough(ret, kind, _, _, p, argAp, ap, config) and
|
||||
parameterFlowsThroughRev(p, argAp, kind, ap, config)
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
predicate revFlowInToReturnIsReturned(
|
||||
DataFlowCall call, ArgNodeEx arg, FlowState state, ReturnCtx returnCtx, ApOption returnAp,
|
||||
Ap ap, Configuration config
|
||||
) {
|
||||
exists(ReturnKindExt returnKind0, Ap returnAp0 |
|
||||
revFlowInToReturn(call, arg, state, returnKind0, returnAp0, ap, config) and
|
||||
revFlowIsReturned(call, returnCtx, returnAp, returnKind0, returnAp0, config)
|
||||
predicate returnMayFlowThrough(RetNodeEx ret, ReturnKindExt kind, Configuration config) {
|
||||
exists(ParamNodeEx p, Ap ap |
|
||||
returnFlowsThrough(ret, kind, _, _, p, ap, _, config) and
|
||||
parameterFlowsThroughRev(p, ap, kind, config)
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
predicate callMayFlowThroughRev(DataFlowCall call, Configuration config) {
|
||||
exists(ArgNodeEx arg, FlowState state, ReturnCtx returnCtx, ApOption returnAp, Ap ap |
|
||||
exists(
|
||||
ReturnKindExt returnKind0, Ap returnAp0, ArgNodeEx arg, FlowState state,
|
||||
ReturnCtx returnCtx, ApOption returnAp, Ap ap
|
||||
|
|
||||
revFlow(arg, state, returnCtx, returnAp, ap, config) and
|
||||
revFlowInToReturnIsReturned(call, arg, state, returnCtx, returnAp, ap, config)
|
||||
revFlowInToReturn(call, arg, state, returnKind0, returnAp0, ap, config) and
|
||||
revFlowIsReturned(call, returnCtx, returnAp, returnKind0, returnAp0, config)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -2317,16 +2179,16 @@ private module LocalFlowBigStep {
|
||||
pragma[nomagic]
|
||||
predicate localFlowBigStep(
|
||||
NodeEx node1, FlowState state1, NodeEx node2, FlowState state2, boolean preservesValue,
|
||||
DataFlowType t, Configuration config, LocalCallContext callContext
|
||||
AccessPathFrontNil apf, Configuration config, LocalCallContext callContext
|
||||
) {
|
||||
localFlowStepPlus(node1, state1, node2, preservesValue, t, config, callContext) and
|
||||
localFlowStepPlus(node1, state1, node2, preservesValue, apf.getType(), config, callContext) and
|
||||
localFlowExit(node2, state1, config) and
|
||||
state1 = state2
|
||||
or
|
||||
additionalLocalFlowStepNodeCand2(node1, state1, node2, state2, config) and
|
||||
state1 != state2 and
|
||||
preservesValue = false and
|
||||
t = node2.getDataFlowType() and
|
||||
apf = TFrontNil(node2.getDataFlowType()) and
|
||||
callContext.relevantFor(node1.getEnclosingCallable()) and
|
||||
not exists(DataFlowCall call | call = callContext.(LocalCallContextSpecificCall).getCall() |
|
||||
isUnreachableInCallCached(node1.asNode(), call) or
|
||||
@@ -2340,87 +2202,11 @@ private import LocalFlowBigStep
|
||||
private module Stage3Param implements MkStage<Stage2>::StageParam {
|
||||
private module PrevStage = Stage2;
|
||||
|
||||
class Ap = ApproxAccessPathFront;
|
||||
|
||||
class ApNil = ApproxAccessPathFrontNil;
|
||||
|
||||
PrevStage::Ap getApprox(Ap ap) { result = ap.toBoolNonEmpty() }
|
||||
|
||||
ApNil getApNil(NodeEx node) {
|
||||
PrevStage::revFlow(node, _) and result = TApproxFrontNil(node.getDataFlowType())
|
||||
}
|
||||
|
||||
bindingset[tc, tail]
|
||||
Ap apCons(TypedContent tc, Ap tail) { result.getAHead() = tc and exists(tail) }
|
||||
|
||||
pragma[noinline]
|
||||
Content getHeadContent(Ap ap) { result = ap.getAHead().getContent() }
|
||||
|
||||
class ApOption = ApproxAccessPathFrontOption;
|
||||
|
||||
ApOption apNone() { result = TApproxAccessPathFrontNone() }
|
||||
|
||||
ApOption apSome(Ap ap) { result = TApproxAccessPathFrontSome(ap) }
|
||||
|
||||
import BooleanCallContext
|
||||
|
||||
predicate localStep(
|
||||
NodeEx node1, FlowState state1, NodeEx node2, FlowState state2, boolean preservesValue,
|
||||
ApproxAccessPathFrontNil ap, Configuration config, LocalCc lcc
|
||||
) {
|
||||
localFlowBigStep(node1, state1, node2, state2, preservesValue, ap.getType(), config, _) and
|
||||
exists(lcc)
|
||||
}
|
||||
|
||||
predicate flowOutOfCall = flowOutOfCallNodeCand2/6;
|
||||
|
||||
predicate flowIntoCall = flowIntoCallNodeCand2/5;
|
||||
|
||||
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.getAHead().getContent()
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate castingNodeEx(NodeEx node) { node.asNode() instanceof CastingNode }
|
||||
|
||||
bindingset[node, state, ap, config]
|
||||
predicate filter(NodeEx node, FlowState state, Ap ap, Configuration config) {
|
||||
exists(state) and
|
||||
exists(config) and
|
||||
(if castingNodeEx(node) then compatibleTypes(node.getDataFlowType(), ap.getType()) else any()) and
|
||||
(
|
||||
notExpectsContent(node)
|
||||
or
|
||||
expectsContentCand(node, ap, config)
|
||||
)
|
||||
}
|
||||
|
||||
bindingset[ap, contentType]
|
||||
predicate typecheckStore(Ap ap, DataFlowType contentType) {
|
||||
// We need to typecheck stores here, since reverse flow through a getter
|
||||
// might have a different type here compared to inside the getter.
|
||||
compatibleTypes(ap.getType(), contentType)
|
||||
}
|
||||
}
|
||||
|
||||
private module Stage3 implements StageSig {
|
||||
import MkStage<Stage2>::Stage<Stage3Param>
|
||||
}
|
||||
|
||||
private module Stage4Param implements MkStage<Stage3>::StageParam {
|
||||
private module PrevStage = Stage3;
|
||||
|
||||
class Ap = AccessPathFront;
|
||||
|
||||
class ApNil = AccessPathFrontNil;
|
||||
|
||||
PrevStage::Ap getApprox(Ap ap) { result = ap.toApprox() }
|
||||
PrevStage::Ap getApprox(Ap ap) { result = ap.toBoolNonEmpty() }
|
||||
|
||||
ApNil getApNil(NodeEx node) {
|
||||
PrevStage::revFlow(node, _) and result = TFrontNil(node.getDataFlowType())
|
||||
@@ -2440,42 +2226,16 @@ private module Stage4Param implements MkStage<Stage3>::StageParam {
|
||||
|
||||
import BooleanCallContext
|
||||
|
||||
pragma[nomagic]
|
||||
predicate localStep(
|
||||
NodeEx node1, FlowState state1, NodeEx node2, FlowState state2, boolean preservesValue,
|
||||
ApNil ap, Configuration config, LocalCc lcc
|
||||
) {
|
||||
localFlowBigStep(node1, state1, node2, state2, preservesValue, ap.getType(), config, _) and
|
||||
PrevStage::revFlow(node1, pragma[only_bind_into](state1), _, pragma[only_bind_into](config)) and
|
||||
PrevStage::revFlowAlias(node2, pragma[only_bind_into](state2), _, pragma[only_bind_into](config)) and
|
||||
exists(lcc)
|
||||
localFlowBigStep(node1, state1, node2, state2, preservesValue, ap, config, _) and exists(lcc)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
predicate flowOutOfCall(
|
||||
DataFlowCall call, RetNodeEx node1, ReturnKindExt kind, NodeEx node2, boolean allowsFieldFlow,
|
||||
Configuration config
|
||||
) {
|
||||
exists(FlowState state |
|
||||
flowOutOfCallNodeCand2(call, node1, kind, node2, allowsFieldFlow, config) and
|
||||
PrevStage::revFlow(node2, pragma[only_bind_into](state), _, pragma[only_bind_into](config)) and
|
||||
PrevStage::revFlowAlias(node1, pragma[only_bind_into](state), _,
|
||||
pragma[only_bind_into](config))
|
||||
)
|
||||
}
|
||||
predicate flowOutOfCall = flowOutOfCallNodeCand2/6;
|
||||
|
||||
pragma[nomagic]
|
||||
predicate flowIntoCall(
|
||||
DataFlowCall call, ArgNodeEx node1, ParamNodeEx node2, boolean allowsFieldFlow,
|
||||
Configuration config
|
||||
) {
|
||||
exists(FlowState state |
|
||||
flowIntoCallNodeCand2(call, node1, node2, allowsFieldFlow, config) and
|
||||
PrevStage::revFlow(node2, pragma[only_bind_into](state), _, pragma[only_bind_into](config)) and
|
||||
PrevStage::revFlowAlias(node1, pragma[only_bind_into](state), _,
|
||||
pragma[only_bind_into](config))
|
||||
)
|
||||
}
|
||||
predicate flowIntoCall = flowIntoCallNodeCand2/5;
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate clearSet(NodeEx node, ContentSet c, Configuration config) {
|
||||
@@ -2531,8 +2291,8 @@ private module Stage4Param implements MkStage<Stage3>::StageParam {
|
||||
}
|
||||
}
|
||||
|
||||
private module Stage4 implements StageSig {
|
||||
import MkStage<Stage3>::Stage<Stage4Param>
|
||||
private module Stage3 implements StageSig {
|
||||
import MkStage<Stage2>::Stage<Stage3Param>
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -2543,8 +2303,8 @@ private predicate flowCandSummaryCtx(
|
||||
NodeEx node, FlowState state, AccessPathFront argApf, Configuration config
|
||||
) {
|
||||
exists(AccessPathFront apf |
|
||||
Stage4::revFlow(node, state, TReturnCtxMaybeFlowThrough(_), _, apf, config) and
|
||||
Stage4::fwdFlow(node, state, any(Stage4::CcCall ccc), _, TAccessPathFrontSome(argApf), apf,
|
||||
Stage3::revFlow(node, state, TReturnCtxMaybeFlowThrough(_), _, apf, config) and
|
||||
Stage3::fwdFlow(node, state, any(Stage3::CcCall ccc), _, TAccessPathFrontSome(argApf), apf,
|
||||
config)
|
||||
)
|
||||
}
|
||||
@@ -2555,10 +2315,10 @@ private predicate flowCandSummaryCtx(
|
||||
*/
|
||||
private predicate expensiveLen2unfolding(TypedContent tc, Configuration config) {
|
||||
exists(int tails, int nodes, int apLimit, int tupleLimit |
|
||||
tails = strictcount(AccessPathFront apf | Stage4::consCand(tc, apf, config)) and
|
||||
tails = strictcount(AccessPathFront apf | Stage3::consCand(tc, apf, config)) and
|
||||
nodes =
|
||||
strictcount(NodeEx n, FlowState state |
|
||||
Stage4::revFlow(n, state, any(AccessPathFrontHead apf | apf.getHead() = tc), config)
|
||||
Stage3::revFlow(n, state, any(AccessPathFrontHead apf | apf.getHead() = tc), config)
|
||||
or
|
||||
flowCandSummaryCtx(n, state, any(AccessPathFrontHead apf | apf.getHead() = tc), config)
|
||||
) and
|
||||
@@ -2572,11 +2332,11 @@ private predicate expensiveLen2unfolding(TypedContent tc, Configuration config)
|
||||
private newtype TAccessPathApprox =
|
||||
TNil(DataFlowType t) or
|
||||
TConsNil(TypedContent tc, DataFlowType t) {
|
||||
Stage4::consCand(tc, TFrontNil(t), _) and
|
||||
Stage3::consCand(tc, TFrontNil(t), _) and
|
||||
not expensiveLen2unfolding(tc, _)
|
||||
} or
|
||||
TConsCons(TypedContent tc1, TypedContent tc2, int len) {
|
||||
Stage4::consCand(tc1, TFrontHead(tc2), _) and
|
||||
Stage3::consCand(tc1, TFrontHead(tc2), _) and
|
||||
len in [2 .. accessPathLimit()] and
|
||||
not expensiveLen2unfolding(tc1, _)
|
||||
} or
|
||||
@@ -2706,7 +2466,7 @@ private class AccessPathApproxCons1 extends AccessPathApproxCons, TCons1 {
|
||||
override AccessPathApprox pop(TypedContent head) {
|
||||
head = tc and
|
||||
(
|
||||
exists(TypedContent tc2 | Stage4::consCand(tc, TFrontHead(tc2), _) |
|
||||
exists(TypedContent tc2 | Stage3::consCand(tc, TFrontHead(tc2), _) |
|
||||
result = TConsCons(tc2, _, len - 1)
|
||||
or
|
||||
len = 2 and
|
||||
@@ -2717,7 +2477,7 @@ private class AccessPathApproxCons1 extends AccessPathApproxCons, TCons1 {
|
||||
or
|
||||
exists(DataFlowType t |
|
||||
len = 1 and
|
||||
Stage4::consCand(tc, TFrontNil(t), _) and
|
||||
Stage3::consCand(tc, TFrontNil(t), _) and
|
||||
result = TNil(t)
|
||||
)
|
||||
)
|
||||
@@ -2742,14 +2502,13 @@ private class AccessPathApproxOption extends TAccessPathApproxOption {
|
||||
}
|
||||
}
|
||||
|
||||
private module Stage5Param implements MkStage<Stage4>::StageParam {
|
||||
private module PrevStage = Stage4;
|
||||
private module Stage4Param implements MkStage<Stage3>::StageParam {
|
||||
private module PrevStage = Stage3;
|
||||
|
||||
class Ap = AccessPathApprox;
|
||||
|
||||
class ApNil = AccessPathApproxNil;
|
||||
|
||||
pragma[nomagic]
|
||||
PrevStage::Ap getApprox(Ap ap) { result = ap.getFront() }
|
||||
|
||||
ApNil getApNil(NodeEx node) {
|
||||
@@ -2775,9 +2534,7 @@ private module Stage5Param implements MkStage<Stage4>::StageParam {
|
||||
NodeEx node1, FlowState state1, NodeEx node2, FlowState state2, boolean preservesValue,
|
||||
ApNil ap, Configuration config, LocalCc lcc
|
||||
) {
|
||||
localFlowBigStep(node1, state1, node2, state2, preservesValue, ap.getType(), config, lcc) and
|
||||
PrevStage::revFlow(node1, pragma[only_bind_into](state1), _, pragma[only_bind_into](config)) and
|
||||
PrevStage::revFlowAlias(node2, pragma[only_bind_into](state2), _, pragma[only_bind_into](config))
|
||||
localFlowBigStep(node1, state1, node2, state2, preservesValue, ap.getFront(), config, lcc)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
@@ -2814,7 +2571,7 @@ private module Stage5Param implements MkStage<Stage4>::StageParam {
|
||||
predicate typecheckStore(Ap ap, DataFlowType contentType) { any() }
|
||||
}
|
||||
|
||||
private module Stage5 = MkStage<Stage4>::Stage<Stage5Param>;
|
||||
private module Stage4 = MkStage<Stage3>::Stage<Stage4Param>;
|
||||
|
||||
bindingset[conf, result]
|
||||
private Configuration unbindConf(Configuration conf) {
|
||||
@@ -2828,8 +2585,8 @@ private predicate nodeMayUseSummary0(
|
||||
) {
|
||||
exists(AccessPathApprox apa0 |
|
||||
c = n.getEnclosingCallable() and
|
||||
Stage5::revFlow(n, state, TReturnCtxMaybeFlowThrough(_), _, apa0, config) and
|
||||
Stage5::fwdFlow(n, state, any(CallContextCall ccc), TParameterPositionSome(pos),
|
||||
Stage4::revFlow(n, state, TReturnCtxMaybeFlowThrough(_), _, apa0, config) and
|
||||
Stage4::fwdFlow(n, state, any(CallContextCall ccc), TParameterPositionSome(pos),
|
||||
TAccessPathApproxSome(apa), apa0, config)
|
||||
)
|
||||
}
|
||||
@@ -2839,7 +2596,7 @@ private predicate nodeMayUseSummary(
|
||||
NodeEx n, FlowState state, AccessPathApprox apa, Configuration config
|
||||
) {
|
||||
exists(DataFlowCallable c, ParameterPosition pos, ParamNodeEx p |
|
||||
Stage5::parameterMayFlowThrough(p, apa, config) and
|
||||
Stage4::parameterMayFlowThrough(p, apa, config) and
|
||||
nodeMayUseSummary0(n, c, pos, state, apa, config) and
|
||||
p.isParameterOf(c, pos)
|
||||
)
|
||||
@@ -2849,8 +2606,8 @@ private newtype TSummaryCtx =
|
||||
TSummaryCtxNone() or
|
||||
TSummaryCtxSome(ParamNodeEx p, FlowState state, AccessPath ap) {
|
||||
exists(Configuration config |
|
||||
Stage5::parameterMayFlowThrough(p, ap.getApprox(), config) and
|
||||
Stage5::revFlow(p, state, _, config)
|
||||
Stage4::parameterMayFlowThrough(p, ap.getApprox(), config) and
|
||||
Stage4::revFlow(p, state, _, config)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -2899,7 +2656,7 @@ private int count1to2unfold(AccessPathApproxCons1 apa, Configuration config) {
|
||||
len = apa.len() and
|
||||
result =
|
||||
strictcount(AccessPathFront apf |
|
||||
Stage5::consCand(tc, any(AccessPathApprox ap | ap.getFront() = apf and ap.len() = len - 1),
|
||||
Stage4::consCand(tc, any(AccessPathApprox ap | ap.getFront() = apf and ap.len() = len - 1),
|
||||
config)
|
||||
)
|
||||
)
|
||||
@@ -2908,7 +2665,7 @@ private int count1to2unfold(AccessPathApproxCons1 apa, Configuration config) {
|
||||
private int countNodesUsingAccessPath(AccessPathApprox apa, Configuration config) {
|
||||
result =
|
||||
strictcount(NodeEx n, FlowState state |
|
||||
Stage5::revFlow(n, state, apa, config) or nodeMayUseSummary(n, state, apa, config)
|
||||
Stage4::revFlow(n, state, apa, config) or nodeMayUseSummary(n, state, apa, config)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -2929,7 +2686,7 @@ private predicate expensiveLen1to2unfolding(AccessPathApproxCons1 apa, Configura
|
||||
private AccessPathApprox getATail(AccessPathApprox apa, Configuration config) {
|
||||
exists(TypedContent head |
|
||||
apa.pop(head) = result and
|
||||
Stage5::consCand(head, result, config)
|
||||
Stage4::consCand(head, result, config)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -3011,7 +2768,7 @@ private newtype TPathNode =
|
||||
NodeEx node, FlowState state, CallContext cc, SummaryCtx sc, AccessPath ap, Configuration config
|
||||
) {
|
||||
// A PathNode is introduced by a source ...
|
||||
Stage5::revFlow(node, state, config) and
|
||||
Stage4::revFlow(node, state, config) and
|
||||
sourceNode(node, state, config) and
|
||||
(
|
||||
if hasSourceCallCtx(config)
|
||||
@@ -3025,7 +2782,7 @@ private newtype TPathNode =
|
||||
exists(PathNodeMid mid |
|
||||
pathStep(mid, node, state, cc, sc, ap) and
|
||||
pragma[only_bind_into](config) = mid.getConfiguration() and
|
||||
Stage5::revFlow(node, state, ap.getApprox(), pragma[only_bind_into](config))
|
||||
Stage4::revFlow(node, state, ap.getApprox(), pragma[only_bind_into](config))
|
||||
)
|
||||
} or
|
||||
TPathNodeSink(NodeEx node, FlowState state, Configuration config) {
|
||||
@@ -3167,7 +2924,7 @@ private class AccessPathCons2 extends AccessPath, TAccessPathCons2 {
|
||||
override TypedContent getHead() { result = head1 }
|
||||
|
||||
override AccessPath getTail() {
|
||||
Stage5::consCand(head1, result.getApprox(), _) and
|
||||
Stage4::consCand(head1, result.getApprox(), _) and
|
||||
result.getHead() = head2 and
|
||||
result.length() = len - 1
|
||||
}
|
||||
@@ -3198,7 +2955,7 @@ private class AccessPathCons1 extends AccessPath, TAccessPathCons1 {
|
||||
override TypedContent getHead() { result = head }
|
||||
|
||||
override AccessPath getTail() {
|
||||
Stage5::consCand(head, result.getApprox(), _) and result.length() = len - 1
|
||||
Stage4::consCand(head, result.getApprox(), _) and result.length() = len - 1
|
||||
}
|
||||
|
||||
override AccessPathFrontHead getFront() { result = TFrontHead(head) }
|
||||
@@ -3590,8 +3347,7 @@ private predicate pathStep(
|
||||
AccessPath ap0, NodeEx midnode, FlowState state0, Configuration conf, LocalCallContext localCC
|
||||
|
|
||||
pathNode(mid, midnode, state0, cc, sc, ap0, conf, localCC) and
|
||||
localFlowBigStep(midnode, state0, node, state, false, ap.(AccessPathNil).getType(), conf,
|
||||
localCC) and
|
||||
localFlowBigStep(midnode, state0, node, state, false, ap.getFront(), conf, localCC) and
|
||||
ap0 instanceof AccessPathNil
|
||||
)
|
||||
or
|
||||
@@ -3633,7 +3389,7 @@ private predicate pathReadStep(
|
||||
) {
|
||||
ap0 = mid.getAp() and
|
||||
tc = ap0.getHead() and
|
||||
Stage5::readStepCand(mid.getNodeEx(), tc.getContent(), node, mid.getConfiguration()) and
|
||||
Stage4::readStepCand(mid.getNodeEx(), tc.getContent(), node, mid.getConfiguration()) and
|
||||
state = mid.getState() and
|
||||
cc = mid.getCallContext()
|
||||
}
|
||||
@@ -3643,7 +3399,7 @@ private predicate pathStoreStep(
|
||||
PathNodeMid mid, NodeEx node, FlowState state, AccessPath ap0, TypedContent tc, CallContext cc
|
||||
) {
|
||||
ap0 = mid.getAp() and
|
||||
Stage5::storeStepCand(mid.getNodeEx(), _, tc, node, _, mid.getConfiguration()) and
|
||||
Stage4::storeStepCand(mid.getNodeEx(), _, tc, node, _, mid.getConfiguration()) and
|
||||
state = mid.getState() and
|
||||
cc = mid.getCallContext()
|
||||
}
|
||||
@@ -3680,7 +3436,7 @@ private NodeEx getAnOutNodeFlow(
|
||||
ReturnKindExt kind, DataFlowCall call, AccessPathApprox apa, Configuration config
|
||||
) {
|
||||
result.asNode() = kind.getAnOutNode(call) and
|
||||
Stage5::revFlow(result, _, apa, config)
|
||||
Stage4::revFlow(result, _, apa, config)
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -3716,7 +3472,7 @@ private predicate parameterCand(
|
||||
DataFlowCallable callable, ParameterPosition pos, AccessPathApprox apa, Configuration config
|
||||
) {
|
||||
exists(ParamNodeEx p |
|
||||
Stage5::revFlow(p, _, apa, config) and
|
||||
Stage4::revFlow(p, _, apa, config) and
|
||||
p.isParameterOf(callable, pos)
|
||||
)
|
||||
}
|
||||
@@ -3995,17 +3751,9 @@ predicate stageStats(
|
||||
n = 45 and
|
||||
Stage4::stats(false, nodes, fields, conscand, states, tuples, config)
|
||||
or
|
||||
stage = "5 Fwd" and
|
||||
n = 50 and
|
||||
Stage5::stats(true, nodes, fields, conscand, states, tuples, config)
|
||||
stage = "5 Fwd" and n = 50 and finalStats(true, nodes, fields, conscand, states, tuples)
|
||||
or
|
||||
stage = "5 Rev" and
|
||||
n = 55 and
|
||||
Stage5::stats(false, nodes, fields, conscand, states, tuples, config)
|
||||
or
|
||||
stage = "6 Fwd" and n = 60 and finalStats(true, nodes, fields, conscand, states, tuples)
|
||||
or
|
||||
stage = "6 Rev" and n = 65 and finalStats(false, nodes, fields, conscand, states, tuples)
|
||||
stage = "5 Rev" and n = 55 and finalStats(false, nodes, fields, conscand, states, tuples)
|
||||
}
|
||||
|
||||
private module FlowExploration {
|
||||
|
||||
@@ -621,6 +621,23 @@ private predicate parameterFlowThroughAllowed(ParamNodeEx p, ReturnKindExt kind)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if flow from a parameter at position `pos` inside `c` to a return node of
|
||||
* kind `kind` is allowed.
|
||||
*
|
||||
* We don't expect a parameter to return stored in itself, unless
|
||||
* explicitly allowed
|
||||
*/
|
||||
bindingset[c, pos, kind]
|
||||
private predicate parameterFlowThroughAllowed(
|
||||
DataFlowCallable c, ParameterPosition pos, ReturnKindExt kind
|
||||
) {
|
||||
exists(ParamNodeEx p |
|
||||
p.isParameterOf(c, pos) and
|
||||
parameterFlowThroughAllowed(p, kind)
|
||||
)
|
||||
}
|
||||
|
||||
private module Stage1 implements StageSig {
|
||||
class Ap = Unit;
|
||||
|
||||
@@ -963,12 +980,6 @@ private module Stage1 implements StageSig {
|
||||
pragma[nomagic]
|
||||
predicate revFlow(NodeEx node, Configuration config) { revFlow(node, _, config) }
|
||||
|
||||
pragma[nomagic]
|
||||
predicate revFlowAp(NodeEx node, Ap ap, Configuration config) {
|
||||
revFlow(node, config) and
|
||||
exists(ap)
|
||||
}
|
||||
|
||||
bindingset[node, state, config]
|
||||
predicate revFlow(NodeEx node, FlowState state, Ap ap, Configuration config) {
|
||||
revFlow(node, _, pragma[only_bind_into](config)) and
|
||||
@@ -1011,13 +1022,9 @@ private module Stage1 implements StageSig {
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
predicate returnMayFlowThrough(
|
||||
RetNodeEx ret, Ap argAp, Ap ap, ReturnKindExt kind, Configuration config
|
||||
) {
|
||||
predicate returnMayFlowThrough(RetNodeEx ret, ReturnKindExt kind, Configuration config) {
|
||||
throughFlowNodeCand(ret, config) and
|
||||
kind = ret.getKind() and
|
||||
exists(argAp) and
|
||||
exists(ap)
|
||||
kind = ret.getKind()
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
@@ -1182,8 +1189,6 @@ private signature module StageSig {
|
||||
|
||||
predicate revFlow(NodeEx node, Configuration config);
|
||||
|
||||
predicate revFlowAp(NodeEx node, Ap ap, Configuration config);
|
||||
|
||||
bindingset[node, state, config]
|
||||
predicate revFlow(NodeEx node, FlowState state, Ap ap, Configuration config);
|
||||
|
||||
@@ -1191,9 +1196,7 @@ private signature module StageSig {
|
||||
|
||||
predicate parameterMayFlowThrough(ParamNodeEx p, Ap ap, Configuration config);
|
||||
|
||||
predicate returnMayFlowThrough(
|
||||
RetNodeEx ret, Ap argAp, Ap ap, ReturnKindExt kind, Configuration config
|
||||
);
|
||||
predicate returnMayFlowThrough(RetNodeEx ret, ReturnKindExt kind, Configuration config);
|
||||
|
||||
predicate storeStepCand(
|
||||
NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, DataFlowType contentType,
|
||||
@@ -1278,43 +1281,21 @@ private module MkStage<StageSig PrevStage> {
|
||||
import Param
|
||||
|
||||
/* Begin: Stage logic. */
|
||||
// use an alias as a workaround for bad functionality-induced joins
|
||||
pragma[nomagic]
|
||||
private predicate revFlowApAlias(NodeEx node, ApApprox apa, Configuration config) {
|
||||
PrevStage::revFlowAp(node, apa, config)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate flowIntoCallApa(
|
||||
DataFlowCall call, ArgNodeEx arg, ParamNodeEx p, boolean allowsFieldFlow, ApApprox apa,
|
||||
Configuration config
|
||||
) {
|
||||
flowIntoCall(call, arg, p, allowsFieldFlow, config) and
|
||||
PrevStage::revFlowAp(p, pragma[only_bind_into](apa), pragma[only_bind_into](config)) and
|
||||
revFlowApAlias(arg, pragma[only_bind_into](apa), pragma[only_bind_into](config))
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate flowOutOfCallApa(
|
||||
DataFlowCall call, RetNodeEx ret, ReturnKindExt kind, NodeEx out, boolean allowsFieldFlow,
|
||||
ApApprox apa, Configuration config
|
||||
) {
|
||||
flowOutOfCall(call, ret, kind, out, allowsFieldFlow, config) and
|
||||
PrevStage::revFlowAp(out, pragma[only_bind_into](apa), pragma[only_bind_into](config)) and
|
||||
revFlowApAlias(ret, pragma[only_bind_into](apa), pragma[only_bind_into](config))
|
||||
bindingset[result, apa]
|
||||
private ApApprox unbindApa(ApApprox apa) {
|
||||
pragma[only_bind_out](apa) = pragma[only_bind_out](result)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate flowThroughOutOfCall(
|
||||
DataFlowCall call, CcCall ccc, RetNodeEx ret, NodeEx out, boolean allowsFieldFlow,
|
||||
ApApprox argApa, ApApprox apa, Configuration config
|
||||
DataFlowCall call, DataFlowCallable c, CcCall ccc, RetNodeEx ret, ReturnKindExt kind,
|
||||
NodeEx out, boolean allowsFieldFlow, Configuration config
|
||||
) {
|
||||
exists(ReturnKindExt kind |
|
||||
flowOutOfCallApa(call, ret, kind, out, allowsFieldFlow, apa, pragma[only_bind_into](config)) and
|
||||
PrevStage::callMayFlowThroughRev(call, pragma[only_bind_into](config)) and
|
||||
PrevStage::returnMayFlowThrough(ret, argApa, apa, kind, pragma[only_bind_into](config)) and
|
||||
matchesCall(ccc, call)
|
||||
)
|
||||
flowOutOfCall(call, ret, kind, out, allowsFieldFlow, pragma[only_bind_into](config)) and
|
||||
PrevStage::callMayFlowThroughRev(call, pragma[only_bind_into](config)) and
|
||||
PrevStage::returnMayFlowThrough(ret, kind, pragma[only_bind_into](config)) and
|
||||
matchesCall(ccc, call) and
|
||||
c = ret.getEnclosingCallable()
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1326,50 +1307,39 @@ private module MkStage<StageSig PrevStage> {
|
||||
* corresponding parameter position and access path of that argument, respectively.
|
||||
*/
|
||||
pragma[nomagic]
|
||||
additional predicate fwdFlow(
|
||||
NodeEx node, FlowState state, Cc cc, ParameterPositionOption summaryCtx, ApOption argAp,
|
||||
Ap ap, ApApprox apa, Configuration config
|
||||
) {
|
||||
fwdFlow0(node, state, cc, summaryCtx, argAp, ap, apa, config) and
|
||||
PrevStage::revFlow(node, state, apa, config) and
|
||||
filter(node, state, ap, config)
|
||||
}
|
||||
|
||||
pragma[inline]
|
||||
additional predicate fwdFlow(
|
||||
NodeEx node, FlowState state, Cc cc, ParameterPositionOption summaryCtx, ApOption argAp,
|
||||
Ap ap, Configuration config
|
||||
) {
|
||||
fwdFlow(node, state, cc, summaryCtx, argAp, ap, _, config)
|
||||
fwdFlow0(node, state, cc, summaryCtx, argAp, ap, config) and
|
||||
PrevStage::revFlow(node, state, unbindApa(getApprox(ap)), config) and
|
||||
filter(node, state, ap, config)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate fwdFlow0(
|
||||
NodeEx node, FlowState state, Cc cc, ParameterPositionOption summaryCtx, ApOption argAp,
|
||||
Ap ap, ApApprox apa, Configuration config
|
||||
Ap ap, Configuration config
|
||||
) {
|
||||
sourceNode(node, state, config) and
|
||||
(if hasSourceCallCtx(config) then cc = ccSomeCall() else cc = ccNone()) and
|
||||
argAp = apNone() and
|
||||
summaryCtx = TParameterPositionNone() and
|
||||
ap = getApNil(node) and
|
||||
apa = getApprox(ap)
|
||||
ap = getApNil(node)
|
||||
or
|
||||
exists(NodeEx mid, FlowState state0, Ap ap0, ApApprox apa0, LocalCc localCc |
|
||||
fwdFlow(mid, state0, cc, summaryCtx, argAp, ap0, apa0, config) and
|
||||
exists(NodeEx mid, FlowState state0, Ap ap0, LocalCc localCc |
|
||||
fwdFlow(mid, state0, cc, summaryCtx, argAp, ap0, config) and
|
||||
localCc = getLocalCc(mid, cc)
|
||||
|
|
||||
localStep(mid, state0, node, state, true, _, config, localCc) and
|
||||
ap = ap0 and
|
||||
apa = apa0
|
||||
ap = ap0
|
||||
or
|
||||
localStep(mid, state0, node, state, false, ap, config, localCc) and
|
||||
ap0 instanceof ApNil and
|
||||
apa = getApprox(ap)
|
||||
ap0 instanceof ApNil
|
||||
)
|
||||
or
|
||||
exists(NodeEx mid |
|
||||
fwdFlow(mid, pragma[only_bind_into](state), _, _, _, ap, apa, pragma[only_bind_into](config)) and
|
||||
fwdFlow(mid, pragma[only_bind_into](state), _, _, _, ap, pragma[only_bind_into](config)) and
|
||||
jumpStep(mid, node, config) and
|
||||
cc = ccNone() and
|
||||
summaryCtx = TParameterPositionNone() and
|
||||
@@ -1382,8 +1352,7 @@ private module MkStage<StageSig PrevStage> {
|
||||
cc = ccNone() and
|
||||
summaryCtx = TParameterPositionNone() and
|
||||
argAp = apNone() and
|
||||
ap = getApNil(node) and
|
||||
apa = getApprox(ap)
|
||||
ap = getApNil(node)
|
||||
)
|
||||
or
|
||||
exists(NodeEx mid, FlowState state0, ApNil nil |
|
||||
@@ -1392,32 +1361,32 @@ private module MkStage<StageSig PrevStage> {
|
||||
cc = ccNone() and
|
||||
summaryCtx = TParameterPositionNone() and
|
||||
argAp = apNone() and
|
||||
ap = getApNil(node) and
|
||||
apa = getApprox(ap)
|
||||
ap = getApNil(node)
|
||||
)
|
||||
or
|
||||
// store
|
||||
exists(TypedContent tc, Ap ap0 |
|
||||
fwdFlowStore(_, ap0, tc, node, state, cc, summaryCtx, argAp, config) and
|
||||
ap = apCons(tc, ap0) and
|
||||
apa = getApprox(ap)
|
||||
ap = apCons(tc, ap0)
|
||||
)
|
||||
or
|
||||
// read
|
||||
exists(Ap ap0, Content c |
|
||||
fwdFlowRead(ap0, c, _, node, state, cc, summaryCtx, argAp, config) and
|
||||
fwdFlowConsCand(ap0, c, ap, config) and
|
||||
apa = getApprox(ap)
|
||||
fwdFlowConsCand(ap0, c, ap, config)
|
||||
)
|
||||
or
|
||||
// flow into a callable
|
||||
fwdFlowIn(_, node, state, _, cc, _, _, ap, apa, config) and
|
||||
if PrevStage::parameterMayFlowThrough(node, apa, config)
|
||||
then (
|
||||
summaryCtx = TParameterPositionSome(node.(ParamNodeEx).getPosition()) and
|
||||
argAp = apSome(ap)
|
||||
) else (
|
||||
summaryCtx = TParameterPositionNone() and argAp = apNone()
|
||||
exists(ApApprox apa |
|
||||
fwdFlowIn(_, node, state, _, cc, _, _, ap, config) and
|
||||
apa = getApprox(ap) and
|
||||
if PrevStage::parameterMayFlowThrough(node, apa, config)
|
||||
then (
|
||||
summaryCtx = TParameterPositionSome(node.(ParamNodeEx).getPosition()) and
|
||||
argAp = apSome(ap)
|
||||
) else (
|
||||
summaryCtx = TParameterPositionNone() and argAp = apNone()
|
||||
)
|
||||
)
|
||||
or
|
||||
// flow out of a callable
|
||||
@@ -1425,8 +1394,8 @@ private module MkStage<StageSig PrevStage> {
|
||||
DataFlowCall call, RetNodeEx ret, boolean allowsFieldFlow, CcNoCall innercc,
|
||||
DataFlowCallable inner
|
||||
|
|
||||
fwdFlow(ret, state, innercc, summaryCtx, argAp, ap, apa, config) and
|
||||
flowOutOfCallApa(call, ret, _, node, allowsFieldFlow, apa, config) and
|
||||
fwdFlow(ret, state, innercc, summaryCtx, argAp, ap, config) and
|
||||
flowOutOfCall(call, ret, _, node, allowsFieldFlow, config) and
|
||||
inner = ret.getEnclosingCallable() and
|
||||
cc = getCallContextReturn(inner, call, innercc) and
|
||||
if allowsFieldFlow = false then ap instanceof ApNil else any()
|
||||
@@ -1434,7 +1403,7 @@ private module MkStage<StageSig PrevStage> {
|
||||
or
|
||||
// flow through a callable
|
||||
exists(DataFlowCall call, ParameterPosition summaryCtx0, Ap argAp0 |
|
||||
fwdFlowOutFromArg(call, node, state, summaryCtx0, argAp0, ap, apa, config) and
|
||||
fwdFlowOutFromArg(call, node, state, summaryCtx0, argAp0, ap, config) and
|
||||
fwdFlowIsEntered(call, cc, summaryCtx, argAp, summaryCtx0, argAp0, config)
|
||||
)
|
||||
}
|
||||
@@ -1444,9 +1413,9 @@ private module MkStage<StageSig PrevStage> {
|
||||
NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, FlowState state, Cc cc,
|
||||
ParameterPositionOption summaryCtx, ApOption argAp, Configuration config
|
||||
) {
|
||||
exists(DataFlowType contentType, ApApprox apa1 |
|
||||
fwdFlow(node1, state, cc, summaryCtx, argAp, ap1, apa1, config) and
|
||||
PrevStage::storeStepCand(node1, apa1, tc, node2, contentType, config) and
|
||||
exists(DataFlowType contentType |
|
||||
fwdFlow(node1, state, cc, summaryCtx, argAp, ap1, config) and
|
||||
PrevStage::storeStepCand(node1, unbindApa(getApprox(ap1)), tc, node2, contentType, config) and
|
||||
typecheckStore(ap1, contentType)
|
||||
)
|
||||
}
|
||||
@@ -1464,104 +1433,43 @@ private module MkStage<StageSig PrevStage> {
|
||||
)
|
||||
}
|
||||
|
||||
private class ApNonNil instanceof Ap {
|
||||
pragma[nomagic]
|
||||
ApNonNil() { not this instanceof ApNil }
|
||||
|
||||
string toString() { result = "" }
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate fwdFlowRead0(
|
||||
NodeEx node1, FlowState state, Cc cc, ParameterPositionOption summaryCtx, ApOption argAp,
|
||||
ApNonNil ap, Configuration config
|
||||
) {
|
||||
fwdFlow(node1, state, cc, summaryCtx, argAp, ap, config) and
|
||||
PrevStage::readStepCand(node1, _, _, config)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate fwdFlowRead(
|
||||
Ap ap, Content c, NodeEx node1, NodeEx node2, FlowState state, Cc cc,
|
||||
ParameterPositionOption summaryCtx, ApOption argAp, Configuration config
|
||||
) {
|
||||
fwdFlowRead0(node1, state, cc, summaryCtx, argAp, ap, config) and
|
||||
fwdFlow(node1, state, cc, summaryCtx, argAp, ap, config) and
|
||||
PrevStage::readStepCand(node1, c, node2, config) and
|
||||
getHeadContent(ap) = c
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate fwdFlowIn(
|
||||
DataFlowCall call, ParamNodeEx p, FlowState state, Cc outercc, CcCall innercc,
|
||||
ParameterPositionOption summaryCtx, ApOption argAp, Ap ap, ApApprox apa, Configuration config
|
||||
DataFlowCall call, ParamNodeEx p, FlowState state, Cc outercc, Cc innercc,
|
||||
ParameterPositionOption summaryCtx, ApOption argAp, Ap ap, Configuration config
|
||||
) {
|
||||
exists(ArgNodeEx arg, boolean allowsFieldFlow |
|
||||
fwdFlow(arg, state, outercc, summaryCtx, argAp, ap, apa, config) and
|
||||
flowIntoCallApa(call, arg, p, allowsFieldFlow, apa, config) and
|
||||
fwdFlow(arg, state, outercc, summaryCtx, argAp, ap, config) and
|
||||
flowIntoCall(call, arg, p, allowsFieldFlow, config) and
|
||||
innercc = getCallContextCall(call, p.getEnclosingCallable(), outercc) and
|
||||
if allowsFieldFlow = false then ap instanceof ApNil else any()
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate fwdFlowRetFromArg(
|
||||
RetNodeEx ret, FlowState state, CcCall ccc, ParameterPosition summaryCtx, ParamNodeEx p,
|
||||
Ap argAp, ApApprox argApa, Ap ap, ApApprox apa, Configuration config
|
||||
) {
|
||||
exists(DataFlowCallable c, ReturnKindExt kind |
|
||||
fwdFlow(pragma[only_bind_into](ret), state, ccc,
|
||||
TParameterPositionSome(pragma[only_bind_into](summaryCtx)), apSome(argAp), ap, apa, config) and
|
||||
getApprox(argAp) = argApa and
|
||||
c = ret.getEnclosingCallable() and
|
||||
kind = ret.getKind() and
|
||||
p.isParameterOf(c, pragma[only_bind_into](summaryCtx)) and
|
||||
parameterFlowThroughAllowed(p, kind)
|
||||
)
|
||||
}
|
||||
|
||||
pragma[inline]
|
||||
private predicate fwdFlowInMayFlowThrough(
|
||||
DataFlowCall call, Cc cc, CcCall innerCc, ParameterPositionOption summaryCtx, ApOption argAp,
|
||||
ParamNodeEx param, Ap ap, ApApprox apa, Configuration config
|
||||
) {
|
||||
fwdFlowIn(call, pragma[only_bind_into](param), _, cc, innerCc, summaryCtx, argAp, ap,
|
||||
pragma[only_bind_into](apa), pragma[only_bind_into](config)) and
|
||||
PrevStage::parameterMayFlowThrough(param, apa, config)
|
||||
}
|
||||
|
||||
// dedup before joining with `flowThroughOutOfCall`
|
||||
pragma[nomagic]
|
||||
private predicate fwdFlowInMayFlowThroughProj(
|
||||
DataFlowCall call, CcCall innerCc, ApApprox apa, Configuration config
|
||||
) {
|
||||
fwdFlowInMayFlowThrough(call, _, innerCc, _, _, _, _, apa, config)
|
||||
}
|
||||
|
||||
/**
|
||||
* Same as `flowThroughOutOfCall`, but restricted to calls that are reached
|
||||
* in the flow covered by `fwdFlow`, where data might flow through the target
|
||||
* callable and back out at `call`.
|
||||
*/
|
||||
pragma[nomagic]
|
||||
private predicate fwdFlowThroughOutOfCall(
|
||||
DataFlowCall call, CcCall ccc, RetNodeEx ret, NodeEx out, boolean allowsFieldFlow,
|
||||
ApApprox argApa, ApApprox apa, Configuration config
|
||||
) {
|
||||
fwdFlowInMayFlowThroughProj(call, ccc, argApa, config) and
|
||||
flowThroughOutOfCall(call, ccc, ret, out, allowsFieldFlow, argApa, apa, config)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate fwdFlowOutFromArg(
|
||||
DataFlowCall call, NodeEx out, FlowState state, ParameterPosition summaryCtx, Ap argAp, Ap ap,
|
||||
ApApprox apa, Configuration config
|
||||
Configuration config
|
||||
) {
|
||||
exists(RetNodeEx ret, boolean allowsFieldFlow, CcCall ccc, ApApprox argApa |
|
||||
fwdFlowRetFromArg(pragma[only_bind_into](ret), state, pragma[only_bind_into](ccc),
|
||||
summaryCtx, _, argAp, pragma[only_bind_into](argApa), ap, pragma[only_bind_into](apa),
|
||||
exists(
|
||||
DataFlowCallable c, RetNodeEx ret, ReturnKindExt kind, boolean allowsFieldFlow, CcCall ccc
|
||||
|
|
||||
fwdFlow(pragma[only_bind_into](ret), state, pragma[only_bind_into](ccc),
|
||||
TParameterPositionSome(pragma[only_bind_into](summaryCtx)), apSome(argAp), ap, config) and
|
||||
flowThroughOutOfCall(call, pragma[only_bind_into](c), ccc, ret, kind, out, allowsFieldFlow,
|
||||
config) and
|
||||
fwdFlowThroughOutOfCall(call, ccc, ret, out, allowsFieldFlow, argApa, apa, config) and
|
||||
(if allowsFieldFlow = false then ap instanceof ApNil else any())
|
||||
(if allowsFieldFlow = false then ap instanceof ApNil else any()) and
|
||||
parameterFlowThroughAllowed(c, pragma[only_bind_into](summaryCtx), kind)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1575,7 +1483,8 @@ private module MkStage<StageSig PrevStage> {
|
||||
ParameterPosition pos, Ap ap, Configuration config
|
||||
) {
|
||||
exists(ParamNodeEx param |
|
||||
fwdFlowInMayFlowThrough(call, cc, _, summaryCtx, argAp, param, ap, _, config) and
|
||||
fwdFlowIn(call, param, _, cc, _, summaryCtx, argAp, ap, config) and
|
||||
PrevStage::parameterMayFlowThrough(param, unbindApa(getApprox(ap)), config) and
|
||||
pos = param.getPosition()
|
||||
)
|
||||
}
|
||||
@@ -1596,55 +1505,41 @@ private module MkStage<StageSig PrevStage> {
|
||||
fwdFlowConsCand(ap1, c, ap2, config)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate returnFlowsThrough0(
|
||||
RetNodeEx ret, ReturnKindExt kind, FlowState state, CcCall ccc, DataFlowCallable c,
|
||||
ParameterPosition ppos, Ap argAp, Ap ap, Configuration config
|
||||
) {
|
||||
exists(boolean allowsFieldFlow |
|
||||
fwdFlow(ret, state, ccc, TParameterPositionSome(ppos), apSome(argAp), ap, config) and
|
||||
flowThroughOutOfCall(_, c, _, pragma[only_bind_into](ret), kind, _, allowsFieldFlow,
|
||||
pragma[only_bind_into](config)) and
|
||||
if allowsFieldFlow = false then ap instanceof ApNil else any()
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate returnFlowsThrough(
|
||||
RetNodeEx ret, ReturnKindExt kind, FlowState state, CcCall ccc, ParamNodeEx p, Ap argAp,
|
||||
Ap ap, Configuration config
|
||||
) {
|
||||
exists(boolean allowsFieldFlow, ApApprox argApa, ApApprox apa |
|
||||
fwdFlowRetFromArg(pragma[only_bind_into](ret), state, pragma[only_bind_into](ccc), _, p,
|
||||
argAp, pragma[only_bind_into](argApa), ap, pragma[only_bind_into](apa), config) and
|
||||
kind = ret.getKind() and
|
||||
fwdFlowThroughOutOfCall(_, ccc, ret, _, allowsFieldFlow, argApa, apa, config) and
|
||||
(if allowsFieldFlow = false then ap instanceof ApNil else any())
|
||||
exists(DataFlowCallable c, ParameterPosition ppos |
|
||||
returnFlowsThrough0(ret, kind, state, ccc, c, ppos, argAp, ap, config) and
|
||||
p.isParameterOf(c, ppos) and
|
||||
parameterFlowThroughAllowed(p, kind)
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate flowThroughIntoCall(
|
||||
DataFlowCall call, ArgNodeEx arg, ParamNodeEx p, boolean allowsFieldFlow, Ap argAp,
|
||||
Configuration config
|
||||
DataFlowCall call, ArgNodeEx arg, ParamNodeEx p, boolean allowsFieldFlow, Configuration config
|
||||
) {
|
||||
exists(ApApprox argApa |
|
||||
flowIntoCallApa(call, pragma[only_bind_into](arg), pragma[only_bind_into](p),
|
||||
allowsFieldFlow, argApa, pragma[only_bind_into](config)) and
|
||||
fwdFlow(arg, _, _, _, _, pragma[only_bind_into](argAp), argApa,
|
||||
exists(Ap argAp |
|
||||
flowIntoCall(call, pragma[only_bind_into](arg), pragma[only_bind_into](p), allowsFieldFlow,
|
||||
pragma[only_bind_into](config)) and
|
||||
fwdFlow(arg, _, _, _, _, pragma[only_bind_into](argAp), pragma[only_bind_into](config)) and
|
||||
returnFlowsThrough(_, _, _, _, p, pragma[only_bind_into](argAp), _,
|
||||
pragma[only_bind_into](config)) and
|
||||
if allowsFieldFlow = false then argAp instanceof ApNil else any()
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate flowIntoCallAp(
|
||||
DataFlowCall call, ArgNodeEx arg, ParamNodeEx p, boolean allowsFieldFlow, Ap ap,
|
||||
Configuration config
|
||||
) {
|
||||
exists(ApApprox apa |
|
||||
flowIntoCallApa(call, arg, p, allowsFieldFlow, apa, config) and
|
||||
fwdFlow(arg, _, _, _, _, ap, apa, config)
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate flowOutOfCallAp(
|
||||
DataFlowCall call, RetNodeEx ret, ReturnKindExt kind, NodeEx out, boolean allowsFieldFlow,
|
||||
Ap ap, Configuration config
|
||||
) {
|
||||
exists(ApApprox apa |
|
||||
flowOutOfCallApa(call, ret, kind, out, allowsFieldFlow, apa, config) and
|
||||
fwdFlow(ret, _, _, _, _, ap, apa, config)
|
||||
pragma[only_bind_into](config))
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1733,7 +1628,7 @@ private module MkStage<StageSig PrevStage> {
|
||||
// flow into a callable
|
||||
exists(ParamNodeEx p, boolean allowsFieldFlow |
|
||||
revFlow(p, state, TReturnCtxNone(), returnAp, ap, config) and
|
||||
flowIntoCallAp(_, node, p, allowsFieldFlow, ap, config) and
|
||||
flowIntoCall(_, node, p, allowsFieldFlow, config) and
|
||||
(if allowsFieldFlow = false then ap instanceof ApNil else any()) and
|
||||
returnCtx = TReturnCtxNone()
|
||||
)
|
||||
@@ -1787,41 +1682,22 @@ private module MkStage<StageSig PrevStage> {
|
||||
) {
|
||||
exists(NodeEx out, boolean allowsFieldFlow |
|
||||
revFlow(out, state, returnCtx, returnAp, ap, config) and
|
||||
flowOutOfCallAp(call, ret, kind, out, allowsFieldFlow, ap, config) and
|
||||
flowOutOfCall(call, ret, kind, out, allowsFieldFlow, config) and
|
||||
if allowsFieldFlow = false then ap instanceof ApNil else any()
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Same as `flowThroughIntoCall`, but restricted to calls that are reached
|
||||
* in the flow covered by `revFlow`, where data might flow through the target
|
||||
* callable and back out at `call`.
|
||||
*/
|
||||
pragma[nomagic]
|
||||
private predicate revFlowThroughIntoCall(
|
||||
DataFlowCall call, ArgNodeEx arg, ParamNodeEx p, boolean allowsFieldFlow, Ap argAp,
|
||||
Configuration config
|
||||
) {
|
||||
flowThroughIntoCall(call, arg, p, allowsFieldFlow, argAp, config) and
|
||||
revFlowIsReturned(call, _, _, _, _, config)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate revFlowParamToReturn(
|
||||
ParamNodeEx p, FlowState state, ReturnKindExt kind, Ap returnAp, Ap ap, Configuration config
|
||||
) {
|
||||
revFlow(p, state, TReturnCtxMaybeFlowThrough(kind), apSome(returnAp), ap, config) and
|
||||
parameterFlowThroughAllowed(p, kind)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate revFlowInToReturn(
|
||||
DataFlowCall call, ArgNodeEx arg, FlowState state, ReturnKindExt kind, Ap returnAp, Ap ap,
|
||||
Configuration config
|
||||
) {
|
||||
exists(ParamNodeEx p, boolean allowsFieldFlow |
|
||||
revFlowParamToReturn(p, state, kind, returnAp, ap, config) and
|
||||
revFlowThroughIntoCall(call, arg, p, allowsFieldFlow, ap, config)
|
||||
revFlow(pragma[only_bind_into](p), state,
|
||||
TReturnCtxMaybeFlowThrough(pragma[only_bind_into](kind)), apSome(returnAp), ap, config) and
|
||||
flowThroughIntoCall(call, arg, p, allowsFieldFlow, config) and
|
||||
(if allowsFieldFlow = false then ap instanceof ApNil else any()) and
|
||||
parameterFlowThroughAllowed(p, kind)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1874,11 +1750,6 @@ private module MkStage<StageSig PrevStage> {
|
||||
pragma[nomagic]
|
||||
predicate revFlow(NodeEx node, Configuration config) { revFlow(node, _, _, _, _, config) }
|
||||
|
||||
pragma[nomagic]
|
||||
predicate revFlowAp(NodeEx node, Ap ap, Configuration config) {
|
||||
revFlow(node, _, _, _, ap, config)
|
||||
}
|
||||
|
||||
// use an alias as a workaround for bad functionality-induced joins
|
||||
pragma[nomagic]
|
||||
additional predicate revFlowAlias(NodeEx node, Configuration config) {
|
||||
@@ -1915,9 +1786,9 @@ private module MkStage<StageSig PrevStage> {
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate parameterFlowsThroughRev(
|
||||
ParamNodeEx p, Ap ap, ReturnKindExt kind, Ap returnAp, Configuration config
|
||||
ParamNodeEx p, Ap ap, ReturnKindExt kind, Configuration config
|
||||
) {
|
||||
revFlow(p, _, TReturnCtxMaybeFlowThrough(kind), apSome(returnAp), ap, config) and
|
||||
revFlow(p, _, TReturnCtxMaybeFlowThrough(kind), apSome(_), ap, config) and
|
||||
parameterFlowThroughAllowed(p, kind)
|
||||
}
|
||||
|
||||
@@ -1925,36 +1796,27 @@ private module MkStage<StageSig PrevStage> {
|
||||
predicate parameterMayFlowThrough(ParamNodeEx p, Ap ap, Configuration config) {
|
||||
exists(RetNodeEx ret, ReturnKindExt kind |
|
||||
returnFlowsThrough(ret, kind, _, _, p, ap, _, config) and
|
||||
parameterFlowsThroughRev(p, ap, kind, _, config)
|
||||
parameterFlowsThroughRev(p, ap, kind, config)
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
predicate returnMayFlowThrough(
|
||||
RetNodeEx ret, Ap argAp, Ap ap, ReturnKindExt kind, Configuration config
|
||||
) {
|
||||
exists(ParamNodeEx p |
|
||||
returnFlowsThrough(ret, kind, _, _, p, argAp, ap, config) and
|
||||
parameterFlowsThroughRev(p, argAp, kind, ap, config)
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
predicate revFlowInToReturnIsReturned(
|
||||
DataFlowCall call, ArgNodeEx arg, FlowState state, ReturnCtx returnCtx, ApOption returnAp,
|
||||
Ap ap, Configuration config
|
||||
) {
|
||||
exists(ReturnKindExt returnKind0, Ap returnAp0 |
|
||||
revFlowInToReturn(call, arg, state, returnKind0, returnAp0, ap, config) and
|
||||
revFlowIsReturned(call, returnCtx, returnAp, returnKind0, returnAp0, config)
|
||||
predicate returnMayFlowThrough(RetNodeEx ret, ReturnKindExt kind, Configuration config) {
|
||||
exists(ParamNodeEx p, Ap ap |
|
||||
returnFlowsThrough(ret, kind, _, _, p, ap, _, config) and
|
||||
parameterFlowsThroughRev(p, ap, kind, config)
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
predicate callMayFlowThroughRev(DataFlowCall call, Configuration config) {
|
||||
exists(ArgNodeEx arg, FlowState state, ReturnCtx returnCtx, ApOption returnAp, Ap ap |
|
||||
exists(
|
||||
ReturnKindExt returnKind0, Ap returnAp0, ArgNodeEx arg, FlowState state,
|
||||
ReturnCtx returnCtx, ApOption returnAp, Ap ap
|
||||
|
|
||||
revFlow(arg, state, returnCtx, returnAp, ap, config) and
|
||||
revFlowInToReturnIsReturned(call, arg, state, returnCtx, returnAp, ap, config)
|
||||
revFlowInToReturn(call, arg, state, returnKind0, returnAp0, ap, config) and
|
||||
revFlowIsReturned(call, returnCtx, returnAp, returnKind0, returnAp0, config)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -2317,16 +2179,16 @@ private module LocalFlowBigStep {
|
||||
pragma[nomagic]
|
||||
predicate localFlowBigStep(
|
||||
NodeEx node1, FlowState state1, NodeEx node2, FlowState state2, boolean preservesValue,
|
||||
DataFlowType t, Configuration config, LocalCallContext callContext
|
||||
AccessPathFrontNil apf, Configuration config, LocalCallContext callContext
|
||||
) {
|
||||
localFlowStepPlus(node1, state1, node2, preservesValue, t, config, callContext) and
|
||||
localFlowStepPlus(node1, state1, node2, preservesValue, apf.getType(), config, callContext) and
|
||||
localFlowExit(node2, state1, config) and
|
||||
state1 = state2
|
||||
or
|
||||
additionalLocalFlowStepNodeCand2(node1, state1, node2, state2, config) and
|
||||
state1 != state2 and
|
||||
preservesValue = false and
|
||||
t = node2.getDataFlowType() and
|
||||
apf = TFrontNil(node2.getDataFlowType()) and
|
||||
callContext.relevantFor(node1.getEnclosingCallable()) and
|
||||
not exists(DataFlowCall call | call = callContext.(LocalCallContextSpecificCall).getCall() |
|
||||
isUnreachableInCallCached(node1.asNode(), call) or
|
||||
@@ -2340,87 +2202,11 @@ private import LocalFlowBigStep
|
||||
private module Stage3Param implements MkStage<Stage2>::StageParam {
|
||||
private module PrevStage = Stage2;
|
||||
|
||||
class Ap = ApproxAccessPathFront;
|
||||
|
||||
class ApNil = ApproxAccessPathFrontNil;
|
||||
|
||||
PrevStage::Ap getApprox(Ap ap) { result = ap.toBoolNonEmpty() }
|
||||
|
||||
ApNil getApNil(NodeEx node) {
|
||||
PrevStage::revFlow(node, _) and result = TApproxFrontNil(node.getDataFlowType())
|
||||
}
|
||||
|
||||
bindingset[tc, tail]
|
||||
Ap apCons(TypedContent tc, Ap tail) { result.getAHead() = tc and exists(tail) }
|
||||
|
||||
pragma[noinline]
|
||||
Content getHeadContent(Ap ap) { result = ap.getAHead().getContent() }
|
||||
|
||||
class ApOption = ApproxAccessPathFrontOption;
|
||||
|
||||
ApOption apNone() { result = TApproxAccessPathFrontNone() }
|
||||
|
||||
ApOption apSome(Ap ap) { result = TApproxAccessPathFrontSome(ap) }
|
||||
|
||||
import BooleanCallContext
|
||||
|
||||
predicate localStep(
|
||||
NodeEx node1, FlowState state1, NodeEx node2, FlowState state2, boolean preservesValue,
|
||||
ApproxAccessPathFrontNil ap, Configuration config, LocalCc lcc
|
||||
) {
|
||||
localFlowBigStep(node1, state1, node2, state2, preservesValue, ap.getType(), config, _) and
|
||||
exists(lcc)
|
||||
}
|
||||
|
||||
predicate flowOutOfCall = flowOutOfCallNodeCand2/6;
|
||||
|
||||
predicate flowIntoCall = flowIntoCallNodeCand2/5;
|
||||
|
||||
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.getAHead().getContent()
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate castingNodeEx(NodeEx node) { node.asNode() instanceof CastingNode }
|
||||
|
||||
bindingset[node, state, ap, config]
|
||||
predicate filter(NodeEx node, FlowState state, Ap ap, Configuration config) {
|
||||
exists(state) and
|
||||
exists(config) and
|
||||
(if castingNodeEx(node) then compatibleTypes(node.getDataFlowType(), ap.getType()) else any()) and
|
||||
(
|
||||
notExpectsContent(node)
|
||||
or
|
||||
expectsContentCand(node, ap, config)
|
||||
)
|
||||
}
|
||||
|
||||
bindingset[ap, contentType]
|
||||
predicate typecheckStore(Ap ap, DataFlowType contentType) {
|
||||
// We need to typecheck stores here, since reverse flow through a getter
|
||||
// might have a different type here compared to inside the getter.
|
||||
compatibleTypes(ap.getType(), contentType)
|
||||
}
|
||||
}
|
||||
|
||||
private module Stage3 implements StageSig {
|
||||
import MkStage<Stage2>::Stage<Stage3Param>
|
||||
}
|
||||
|
||||
private module Stage4Param implements MkStage<Stage3>::StageParam {
|
||||
private module PrevStage = Stage3;
|
||||
|
||||
class Ap = AccessPathFront;
|
||||
|
||||
class ApNil = AccessPathFrontNil;
|
||||
|
||||
PrevStage::Ap getApprox(Ap ap) { result = ap.toApprox() }
|
||||
PrevStage::Ap getApprox(Ap ap) { result = ap.toBoolNonEmpty() }
|
||||
|
||||
ApNil getApNil(NodeEx node) {
|
||||
PrevStage::revFlow(node, _) and result = TFrontNil(node.getDataFlowType())
|
||||
@@ -2440,42 +2226,16 @@ private module Stage4Param implements MkStage<Stage3>::StageParam {
|
||||
|
||||
import BooleanCallContext
|
||||
|
||||
pragma[nomagic]
|
||||
predicate localStep(
|
||||
NodeEx node1, FlowState state1, NodeEx node2, FlowState state2, boolean preservesValue,
|
||||
ApNil ap, Configuration config, LocalCc lcc
|
||||
) {
|
||||
localFlowBigStep(node1, state1, node2, state2, preservesValue, ap.getType(), config, _) and
|
||||
PrevStage::revFlow(node1, pragma[only_bind_into](state1), _, pragma[only_bind_into](config)) and
|
||||
PrevStage::revFlowAlias(node2, pragma[only_bind_into](state2), _, pragma[only_bind_into](config)) and
|
||||
exists(lcc)
|
||||
localFlowBigStep(node1, state1, node2, state2, preservesValue, ap, config, _) and exists(lcc)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
predicate flowOutOfCall(
|
||||
DataFlowCall call, RetNodeEx node1, ReturnKindExt kind, NodeEx node2, boolean allowsFieldFlow,
|
||||
Configuration config
|
||||
) {
|
||||
exists(FlowState state |
|
||||
flowOutOfCallNodeCand2(call, node1, kind, node2, allowsFieldFlow, config) and
|
||||
PrevStage::revFlow(node2, pragma[only_bind_into](state), _, pragma[only_bind_into](config)) and
|
||||
PrevStage::revFlowAlias(node1, pragma[only_bind_into](state), _,
|
||||
pragma[only_bind_into](config))
|
||||
)
|
||||
}
|
||||
predicate flowOutOfCall = flowOutOfCallNodeCand2/6;
|
||||
|
||||
pragma[nomagic]
|
||||
predicate flowIntoCall(
|
||||
DataFlowCall call, ArgNodeEx node1, ParamNodeEx node2, boolean allowsFieldFlow,
|
||||
Configuration config
|
||||
) {
|
||||
exists(FlowState state |
|
||||
flowIntoCallNodeCand2(call, node1, node2, allowsFieldFlow, config) and
|
||||
PrevStage::revFlow(node2, pragma[only_bind_into](state), _, pragma[only_bind_into](config)) and
|
||||
PrevStage::revFlowAlias(node1, pragma[only_bind_into](state), _,
|
||||
pragma[only_bind_into](config))
|
||||
)
|
||||
}
|
||||
predicate flowIntoCall = flowIntoCallNodeCand2/5;
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate clearSet(NodeEx node, ContentSet c, Configuration config) {
|
||||
@@ -2531,8 +2291,8 @@ private module Stage4Param implements MkStage<Stage3>::StageParam {
|
||||
}
|
||||
}
|
||||
|
||||
private module Stage4 implements StageSig {
|
||||
import MkStage<Stage3>::Stage<Stage4Param>
|
||||
private module Stage3 implements StageSig {
|
||||
import MkStage<Stage2>::Stage<Stage3Param>
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -2543,8 +2303,8 @@ private predicate flowCandSummaryCtx(
|
||||
NodeEx node, FlowState state, AccessPathFront argApf, Configuration config
|
||||
) {
|
||||
exists(AccessPathFront apf |
|
||||
Stage4::revFlow(node, state, TReturnCtxMaybeFlowThrough(_), _, apf, config) and
|
||||
Stage4::fwdFlow(node, state, any(Stage4::CcCall ccc), _, TAccessPathFrontSome(argApf), apf,
|
||||
Stage3::revFlow(node, state, TReturnCtxMaybeFlowThrough(_), _, apf, config) and
|
||||
Stage3::fwdFlow(node, state, any(Stage3::CcCall ccc), _, TAccessPathFrontSome(argApf), apf,
|
||||
config)
|
||||
)
|
||||
}
|
||||
@@ -2555,10 +2315,10 @@ private predicate flowCandSummaryCtx(
|
||||
*/
|
||||
private predicate expensiveLen2unfolding(TypedContent tc, Configuration config) {
|
||||
exists(int tails, int nodes, int apLimit, int tupleLimit |
|
||||
tails = strictcount(AccessPathFront apf | Stage4::consCand(tc, apf, config)) and
|
||||
tails = strictcount(AccessPathFront apf | Stage3::consCand(tc, apf, config)) and
|
||||
nodes =
|
||||
strictcount(NodeEx n, FlowState state |
|
||||
Stage4::revFlow(n, state, any(AccessPathFrontHead apf | apf.getHead() = tc), config)
|
||||
Stage3::revFlow(n, state, any(AccessPathFrontHead apf | apf.getHead() = tc), config)
|
||||
or
|
||||
flowCandSummaryCtx(n, state, any(AccessPathFrontHead apf | apf.getHead() = tc), config)
|
||||
) and
|
||||
@@ -2572,11 +2332,11 @@ private predicate expensiveLen2unfolding(TypedContent tc, Configuration config)
|
||||
private newtype TAccessPathApprox =
|
||||
TNil(DataFlowType t) or
|
||||
TConsNil(TypedContent tc, DataFlowType t) {
|
||||
Stage4::consCand(tc, TFrontNil(t), _) and
|
||||
Stage3::consCand(tc, TFrontNil(t), _) and
|
||||
not expensiveLen2unfolding(tc, _)
|
||||
} or
|
||||
TConsCons(TypedContent tc1, TypedContent tc2, int len) {
|
||||
Stage4::consCand(tc1, TFrontHead(tc2), _) and
|
||||
Stage3::consCand(tc1, TFrontHead(tc2), _) and
|
||||
len in [2 .. accessPathLimit()] and
|
||||
not expensiveLen2unfolding(tc1, _)
|
||||
} or
|
||||
@@ -2706,7 +2466,7 @@ private class AccessPathApproxCons1 extends AccessPathApproxCons, TCons1 {
|
||||
override AccessPathApprox pop(TypedContent head) {
|
||||
head = tc and
|
||||
(
|
||||
exists(TypedContent tc2 | Stage4::consCand(tc, TFrontHead(tc2), _) |
|
||||
exists(TypedContent tc2 | Stage3::consCand(tc, TFrontHead(tc2), _) |
|
||||
result = TConsCons(tc2, _, len - 1)
|
||||
or
|
||||
len = 2 and
|
||||
@@ -2717,7 +2477,7 @@ private class AccessPathApproxCons1 extends AccessPathApproxCons, TCons1 {
|
||||
or
|
||||
exists(DataFlowType t |
|
||||
len = 1 and
|
||||
Stage4::consCand(tc, TFrontNil(t), _) and
|
||||
Stage3::consCand(tc, TFrontNil(t), _) and
|
||||
result = TNil(t)
|
||||
)
|
||||
)
|
||||
@@ -2742,14 +2502,13 @@ private class AccessPathApproxOption extends TAccessPathApproxOption {
|
||||
}
|
||||
}
|
||||
|
||||
private module Stage5Param implements MkStage<Stage4>::StageParam {
|
||||
private module PrevStage = Stage4;
|
||||
private module Stage4Param implements MkStage<Stage3>::StageParam {
|
||||
private module PrevStage = Stage3;
|
||||
|
||||
class Ap = AccessPathApprox;
|
||||
|
||||
class ApNil = AccessPathApproxNil;
|
||||
|
||||
pragma[nomagic]
|
||||
PrevStage::Ap getApprox(Ap ap) { result = ap.getFront() }
|
||||
|
||||
ApNil getApNil(NodeEx node) {
|
||||
@@ -2775,9 +2534,7 @@ private module Stage5Param implements MkStage<Stage4>::StageParam {
|
||||
NodeEx node1, FlowState state1, NodeEx node2, FlowState state2, boolean preservesValue,
|
||||
ApNil ap, Configuration config, LocalCc lcc
|
||||
) {
|
||||
localFlowBigStep(node1, state1, node2, state2, preservesValue, ap.getType(), config, lcc) and
|
||||
PrevStage::revFlow(node1, pragma[only_bind_into](state1), _, pragma[only_bind_into](config)) and
|
||||
PrevStage::revFlowAlias(node2, pragma[only_bind_into](state2), _, pragma[only_bind_into](config))
|
||||
localFlowBigStep(node1, state1, node2, state2, preservesValue, ap.getFront(), config, lcc)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
@@ -2814,7 +2571,7 @@ private module Stage5Param implements MkStage<Stage4>::StageParam {
|
||||
predicate typecheckStore(Ap ap, DataFlowType contentType) { any() }
|
||||
}
|
||||
|
||||
private module Stage5 = MkStage<Stage4>::Stage<Stage5Param>;
|
||||
private module Stage4 = MkStage<Stage3>::Stage<Stage4Param>;
|
||||
|
||||
bindingset[conf, result]
|
||||
private Configuration unbindConf(Configuration conf) {
|
||||
@@ -2828,8 +2585,8 @@ private predicate nodeMayUseSummary0(
|
||||
) {
|
||||
exists(AccessPathApprox apa0 |
|
||||
c = n.getEnclosingCallable() and
|
||||
Stage5::revFlow(n, state, TReturnCtxMaybeFlowThrough(_), _, apa0, config) and
|
||||
Stage5::fwdFlow(n, state, any(CallContextCall ccc), TParameterPositionSome(pos),
|
||||
Stage4::revFlow(n, state, TReturnCtxMaybeFlowThrough(_), _, apa0, config) and
|
||||
Stage4::fwdFlow(n, state, any(CallContextCall ccc), TParameterPositionSome(pos),
|
||||
TAccessPathApproxSome(apa), apa0, config)
|
||||
)
|
||||
}
|
||||
@@ -2839,7 +2596,7 @@ private predicate nodeMayUseSummary(
|
||||
NodeEx n, FlowState state, AccessPathApprox apa, Configuration config
|
||||
) {
|
||||
exists(DataFlowCallable c, ParameterPosition pos, ParamNodeEx p |
|
||||
Stage5::parameterMayFlowThrough(p, apa, config) and
|
||||
Stage4::parameterMayFlowThrough(p, apa, config) and
|
||||
nodeMayUseSummary0(n, c, pos, state, apa, config) and
|
||||
p.isParameterOf(c, pos)
|
||||
)
|
||||
@@ -2849,8 +2606,8 @@ private newtype TSummaryCtx =
|
||||
TSummaryCtxNone() or
|
||||
TSummaryCtxSome(ParamNodeEx p, FlowState state, AccessPath ap) {
|
||||
exists(Configuration config |
|
||||
Stage5::parameterMayFlowThrough(p, ap.getApprox(), config) and
|
||||
Stage5::revFlow(p, state, _, config)
|
||||
Stage4::parameterMayFlowThrough(p, ap.getApprox(), config) and
|
||||
Stage4::revFlow(p, state, _, config)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -2899,7 +2656,7 @@ private int count1to2unfold(AccessPathApproxCons1 apa, Configuration config) {
|
||||
len = apa.len() and
|
||||
result =
|
||||
strictcount(AccessPathFront apf |
|
||||
Stage5::consCand(tc, any(AccessPathApprox ap | ap.getFront() = apf and ap.len() = len - 1),
|
||||
Stage4::consCand(tc, any(AccessPathApprox ap | ap.getFront() = apf and ap.len() = len - 1),
|
||||
config)
|
||||
)
|
||||
)
|
||||
@@ -2908,7 +2665,7 @@ private int count1to2unfold(AccessPathApproxCons1 apa, Configuration config) {
|
||||
private int countNodesUsingAccessPath(AccessPathApprox apa, Configuration config) {
|
||||
result =
|
||||
strictcount(NodeEx n, FlowState state |
|
||||
Stage5::revFlow(n, state, apa, config) or nodeMayUseSummary(n, state, apa, config)
|
||||
Stage4::revFlow(n, state, apa, config) or nodeMayUseSummary(n, state, apa, config)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -2929,7 +2686,7 @@ private predicate expensiveLen1to2unfolding(AccessPathApproxCons1 apa, Configura
|
||||
private AccessPathApprox getATail(AccessPathApprox apa, Configuration config) {
|
||||
exists(TypedContent head |
|
||||
apa.pop(head) = result and
|
||||
Stage5::consCand(head, result, config)
|
||||
Stage4::consCand(head, result, config)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -3011,7 +2768,7 @@ private newtype TPathNode =
|
||||
NodeEx node, FlowState state, CallContext cc, SummaryCtx sc, AccessPath ap, Configuration config
|
||||
) {
|
||||
// A PathNode is introduced by a source ...
|
||||
Stage5::revFlow(node, state, config) and
|
||||
Stage4::revFlow(node, state, config) and
|
||||
sourceNode(node, state, config) and
|
||||
(
|
||||
if hasSourceCallCtx(config)
|
||||
@@ -3025,7 +2782,7 @@ private newtype TPathNode =
|
||||
exists(PathNodeMid mid |
|
||||
pathStep(mid, node, state, cc, sc, ap) and
|
||||
pragma[only_bind_into](config) = mid.getConfiguration() and
|
||||
Stage5::revFlow(node, state, ap.getApprox(), pragma[only_bind_into](config))
|
||||
Stage4::revFlow(node, state, ap.getApprox(), pragma[only_bind_into](config))
|
||||
)
|
||||
} or
|
||||
TPathNodeSink(NodeEx node, FlowState state, Configuration config) {
|
||||
@@ -3167,7 +2924,7 @@ private class AccessPathCons2 extends AccessPath, TAccessPathCons2 {
|
||||
override TypedContent getHead() { result = head1 }
|
||||
|
||||
override AccessPath getTail() {
|
||||
Stage5::consCand(head1, result.getApprox(), _) and
|
||||
Stage4::consCand(head1, result.getApprox(), _) and
|
||||
result.getHead() = head2 and
|
||||
result.length() = len - 1
|
||||
}
|
||||
@@ -3198,7 +2955,7 @@ private class AccessPathCons1 extends AccessPath, TAccessPathCons1 {
|
||||
override TypedContent getHead() { result = head }
|
||||
|
||||
override AccessPath getTail() {
|
||||
Stage5::consCand(head, result.getApprox(), _) and result.length() = len - 1
|
||||
Stage4::consCand(head, result.getApprox(), _) and result.length() = len - 1
|
||||
}
|
||||
|
||||
override AccessPathFrontHead getFront() { result = TFrontHead(head) }
|
||||
@@ -3590,8 +3347,7 @@ private predicate pathStep(
|
||||
AccessPath ap0, NodeEx midnode, FlowState state0, Configuration conf, LocalCallContext localCC
|
||||
|
|
||||
pathNode(mid, midnode, state0, cc, sc, ap0, conf, localCC) and
|
||||
localFlowBigStep(midnode, state0, node, state, false, ap.(AccessPathNil).getType(), conf,
|
||||
localCC) and
|
||||
localFlowBigStep(midnode, state0, node, state, false, ap.getFront(), conf, localCC) and
|
||||
ap0 instanceof AccessPathNil
|
||||
)
|
||||
or
|
||||
@@ -3633,7 +3389,7 @@ private predicate pathReadStep(
|
||||
) {
|
||||
ap0 = mid.getAp() and
|
||||
tc = ap0.getHead() and
|
||||
Stage5::readStepCand(mid.getNodeEx(), tc.getContent(), node, mid.getConfiguration()) and
|
||||
Stage4::readStepCand(mid.getNodeEx(), tc.getContent(), node, mid.getConfiguration()) and
|
||||
state = mid.getState() and
|
||||
cc = mid.getCallContext()
|
||||
}
|
||||
@@ -3643,7 +3399,7 @@ private predicate pathStoreStep(
|
||||
PathNodeMid mid, NodeEx node, FlowState state, AccessPath ap0, TypedContent tc, CallContext cc
|
||||
) {
|
||||
ap0 = mid.getAp() and
|
||||
Stage5::storeStepCand(mid.getNodeEx(), _, tc, node, _, mid.getConfiguration()) and
|
||||
Stage4::storeStepCand(mid.getNodeEx(), _, tc, node, _, mid.getConfiguration()) and
|
||||
state = mid.getState() and
|
||||
cc = mid.getCallContext()
|
||||
}
|
||||
@@ -3680,7 +3436,7 @@ private NodeEx getAnOutNodeFlow(
|
||||
ReturnKindExt kind, DataFlowCall call, AccessPathApprox apa, Configuration config
|
||||
) {
|
||||
result.asNode() = kind.getAnOutNode(call) and
|
||||
Stage5::revFlow(result, _, apa, config)
|
||||
Stage4::revFlow(result, _, apa, config)
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -3716,7 +3472,7 @@ private predicate parameterCand(
|
||||
DataFlowCallable callable, ParameterPosition pos, AccessPathApprox apa, Configuration config
|
||||
) {
|
||||
exists(ParamNodeEx p |
|
||||
Stage5::revFlow(p, _, apa, config) and
|
||||
Stage4::revFlow(p, _, apa, config) and
|
||||
p.isParameterOf(callable, pos)
|
||||
)
|
||||
}
|
||||
@@ -3995,17 +3751,9 @@ predicate stageStats(
|
||||
n = 45 and
|
||||
Stage4::stats(false, nodes, fields, conscand, states, tuples, config)
|
||||
or
|
||||
stage = "5 Fwd" and
|
||||
n = 50 and
|
||||
Stage5::stats(true, nodes, fields, conscand, states, tuples, config)
|
||||
stage = "5 Fwd" and n = 50 and finalStats(true, nodes, fields, conscand, states, tuples)
|
||||
or
|
||||
stage = "5 Rev" and
|
||||
n = 55 and
|
||||
Stage5::stats(false, nodes, fields, conscand, states, tuples, config)
|
||||
or
|
||||
stage = "6 Fwd" and n = 60 and finalStats(true, nodes, fields, conscand, states, tuples)
|
||||
or
|
||||
stage = "6 Rev" and n = 65 and finalStats(false, nodes, fields, conscand, states, tuples)
|
||||
stage = "5 Rev" and n = 55 and finalStats(false, nodes, fields, conscand, states, tuples)
|
||||
}
|
||||
|
||||
private module FlowExploration {
|
||||
|
||||
@@ -621,6 +621,23 @@ private predicate parameterFlowThroughAllowed(ParamNodeEx p, ReturnKindExt kind)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if flow from a parameter at position `pos` inside `c` to a return node of
|
||||
* kind `kind` is allowed.
|
||||
*
|
||||
* We don't expect a parameter to return stored in itself, unless
|
||||
* explicitly allowed
|
||||
*/
|
||||
bindingset[c, pos, kind]
|
||||
private predicate parameterFlowThroughAllowed(
|
||||
DataFlowCallable c, ParameterPosition pos, ReturnKindExt kind
|
||||
) {
|
||||
exists(ParamNodeEx p |
|
||||
p.isParameterOf(c, pos) and
|
||||
parameterFlowThroughAllowed(p, kind)
|
||||
)
|
||||
}
|
||||
|
||||
private module Stage1 implements StageSig {
|
||||
class Ap = Unit;
|
||||
|
||||
@@ -963,12 +980,6 @@ private module Stage1 implements StageSig {
|
||||
pragma[nomagic]
|
||||
predicate revFlow(NodeEx node, Configuration config) { revFlow(node, _, config) }
|
||||
|
||||
pragma[nomagic]
|
||||
predicate revFlowAp(NodeEx node, Ap ap, Configuration config) {
|
||||
revFlow(node, config) and
|
||||
exists(ap)
|
||||
}
|
||||
|
||||
bindingset[node, state, config]
|
||||
predicate revFlow(NodeEx node, FlowState state, Ap ap, Configuration config) {
|
||||
revFlow(node, _, pragma[only_bind_into](config)) and
|
||||
@@ -1011,13 +1022,9 @@ private module Stage1 implements StageSig {
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
predicate returnMayFlowThrough(
|
||||
RetNodeEx ret, Ap argAp, Ap ap, ReturnKindExt kind, Configuration config
|
||||
) {
|
||||
predicate returnMayFlowThrough(RetNodeEx ret, ReturnKindExt kind, Configuration config) {
|
||||
throughFlowNodeCand(ret, config) and
|
||||
kind = ret.getKind() and
|
||||
exists(argAp) and
|
||||
exists(ap)
|
||||
kind = ret.getKind()
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
@@ -1182,8 +1189,6 @@ private signature module StageSig {
|
||||
|
||||
predicate revFlow(NodeEx node, Configuration config);
|
||||
|
||||
predicate revFlowAp(NodeEx node, Ap ap, Configuration config);
|
||||
|
||||
bindingset[node, state, config]
|
||||
predicate revFlow(NodeEx node, FlowState state, Ap ap, Configuration config);
|
||||
|
||||
@@ -1191,9 +1196,7 @@ private signature module StageSig {
|
||||
|
||||
predicate parameterMayFlowThrough(ParamNodeEx p, Ap ap, Configuration config);
|
||||
|
||||
predicate returnMayFlowThrough(
|
||||
RetNodeEx ret, Ap argAp, Ap ap, ReturnKindExt kind, Configuration config
|
||||
);
|
||||
predicate returnMayFlowThrough(RetNodeEx ret, ReturnKindExt kind, Configuration config);
|
||||
|
||||
predicate storeStepCand(
|
||||
NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, DataFlowType contentType,
|
||||
@@ -1278,43 +1281,21 @@ private module MkStage<StageSig PrevStage> {
|
||||
import Param
|
||||
|
||||
/* Begin: Stage logic. */
|
||||
// use an alias as a workaround for bad functionality-induced joins
|
||||
pragma[nomagic]
|
||||
private predicate revFlowApAlias(NodeEx node, ApApprox apa, Configuration config) {
|
||||
PrevStage::revFlowAp(node, apa, config)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate flowIntoCallApa(
|
||||
DataFlowCall call, ArgNodeEx arg, ParamNodeEx p, boolean allowsFieldFlow, ApApprox apa,
|
||||
Configuration config
|
||||
) {
|
||||
flowIntoCall(call, arg, p, allowsFieldFlow, config) and
|
||||
PrevStage::revFlowAp(p, pragma[only_bind_into](apa), pragma[only_bind_into](config)) and
|
||||
revFlowApAlias(arg, pragma[only_bind_into](apa), pragma[only_bind_into](config))
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate flowOutOfCallApa(
|
||||
DataFlowCall call, RetNodeEx ret, ReturnKindExt kind, NodeEx out, boolean allowsFieldFlow,
|
||||
ApApprox apa, Configuration config
|
||||
) {
|
||||
flowOutOfCall(call, ret, kind, out, allowsFieldFlow, config) and
|
||||
PrevStage::revFlowAp(out, pragma[only_bind_into](apa), pragma[only_bind_into](config)) and
|
||||
revFlowApAlias(ret, pragma[only_bind_into](apa), pragma[only_bind_into](config))
|
||||
bindingset[result, apa]
|
||||
private ApApprox unbindApa(ApApprox apa) {
|
||||
pragma[only_bind_out](apa) = pragma[only_bind_out](result)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate flowThroughOutOfCall(
|
||||
DataFlowCall call, CcCall ccc, RetNodeEx ret, NodeEx out, boolean allowsFieldFlow,
|
||||
ApApprox argApa, ApApprox apa, Configuration config
|
||||
DataFlowCall call, DataFlowCallable c, CcCall ccc, RetNodeEx ret, ReturnKindExt kind,
|
||||
NodeEx out, boolean allowsFieldFlow, Configuration config
|
||||
) {
|
||||
exists(ReturnKindExt kind |
|
||||
flowOutOfCallApa(call, ret, kind, out, allowsFieldFlow, apa, pragma[only_bind_into](config)) and
|
||||
PrevStage::callMayFlowThroughRev(call, pragma[only_bind_into](config)) and
|
||||
PrevStage::returnMayFlowThrough(ret, argApa, apa, kind, pragma[only_bind_into](config)) and
|
||||
matchesCall(ccc, call)
|
||||
)
|
||||
flowOutOfCall(call, ret, kind, out, allowsFieldFlow, pragma[only_bind_into](config)) and
|
||||
PrevStage::callMayFlowThroughRev(call, pragma[only_bind_into](config)) and
|
||||
PrevStage::returnMayFlowThrough(ret, kind, pragma[only_bind_into](config)) and
|
||||
matchesCall(ccc, call) and
|
||||
c = ret.getEnclosingCallable()
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1326,50 +1307,39 @@ private module MkStage<StageSig PrevStage> {
|
||||
* corresponding parameter position and access path of that argument, respectively.
|
||||
*/
|
||||
pragma[nomagic]
|
||||
additional predicate fwdFlow(
|
||||
NodeEx node, FlowState state, Cc cc, ParameterPositionOption summaryCtx, ApOption argAp,
|
||||
Ap ap, ApApprox apa, Configuration config
|
||||
) {
|
||||
fwdFlow0(node, state, cc, summaryCtx, argAp, ap, apa, config) and
|
||||
PrevStage::revFlow(node, state, apa, config) and
|
||||
filter(node, state, ap, config)
|
||||
}
|
||||
|
||||
pragma[inline]
|
||||
additional predicate fwdFlow(
|
||||
NodeEx node, FlowState state, Cc cc, ParameterPositionOption summaryCtx, ApOption argAp,
|
||||
Ap ap, Configuration config
|
||||
) {
|
||||
fwdFlow(node, state, cc, summaryCtx, argAp, ap, _, config)
|
||||
fwdFlow0(node, state, cc, summaryCtx, argAp, ap, config) and
|
||||
PrevStage::revFlow(node, state, unbindApa(getApprox(ap)), config) and
|
||||
filter(node, state, ap, config)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate fwdFlow0(
|
||||
NodeEx node, FlowState state, Cc cc, ParameterPositionOption summaryCtx, ApOption argAp,
|
||||
Ap ap, ApApprox apa, Configuration config
|
||||
Ap ap, Configuration config
|
||||
) {
|
||||
sourceNode(node, state, config) and
|
||||
(if hasSourceCallCtx(config) then cc = ccSomeCall() else cc = ccNone()) and
|
||||
argAp = apNone() and
|
||||
summaryCtx = TParameterPositionNone() and
|
||||
ap = getApNil(node) and
|
||||
apa = getApprox(ap)
|
||||
ap = getApNil(node)
|
||||
or
|
||||
exists(NodeEx mid, FlowState state0, Ap ap0, ApApprox apa0, LocalCc localCc |
|
||||
fwdFlow(mid, state0, cc, summaryCtx, argAp, ap0, apa0, config) and
|
||||
exists(NodeEx mid, FlowState state0, Ap ap0, LocalCc localCc |
|
||||
fwdFlow(mid, state0, cc, summaryCtx, argAp, ap0, config) and
|
||||
localCc = getLocalCc(mid, cc)
|
||||
|
|
||||
localStep(mid, state0, node, state, true, _, config, localCc) and
|
||||
ap = ap0 and
|
||||
apa = apa0
|
||||
ap = ap0
|
||||
or
|
||||
localStep(mid, state0, node, state, false, ap, config, localCc) and
|
||||
ap0 instanceof ApNil and
|
||||
apa = getApprox(ap)
|
||||
ap0 instanceof ApNil
|
||||
)
|
||||
or
|
||||
exists(NodeEx mid |
|
||||
fwdFlow(mid, pragma[only_bind_into](state), _, _, _, ap, apa, pragma[only_bind_into](config)) and
|
||||
fwdFlow(mid, pragma[only_bind_into](state), _, _, _, ap, pragma[only_bind_into](config)) and
|
||||
jumpStep(mid, node, config) and
|
||||
cc = ccNone() and
|
||||
summaryCtx = TParameterPositionNone() and
|
||||
@@ -1382,8 +1352,7 @@ private module MkStage<StageSig PrevStage> {
|
||||
cc = ccNone() and
|
||||
summaryCtx = TParameterPositionNone() and
|
||||
argAp = apNone() and
|
||||
ap = getApNil(node) and
|
||||
apa = getApprox(ap)
|
||||
ap = getApNil(node)
|
||||
)
|
||||
or
|
||||
exists(NodeEx mid, FlowState state0, ApNil nil |
|
||||
@@ -1392,32 +1361,32 @@ private module MkStage<StageSig PrevStage> {
|
||||
cc = ccNone() and
|
||||
summaryCtx = TParameterPositionNone() and
|
||||
argAp = apNone() and
|
||||
ap = getApNil(node) and
|
||||
apa = getApprox(ap)
|
||||
ap = getApNil(node)
|
||||
)
|
||||
or
|
||||
// store
|
||||
exists(TypedContent tc, Ap ap0 |
|
||||
fwdFlowStore(_, ap0, tc, node, state, cc, summaryCtx, argAp, config) and
|
||||
ap = apCons(tc, ap0) and
|
||||
apa = getApprox(ap)
|
||||
ap = apCons(tc, ap0)
|
||||
)
|
||||
or
|
||||
// read
|
||||
exists(Ap ap0, Content c |
|
||||
fwdFlowRead(ap0, c, _, node, state, cc, summaryCtx, argAp, config) and
|
||||
fwdFlowConsCand(ap0, c, ap, config) and
|
||||
apa = getApprox(ap)
|
||||
fwdFlowConsCand(ap0, c, ap, config)
|
||||
)
|
||||
or
|
||||
// flow into a callable
|
||||
fwdFlowIn(_, node, state, _, cc, _, _, ap, apa, config) and
|
||||
if PrevStage::parameterMayFlowThrough(node, apa, config)
|
||||
then (
|
||||
summaryCtx = TParameterPositionSome(node.(ParamNodeEx).getPosition()) and
|
||||
argAp = apSome(ap)
|
||||
) else (
|
||||
summaryCtx = TParameterPositionNone() and argAp = apNone()
|
||||
exists(ApApprox apa |
|
||||
fwdFlowIn(_, node, state, _, cc, _, _, ap, config) and
|
||||
apa = getApprox(ap) and
|
||||
if PrevStage::parameterMayFlowThrough(node, apa, config)
|
||||
then (
|
||||
summaryCtx = TParameterPositionSome(node.(ParamNodeEx).getPosition()) and
|
||||
argAp = apSome(ap)
|
||||
) else (
|
||||
summaryCtx = TParameterPositionNone() and argAp = apNone()
|
||||
)
|
||||
)
|
||||
or
|
||||
// flow out of a callable
|
||||
@@ -1425,8 +1394,8 @@ private module MkStage<StageSig PrevStage> {
|
||||
DataFlowCall call, RetNodeEx ret, boolean allowsFieldFlow, CcNoCall innercc,
|
||||
DataFlowCallable inner
|
||||
|
|
||||
fwdFlow(ret, state, innercc, summaryCtx, argAp, ap, apa, config) and
|
||||
flowOutOfCallApa(call, ret, _, node, allowsFieldFlow, apa, config) and
|
||||
fwdFlow(ret, state, innercc, summaryCtx, argAp, ap, config) and
|
||||
flowOutOfCall(call, ret, _, node, allowsFieldFlow, config) and
|
||||
inner = ret.getEnclosingCallable() and
|
||||
cc = getCallContextReturn(inner, call, innercc) and
|
||||
if allowsFieldFlow = false then ap instanceof ApNil else any()
|
||||
@@ -1434,7 +1403,7 @@ private module MkStage<StageSig PrevStage> {
|
||||
or
|
||||
// flow through a callable
|
||||
exists(DataFlowCall call, ParameterPosition summaryCtx0, Ap argAp0 |
|
||||
fwdFlowOutFromArg(call, node, state, summaryCtx0, argAp0, ap, apa, config) and
|
||||
fwdFlowOutFromArg(call, node, state, summaryCtx0, argAp0, ap, config) and
|
||||
fwdFlowIsEntered(call, cc, summaryCtx, argAp, summaryCtx0, argAp0, config)
|
||||
)
|
||||
}
|
||||
@@ -1444,9 +1413,9 @@ private module MkStage<StageSig PrevStage> {
|
||||
NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, FlowState state, Cc cc,
|
||||
ParameterPositionOption summaryCtx, ApOption argAp, Configuration config
|
||||
) {
|
||||
exists(DataFlowType contentType, ApApprox apa1 |
|
||||
fwdFlow(node1, state, cc, summaryCtx, argAp, ap1, apa1, config) and
|
||||
PrevStage::storeStepCand(node1, apa1, tc, node2, contentType, config) and
|
||||
exists(DataFlowType contentType |
|
||||
fwdFlow(node1, state, cc, summaryCtx, argAp, ap1, config) and
|
||||
PrevStage::storeStepCand(node1, unbindApa(getApprox(ap1)), tc, node2, contentType, config) and
|
||||
typecheckStore(ap1, contentType)
|
||||
)
|
||||
}
|
||||
@@ -1464,104 +1433,43 @@ private module MkStage<StageSig PrevStage> {
|
||||
)
|
||||
}
|
||||
|
||||
private class ApNonNil instanceof Ap {
|
||||
pragma[nomagic]
|
||||
ApNonNil() { not this instanceof ApNil }
|
||||
|
||||
string toString() { result = "" }
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate fwdFlowRead0(
|
||||
NodeEx node1, FlowState state, Cc cc, ParameterPositionOption summaryCtx, ApOption argAp,
|
||||
ApNonNil ap, Configuration config
|
||||
) {
|
||||
fwdFlow(node1, state, cc, summaryCtx, argAp, ap, config) and
|
||||
PrevStage::readStepCand(node1, _, _, config)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate fwdFlowRead(
|
||||
Ap ap, Content c, NodeEx node1, NodeEx node2, FlowState state, Cc cc,
|
||||
ParameterPositionOption summaryCtx, ApOption argAp, Configuration config
|
||||
) {
|
||||
fwdFlowRead0(node1, state, cc, summaryCtx, argAp, ap, config) and
|
||||
fwdFlow(node1, state, cc, summaryCtx, argAp, ap, config) and
|
||||
PrevStage::readStepCand(node1, c, node2, config) and
|
||||
getHeadContent(ap) = c
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate fwdFlowIn(
|
||||
DataFlowCall call, ParamNodeEx p, FlowState state, Cc outercc, CcCall innercc,
|
||||
ParameterPositionOption summaryCtx, ApOption argAp, Ap ap, ApApprox apa, Configuration config
|
||||
DataFlowCall call, ParamNodeEx p, FlowState state, Cc outercc, Cc innercc,
|
||||
ParameterPositionOption summaryCtx, ApOption argAp, Ap ap, Configuration config
|
||||
) {
|
||||
exists(ArgNodeEx arg, boolean allowsFieldFlow |
|
||||
fwdFlow(arg, state, outercc, summaryCtx, argAp, ap, apa, config) and
|
||||
flowIntoCallApa(call, arg, p, allowsFieldFlow, apa, config) and
|
||||
fwdFlow(arg, state, outercc, summaryCtx, argAp, ap, config) and
|
||||
flowIntoCall(call, arg, p, allowsFieldFlow, config) and
|
||||
innercc = getCallContextCall(call, p.getEnclosingCallable(), outercc) and
|
||||
if allowsFieldFlow = false then ap instanceof ApNil else any()
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate fwdFlowRetFromArg(
|
||||
RetNodeEx ret, FlowState state, CcCall ccc, ParameterPosition summaryCtx, ParamNodeEx p,
|
||||
Ap argAp, ApApprox argApa, Ap ap, ApApprox apa, Configuration config
|
||||
) {
|
||||
exists(DataFlowCallable c, ReturnKindExt kind |
|
||||
fwdFlow(pragma[only_bind_into](ret), state, ccc,
|
||||
TParameterPositionSome(pragma[only_bind_into](summaryCtx)), apSome(argAp), ap, apa, config) and
|
||||
getApprox(argAp) = argApa and
|
||||
c = ret.getEnclosingCallable() and
|
||||
kind = ret.getKind() and
|
||||
p.isParameterOf(c, pragma[only_bind_into](summaryCtx)) and
|
||||
parameterFlowThroughAllowed(p, kind)
|
||||
)
|
||||
}
|
||||
|
||||
pragma[inline]
|
||||
private predicate fwdFlowInMayFlowThrough(
|
||||
DataFlowCall call, Cc cc, CcCall innerCc, ParameterPositionOption summaryCtx, ApOption argAp,
|
||||
ParamNodeEx param, Ap ap, ApApprox apa, Configuration config
|
||||
) {
|
||||
fwdFlowIn(call, pragma[only_bind_into](param), _, cc, innerCc, summaryCtx, argAp, ap,
|
||||
pragma[only_bind_into](apa), pragma[only_bind_into](config)) and
|
||||
PrevStage::parameterMayFlowThrough(param, apa, config)
|
||||
}
|
||||
|
||||
// dedup before joining with `flowThroughOutOfCall`
|
||||
pragma[nomagic]
|
||||
private predicate fwdFlowInMayFlowThroughProj(
|
||||
DataFlowCall call, CcCall innerCc, ApApprox apa, Configuration config
|
||||
) {
|
||||
fwdFlowInMayFlowThrough(call, _, innerCc, _, _, _, _, apa, config)
|
||||
}
|
||||
|
||||
/**
|
||||
* Same as `flowThroughOutOfCall`, but restricted to calls that are reached
|
||||
* in the flow covered by `fwdFlow`, where data might flow through the target
|
||||
* callable and back out at `call`.
|
||||
*/
|
||||
pragma[nomagic]
|
||||
private predicate fwdFlowThroughOutOfCall(
|
||||
DataFlowCall call, CcCall ccc, RetNodeEx ret, NodeEx out, boolean allowsFieldFlow,
|
||||
ApApprox argApa, ApApprox apa, Configuration config
|
||||
) {
|
||||
fwdFlowInMayFlowThroughProj(call, ccc, argApa, config) and
|
||||
flowThroughOutOfCall(call, ccc, ret, out, allowsFieldFlow, argApa, apa, config)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate fwdFlowOutFromArg(
|
||||
DataFlowCall call, NodeEx out, FlowState state, ParameterPosition summaryCtx, Ap argAp, Ap ap,
|
||||
ApApprox apa, Configuration config
|
||||
Configuration config
|
||||
) {
|
||||
exists(RetNodeEx ret, boolean allowsFieldFlow, CcCall ccc, ApApprox argApa |
|
||||
fwdFlowRetFromArg(pragma[only_bind_into](ret), state, pragma[only_bind_into](ccc),
|
||||
summaryCtx, _, argAp, pragma[only_bind_into](argApa), ap, pragma[only_bind_into](apa),
|
||||
exists(
|
||||
DataFlowCallable c, RetNodeEx ret, ReturnKindExt kind, boolean allowsFieldFlow, CcCall ccc
|
||||
|
|
||||
fwdFlow(pragma[only_bind_into](ret), state, pragma[only_bind_into](ccc),
|
||||
TParameterPositionSome(pragma[only_bind_into](summaryCtx)), apSome(argAp), ap, config) and
|
||||
flowThroughOutOfCall(call, pragma[only_bind_into](c), ccc, ret, kind, out, allowsFieldFlow,
|
||||
config) and
|
||||
fwdFlowThroughOutOfCall(call, ccc, ret, out, allowsFieldFlow, argApa, apa, config) and
|
||||
(if allowsFieldFlow = false then ap instanceof ApNil else any())
|
||||
(if allowsFieldFlow = false then ap instanceof ApNil else any()) and
|
||||
parameterFlowThroughAllowed(c, pragma[only_bind_into](summaryCtx), kind)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1575,7 +1483,8 @@ private module MkStage<StageSig PrevStage> {
|
||||
ParameterPosition pos, Ap ap, Configuration config
|
||||
) {
|
||||
exists(ParamNodeEx param |
|
||||
fwdFlowInMayFlowThrough(call, cc, _, summaryCtx, argAp, param, ap, _, config) and
|
||||
fwdFlowIn(call, param, _, cc, _, summaryCtx, argAp, ap, config) and
|
||||
PrevStage::parameterMayFlowThrough(param, unbindApa(getApprox(ap)), config) and
|
||||
pos = param.getPosition()
|
||||
)
|
||||
}
|
||||
@@ -1596,55 +1505,41 @@ private module MkStage<StageSig PrevStage> {
|
||||
fwdFlowConsCand(ap1, c, ap2, config)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate returnFlowsThrough0(
|
||||
RetNodeEx ret, ReturnKindExt kind, FlowState state, CcCall ccc, DataFlowCallable c,
|
||||
ParameterPosition ppos, Ap argAp, Ap ap, Configuration config
|
||||
) {
|
||||
exists(boolean allowsFieldFlow |
|
||||
fwdFlow(ret, state, ccc, TParameterPositionSome(ppos), apSome(argAp), ap, config) and
|
||||
flowThroughOutOfCall(_, c, _, pragma[only_bind_into](ret), kind, _, allowsFieldFlow,
|
||||
pragma[only_bind_into](config)) and
|
||||
if allowsFieldFlow = false then ap instanceof ApNil else any()
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate returnFlowsThrough(
|
||||
RetNodeEx ret, ReturnKindExt kind, FlowState state, CcCall ccc, ParamNodeEx p, Ap argAp,
|
||||
Ap ap, Configuration config
|
||||
) {
|
||||
exists(boolean allowsFieldFlow, ApApprox argApa, ApApprox apa |
|
||||
fwdFlowRetFromArg(pragma[only_bind_into](ret), state, pragma[only_bind_into](ccc), _, p,
|
||||
argAp, pragma[only_bind_into](argApa), ap, pragma[only_bind_into](apa), config) and
|
||||
kind = ret.getKind() and
|
||||
fwdFlowThroughOutOfCall(_, ccc, ret, _, allowsFieldFlow, argApa, apa, config) and
|
||||
(if allowsFieldFlow = false then ap instanceof ApNil else any())
|
||||
exists(DataFlowCallable c, ParameterPosition ppos |
|
||||
returnFlowsThrough0(ret, kind, state, ccc, c, ppos, argAp, ap, config) and
|
||||
p.isParameterOf(c, ppos) and
|
||||
parameterFlowThroughAllowed(p, kind)
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate flowThroughIntoCall(
|
||||
DataFlowCall call, ArgNodeEx arg, ParamNodeEx p, boolean allowsFieldFlow, Ap argAp,
|
||||
Configuration config
|
||||
DataFlowCall call, ArgNodeEx arg, ParamNodeEx p, boolean allowsFieldFlow, Configuration config
|
||||
) {
|
||||
exists(ApApprox argApa |
|
||||
flowIntoCallApa(call, pragma[only_bind_into](arg), pragma[only_bind_into](p),
|
||||
allowsFieldFlow, argApa, pragma[only_bind_into](config)) and
|
||||
fwdFlow(arg, _, _, _, _, pragma[only_bind_into](argAp), argApa,
|
||||
exists(Ap argAp |
|
||||
flowIntoCall(call, pragma[only_bind_into](arg), pragma[only_bind_into](p), allowsFieldFlow,
|
||||
pragma[only_bind_into](config)) and
|
||||
fwdFlow(arg, _, _, _, _, pragma[only_bind_into](argAp), pragma[only_bind_into](config)) and
|
||||
returnFlowsThrough(_, _, _, _, p, pragma[only_bind_into](argAp), _,
|
||||
pragma[only_bind_into](config)) and
|
||||
if allowsFieldFlow = false then argAp instanceof ApNil else any()
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate flowIntoCallAp(
|
||||
DataFlowCall call, ArgNodeEx arg, ParamNodeEx p, boolean allowsFieldFlow, Ap ap,
|
||||
Configuration config
|
||||
) {
|
||||
exists(ApApprox apa |
|
||||
flowIntoCallApa(call, arg, p, allowsFieldFlow, apa, config) and
|
||||
fwdFlow(arg, _, _, _, _, ap, apa, config)
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate flowOutOfCallAp(
|
||||
DataFlowCall call, RetNodeEx ret, ReturnKindExt kind, NodeEx out, boolean allowsFieldFlow,
|
||||
Ap ap, Configuration config
|
||||
) {
|
||||
exists(ApApprox apa |
|
||||
flowOutOfCallApa(call, ret, kind, out, allowsFieldFlow, apa, config) and
|
||||
fwdFlow(ret, _, _, _, _, ap, apa, config)
|
||||
pragma[only_bind_into](config))
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1733,7 +1628,7 @@ private module MkStage<StageSig PrevStage> {
|
||||
// flow into a callable
|
||||
exists(ParamNodeEx p, boolean allowsFieldFlow |
|
||||
revFlow(p, state, TReturnCtxNone(), returnAp, ap, config) and
|
||||
flowIntoCallAp(_, node, p, allowsFieldFlow, ap, config) and
|
||||
flowIntoCall(_, node, p, allowsFieldFlow, config) and
|
||||
(if allowsFieldFlow = false then ap instanceof ApNil else any()) and
|
||||
returnCtx = TReturnCtxNone()
|
||||
)
|
||||
@@ -1787,41 +1682,22 @@ private module MkStage<StageSig PrevStage> {
|
||||
) {
|
||||
exists(NodeEx out, boolean allowsFieldFlow |
|
||||
revFlow(out, state, returnCtx, returnAp, ap, config) and
|
||||
flowOutOfCallAp(call, ret, kind, out, allowsFieldFlow, ap, config) and
|
||||
flowOutOfCall(call, ret, kind, out, allowsFieldFlow, config) and
|
||||
if allowsFieldFlow = false then ap instanceof ApNil else any()
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Same as `flowThroughIntoCall`, but restricted to calls that are reached
|
||||
* in the flow covered by `revFlow`, where data might flow through the target
|
||||
* callable and back out at `call`.
|
||||
*/
|
||||
pragma[nomagic]
|
||||
private predicate revFlowThroughIntoCall(
|
||||
DataFlowCall call, ArgNodeEx arg, ParamNodeEx p, boolean allowsFieldFlow, Ap argAp,
|
||||
Configuration config
|
||||
) {
|
||||
flowThroughIntoCall(call, arg, p, allowsFieldFlow, argAp, config) and
|
||||
revFlowIsReturned(call, _, _, _, _, config)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate revFlowParamToReturn(
|
||||
ParamNodeEx p, FlowState state, ReturnKindExt kind, Ap returnAp, Ap ap, Configuration config
|
||||
) {
|
||||
revFlow(p, state, TReturnCtxMaybeFlowThrough(kind), apSome(returnAp), ap, config) and
|
||||
parameterFlowThroughAllowed(p, kind)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate revFlowInToReturn(
|
||||
DataFlowCall call, ArgNodeEx arg, FlowState state, ReturnKindExt kind, Ap returnAp, Ap ap,
|
||||
Configuration config
|
||||
) {
|
||||
exists(ParamNodeEx p, boolean allowsFieldFlow |
|
||||
revFlowParamToReturn(p, state, kind, returnAp, ap, config) and
|
||||
revFlowThroughIntoCall(call, arg, p, allowsFieldFlow, ap, config)
|
||||
revFlow(pragma[only_bind_into](p), state,
|
||||
TReturnCtxMaybeFlowThrough(pragma[only_bind_into](kind)), apSome(returnAp), ap, config) and
|
||||
flowThroughIntoCall(call, arg, p, allowsFieldFlow, config) and
|
||||
(if allowsFieldFlow = false then ap instanceof ApNil else any()) and
|
||||
parameterFlowThroughAllowed(p, kind)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1874,11 +1750,6 @@ private module MkStage<StageSig PrevStage> {
|
||||
pragma[nomagic]
|
||||
predicate revFlow(NodeEx node, Configuration config) { revFlow(node, _, _, _, _, config) }
|
||||
|
||||
pragma[nomagic]
|
||||
predicate revFlowAp(NodeEx node, Ap ap, Configuration config) {
|
||||
revFlow(node, _, _, _, ap, config)
|
||||
}
|
||||
|
||||
// use an alias as a workaround for bad functionality-induced joins
|
||||
pragma[nomagic]
|
||||
additional predicate revFlowAlias(NodeEx node, Configuration config) {
|
||||
@@ -1915,9 +1786,9 @@ private module MkStage<StageSig PrevStage> {
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate parameterFlowsThroughRev(
|
||||
ParamNodeEx p, Ap ap, ReturnKindExt kind, Ap returnAp, Configuration config
|
||||
ParamNodeEx p, Ap ap, ReturnKindExt kind, Configuration config
|
||||
) {
|
||||
revFlow(p, _, TReturnCtxMaybeFlowThrough(kind), apSome(returnAp), ap, config) and
|
||||
revFlow(p, _, TReturnCtxMaybeFlowThrough(kind), apSome(_), ap, config) and
|
||||
parameterFlowThroughAllowed(p, kind)
|
||||
}
|
||||
|
||||
@@ -1925,36 +1796,27 @@ private module MkStage<StageSig PrevStage> {
|
||||
predicate parameterMayFlowThrough(ParamNodeEx p, Ap ap, Configuration config) {
|
||||
exists(RetNodeEx ret, ReturnKindExt kind |
|
||||
returnFlowsThrough(ret, kind, _, _, p, ap, _, config) and
|
||||
parameterFlowsThroughRev(p, ap, kind, _, config)
|
||||
parameterFlowsThroughRev(p, ap, kind, config)
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
predicate returnMayFlowThrough(
|
||||
RetNodeEx ret, Ap argAp, Ap ap, ReturnKindExt kind, Configuration config
|
||||
) {
|
||||
exists(ParamNodeEx p |
|
||||
returnFlowsThrough(ret, kind, _, _, p, argAp, ap, config) and
|
||||
parameterFlowsThroughRev(p, argAp, kind, ap, config)
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
predicate revFlowInToReturnIsReturned(
|
||||
DataFlowCall call, ArgNodeEx arg, FlowState state, ReturnCtx returnCtx, ApOption returnAp,
|
||||
Ap ap, Configuration config
|
||||
) {
|
||||
exists(ReturnKindExt returnKind0, Ap returnAp0 |
|
||||
revFlowInToReturn(call, arg, state, returnKind0, returnAp0, ap, config) and
|
||||
revFlowIsReturned(call, returnCtx, returnAp, returnKind0, returnAp0, config)
|
||||
predicate returnMayFlowThrough(RetNodeEx ret, ReturnKindExt kind, Configuration config) {
|
||||
exists(ParamNodeEx p, Ap ap |
|
||||
returnFlowsThrough(ret, kind, _, _, p, ap, _, config) and
|
||||
parameterFlowsThroughRev(p, ap, kind, config)
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
predicate callMayFlowThroughRev(DataFlowCall call, Configuration config) {
|
||||
exists(ArgNodeEx arg, FlowState state, ReturnCtx returnCtx, ApOption returnAp, Ap ap |
|
||||
exists(
|
||||
ReturnKindExt returnKind0, Ap returnAp0, ArgNodeEx arg, FlowState state,
|
||||
ReturnCtx returnCtx, ApOption returnAp, Ap ap
|
||||
|
|
||||
revFlow(arg, state, returnCtx, returnAp, ap, config) and
|
||||
revFlowInToReturnIsReturned(call, arg, state, returnCtx, returnAp, ap, config)
|
||||
revFlowInToReturn(call, arg, state, returnKind0, returnAp0, ap, config) and
|
||||
revFlowIsReturned(call, returnCtx, returnAp, returnKind0, returnAp0, config)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -2317,16 +2179,16 @@ private module LocalFlowBigStep {
|
||||
pragma[nomagic]
|
||||
predicate localFlowBigStep(
|
||||
NodeEx node1, FlowState state1, NodeEx node2, FlowState state2, boolean preservesValue,
|
||||
DataFlowType t, Configuration config, LocalCallContext callContext
|
||||
AccessPathFrontNil apf, Configuration config, LocalCallContext callContext
|
||||
) {
|
||||
localFlowStepPlus(node1, state1, node2, preservesValue, t, config, callContext) and
|
||||
localFlowStepPlus(node1, state1, node2, preservesValue, apf.getType(), config, callContext) and
|
||||
localFlowExit(node2, state1, config) and
|
||||
state1 = state2
|
||||
or
|
||||
additionalLocalFlowStepNodeCand2(node1, state1, node2, state2, config) and
|
||||
state1 != state2 and
|
||||
preservesValue = false and
|
||||
t = node2.getDataFlowType() and
|
||||
apf = TFrontNil(node2.getDataFlowType()) and
|
||||
callContext.relevantFor(node1.getEnclosingCallable()) and
|
||||
not exists(DataFlowCall call | call = callContext.(LocalCallContextSpecificCall).getCall() |
|
||||
isUnreachableInCallCached(node1.asNode(), call) or
|
||||
@@ -2340,87 +2202,11 @@ private import LocalFlowBigStep
|
||||
private module Stage3Param implements MkStage<Stage2>::StageParam {
|
||||
private module PrevStage = Stage2;
|
||||
|
||||
class Ap = ApproxAccessPathFront;
|
||||
|
||||
class ApNil = ApproxAccessPathFrontNil;
|
||||
|
||||
PrevStage::Ap getApprox(Ap ap) { result = ap.toBoolNonEmpty() }
|
||||
|
||||
ApNil getApNil(NodeEx node) {
|
||||
PrevStage::revFlow(node, _) and result = TApproxFrontNil(node.getDataFlowType())
|
||||
}
|
||||
|
||||
bindingset[tc, tail]
|
||||
Ap apCons(TypedContent tc, Ap tail) { result.getAHead() = tc and exists(tail) }
|
||||
|
||||
pragma[noinline]
|
||||
Content getHeadContent(Ap ap) { result = ap.getAHead().getContent() }
|
||||
|
||||
class ApOption = ApproxAccessPathFrontOption;
|
||||
|
||||
ApOption apNone() { result = TApproxAccessPathFrontNone() }
|
||||
|
||||
ApOption apSome(Ap ap) { result = TApproxAccessPathFrontSome(ap) }
|
||||
|
||||
import BooleanCallContext
|
||||
|
||||
predicate localStep(
|
||||
NodeEx node1, FlowState state1, NodeEx node2, FlowState state2, boolean preservesValue,
|
||||
ApproxAccessPathFrontNil ap, Configuration config, LocalCc lcc
|
||||
) {
|
||||
localFlowBigStep(node1, state1, node2, state2, preservesValue, ap.getType(), config, _) and
|
||||
exists(lcc)
|
||||
}
|
||||
|
||||
predicate flowOutOfCall = flowOutOfCallNodeCand2/6;
|
||||
|
||||
predicate flowIntoCall = flowIntoCallNodeCand2/5;
|
||||
|
||||
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.getAHead().getContent()
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate castingNodeEx(NodeEx node) { node.asNode() instanceof CastingNode }
|
||||
|
||||
bindingset[node, state, ap, config]
|
||||
predicate filter(NodeEx node, FlowState state, Ap ap, Configuration config) {
|
||||
exists(state) and
|
||||
exists(config) and
|
||||
(if castingNodeEx(node) then compatibleTypes(node.getDataFlowType(), ap.getType()) else any()) and
|
||||
(
|
||||
notExpectsContent(node)
|
||||
or
|
||||
expectsContentCand(node, ap, config)
|
||||
)
|
||||
}
|
||||
|
||||
bindingset[ap, contentType]
|
||||
predicate typecheckStore(Ap ap, DataFlowType contentType) {
|
||||
// We need to typecheck stores here, since reverse flow through a getter
|
||||
// might have a different type here compared to inside the getter.
|
||||
compatibleTypes(ap.getType(), contentType)
|
||||
}
|
||||
}
|
||||
|
||||
private module Stage3 implements StageSig {
|
||||
import MkStage<Stage2>::Stage<Stage3Param>
|
||||
}
|
||||
|
||||
private module Stage4Param implements MkStage<Stage3>::StageParam {
|
||||
private module PrevStage = Stage3;
|
||||
|
||||
class Ap = AccessPathFront;
|
||||
|
||||
class ApNil = AccessPathFrontNil;
|
||||
|
||||
PrevStage::Ap getApprox(Ap ap) { result = ap.toApprox() }
|
||||
PrevStage::Ap getApprox(Ap ap) { result = ap.toBoolNonEmpty() }
|
||||
|
||||
ApNil getApNil(NodeEx node) {
|
||||
PrevStage::revFlow(node, _) and result = TFrontNil(node.getDataFlowType())
|
||||
@@ -2440,42 +2226,16 @@ private module Stage4Param implements MkStage<Stage3>::StageParam {
|
||||
|
||||
import BooleanCallContext
|
||||
|
||||
pragma[nomagic]
|
||||
predicate localStep(
|
||||
NodeEx node1, FlowState state1, NodeEx node2, FlowState state2, boolean preservesValue,
|
||||
ApNil ap, Configuration config, LocalCc lcc
|
||||
) {
|
||||
localFlowBigStep(node1, state1, node2, state2, preservesValue, ap.getType(), config, _) and
|
||||
PrevStage::revFlow(node1, pragma[only_bind_into](state1), _, pragma[only_bind_into](config)) and
|
||||
PrevStage::revFlowAlias(node2, pragma[only_bind_into](state2), _, pragma[only_bind_into](config)) and
|
||||
exists(lcc)
|
||||
localFlowBigStep(node1, state1, node2, state2, preservesValue, ap, config, _) and exists(lcc)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
predicate flowOutOfCall(
|
||||
DataFlowCall call, RetNodeEx node1, ReturnKindExt kind, NodeEx node2, boolean allowsFieldFlow,
|
||||
Configuration config
|
||||
) {
|
||||
exists(FlowState state |
|
||||
flowOutOfCallNodeCand2(call, node1, kind, node2, allowsFieldFlow, config) and
|
||||
PrevStage::revFlow(node2, pragma[only_bind_into](state), _, pragma[only_bind_into](config)) and
|
||||
PrevStage::revFlowAlias(node1, pragma[only_bind_into](state), _,
|
||||
pragma[only_bind_into](config))
|
||||
)
|
||||
}
|
||||
predicate flowOutOfCall = flowOutOfCallNodeCand2/6;
|
||||
|
||||
pragma[nomagic]
|
||||
predicate flowIntoCall(
|
||||
DataFlowCall call, ArgNodeEx node1, ParamNodeEx node2, boolean allowsFieldFlow,
|
||||
Configuration config
|
||||
) {
|
||||
exists(FlowState state |
|
||||
flowIntoCallNodeCand2(call, node1, node2, allowsFieldFlow, config) and
|
||||
PrevStage::revFlow(node2, pragma[only_bind_into](state), _, pragma[only_bind_into](config)) and
|
||||
PrevStage::revFlowAlias(node1, pragma[only_bind_into](state), _,
|
||||
pragma[only_bind_into](config))
|
||||
)
|
||||
}
|
||||
predicate flowIntoCall = flowIntoCallNodeCand2/5;
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate clearSet(NodeEx node, ContentSet c, Configuration config) {
|
||||
@@ -2531,8 +2291,8 @@ private module Stage4Param implements MkStage<Stage3>::StageParam {
|
||||
}
|
||||
}
|
||||
|
||||
private module Stage4 implements StageSig {
|
||||
import MkStage<Stage3>::Stage<Stage4Param>
|
||||
private module Stage3 implements StageSig {
|
||||
import MkStage<Stage2>::Stage<Stage3Param>
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -2543,8 +2303,8 @@ private predicate flowCandSummaryCtx(
|
||||
NodeEx node, FlowState state, AccessPathFront argApf, Configuration config
|
||||
) {
|
||||
exists(AccessPathFront apf |
|
||||
Stage4::revFlow(node, state, TReturnCtxMaybeFlowThrough(_), _, apf, config) and
|
||||
Stage4::fwdFlow(node, state, any(Stage4::CcCall ccc), _, TAccessPathFrontSome(argApf), apf,
|
||||
Stage3::revFlow(node, state, TReturnCtxMaybeFlowThrough(_), _, apf, config) and
|
||||
Stage3::fwdFlow(node, state, any(Stage3::CcCall ccc), _, TAccessPathFrontSome(argApf), apf,
|
||||
config)
|
||||
)
|
||||
}
|
||||
@@ -2555,10 +2315,10 @@ private predicate flowCandSummaryCtx(
|
||||
*/
|
||||
private predicate expensiveLen2unfolding(TypedContent tc, Configuration config) {
|
||||
exists(int tails, int nodes, int apLimit, int tupleLimit |
|
||||
tails = strictcount(AccessPathFront apf | Stage4::consCand(tc, apf, config)) and
|
||||
tails = strictcount(AccessPathFront apf | Stage3::consCand(tc, apf, config)) and
|
||||
nodes =
|
||||
strictcount(NodeEx n, FlowState state |
|
||||
Stage4::revFlow(n, state, any(AccessPathFrontHead apf | apf.getHead() = tc), config)
|
||||
Stage3::revFlow(n, state, any(AccessPathFrontHead apf | apf.getHead() = tc), config)
|
||||
or
|
||||
flowCandSummaryCtx(n, state, any(AccessPathFrontHead apf | apf.getHead() = tc), config)
|
||||
) and
|
||||
@@ -2572,11 +2332,11 @@ private predicate expensiveLen2unfolding(TypedContent tc, Configuration config)
|
||||
private newtype TAccessPathApprox =
|
||||
TNil(DataFlowType t) or
|
||||
TConsNil(TypedContent tc, DataFlowType t) {
|
||||
Stage4::consCand(tc, TFrontNil(t), _) and
|
||||
Stage3::consCand(tc, TFrontNil(t), _) and
|
||||
not expensiveLen2unfolding(tc, _)
|
||||
} or
|
||||
TConsCons(TypedContent tc1, TypedContent tc2, int len) {
|
||||
Stage4::consCand(tc1, TFrontHead(tc2), _) and
|
||||
Stage3::consCand(tc1, TFrontHead(tc2), _) and
|
||||
len in [2 .. accessPathLimit()] and
|
||||
not expensiveLen2unfolding(tc1, _)
|
||||
} or
|
||||
@@ -2706,7 +2466,7 @@ private class AccessPathApproxCons1 extends AccessPathApproxCons, TCons1 {
|
||||
override AccessPathApprox pop(TypedContent head) {
|
||||
head = tc and
|
||||
(
|
||||
exists(TypedContent tc2 | Stage4::consCand(tc, TFrontHead(tc2), _) |
|
||||
exists(TypedContent tc2 | Stage3::consCand(tc, TFrontHead(tc2), _) |
|
||||
result = TConsCons(tc2, _, len - 1)
|
||||
or
|
||||
len = 2 and
|
||||
@@ -2717,7 +2477,7 @@ private class AccessPathApproxCons1 extends AccessPathApproxCons, TCons1 {
|
||||
or
|
||||
exists(DataFlowType t |
|
||||
len = 1 and
|
||||
Stage4::consCand(tc, TFrontNil(t), _) and
|
||||
Stage3::consCand(tc, TFrontNil(t), _) and
|
||||
result = TNil(t)
|
||||
)
|
||||
)
|
||||
@@ -2742,14 +2502,13 @@ private class AccessPathApproxOption extends TAccessPathApproxOption {
|
||||
}
|
||||
}
|
||||
|
||||
private module Stage5Param implements MkStage<Stage4>::StageParam {
|
||||
private module PrevStage = Stage4;
|
||||
private module Stage4Param implements MkStage<Stage3>::StageParam {
|
||||
private module PrevStage = Stage3;
|
||||
|
||||
class Ap = AccessPathApprox;
|
||||
|
||||
class ApNil = AccessPathApproxNil;
|
||||
|
||||
pragma[nomagic]
|
||||
PrevStage::Ap getApprox(Ap ap) { result = ap.getFront() }
|
||||
|
||||
ApNil getApNil(NodeEx node) {
|
||||
@@ -2775,9 +2534,7 @@ private module Stage5Param implements MkStage<Stage4>::StageParam {
|
||||
NodeEx node1, FlowState state1, NodeEx node2, FlowState state2, boolean preservesValue,
|
||||
ApNil ap, Configuration config, LocalCc lcc
|
||||
) {
|
||||
localFlowBigStep(node1, state1, node2, state2, preservesValue, ap.getType(), config, lcc) and
|
||||
PrevStage::revFlow(node1, pragma[only_bind_into](state1), _, pragma[only_bind_into](config)) and
|
||||
PrevStage::revFlowAlias(node2, pragma[only_bind_into](state2), _, pragma[only_bind_into](config))
|
||||
localFlowBigStep(node1, state1, node2, state2, preservesValue, ap.getFront(), config, lcc)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
@@ -2814,7 +2571,7 @@ private module Stage5Param implements MkStage<Stage4>::StageParam {
|
||||
predicate typecheckStore(Ap ap, DataFlowType contentType) { any() }
|
||||
}
|
||||
|
||||
private module Stage5 = MkStage<Stage4>::Stage<Stage5Param>;
|
||||
private module Stage4 = MkStage<Stage3>::Stage<Stage4Param>;
|
||||
|
||||
bindingset[conf, result]
|
||||
private Configuration unbindConf(Configuration conf) {
|
||||
@@ -2828,8 +2585,8 @@ private predicate nodeMayUseSummary0(
|
||||
) {
|
||||
exists(AccessPathApprox apa0 |
|
||||
c = n.getEnclosingCallable() and
|
||||
Stage5::revFlow(n, state, TReturnCtxMaybeFlowThrough(_), _, apa0, config) and
|
||||
Stage5::fwdFlow(n, state, any(CallContextCall ccc), TParameterPositionSome(pos),
|
||||
Stage4::revFlow(n, state, TReturnCtxMaybeFlowThrough(_), _, apa0, config) and
|
||||
Stage4::fwdFlow(n, state, any(CallContextCall ccc), TParameterPositionSome(pos),
|
||||
TAccessPathApproxSome(apa), apa0, config)
|
||||
)
|
||||
}
|
||||
@@ -2839,7 +2596,7 @@ private predicate nodeMayUseSummary(
|
||||
NodeEx n, FlowState state, AccessPathApprox apa, Configuration config
|
||||
) {
|
||||
exists(DataFlowCallable c, ParameterPosition pos, ParamNodeEx p |
|
||||
Stage5::parameterMayFlowThrough(p, apa, config) and
|
||||
Stage4::parameterMayFlowThrough(p, apa, config) and
|
||||
nodeMayUseSummary0(n, c, pos, state, apa, config) and
|
||||
p.isParameterOf(c, pos)
|
||||
)
|
||||
@@ -2849,8 +2606,8 @@ private newtype TSummaryCtx =
|
||||
TSummaryCtxNone() or
|
||||
TSummaryCtxSome(ParamNodeEx p, FlowState state, AccessPath ap) {
|
||||
exists(Configuration config |
|
||||
Stage5::parameterMayFlowThrough(p, ap.getApprox(), config) and
|
||||
Stage5::revFlow(p, state, _, config)
|
||||
Stage4::parameterMayFlowThrough(p, ap.getApprox(), config) and
|
||||
Stage4::revFlow(p, state, _, config)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -2899,7 +2656,7 @@ private int count1to2unfold(AccessPathApproxCons1 apa, Configuration config) {
|
||||
len = apa.len() and
|
||||
result =
|
||||
strictcount(AccessPathFront apf |
|
||||
Stage5::consCand(tc, any(AccessPathApprox ap | ap.getFront() = apf and ap.len() = len - 1),
|
||||
Stage4::consCand(tc, any(AccessPathApprox ap | ap.getFront() = apf and ap.len() = len - 1),
|
||||
config)
|
||||
)
|
||||
)
|
||||
@@ -2908,7 +2665,7 @@ private int count1to2unfold(AccessPathApproxCons1 apa, Configuration config) {
|
||||
private int countNodesUsingAccessPath(AccessPathApprox apa, Configuration config) {
|
||||
result =
|
||||
strictcount(NodeEx n, FlowState state |
|
||||
Stage5::revFlow(n, state, apa, config) or nodeMayUseSummary(n, state, apa, config)
|
||||
Stage4::revFlow(n, state, apa, config) or nodeMayUseSummary(n, state, apa, config)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -2929,7 +2686,7 @@ private predicate expensiveLen1to2unfolding(AccessPathApproxCons1 apa, Configura
|
||||
private AccessPathApprox getATail(AccessPathApprox apa, Configuration config) {
|
||||
exists(TypedContent head |
|
||||
apa.pop(head) = result and
|
||||
Stage5::consCand(head, result, config)
|
||||
Stage4::consCand(head, result, config)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -3011,7 +2768,7 @@ private newtype TPathNode =
|
||||
NodeEx node, FlowState state, CallContext cc, SummaryCtx sc, AccessPath ap, Configuration config
|
||||
) {
|
||||
// A PathNode is introduced by a source ...
|
||||
Stage5::revFlow(node, state, config) and
|
||||
Stage4::revFlow(node, state, config) and
|
||||
sourceNode(node, state, config) and
|
||||
(
|
||||
if hasSourceCallCtx(config)
|
||||
@@ -3025,7 +2782,7 @@ private newtype TPathNode =
|
||||
exists(PathNodeMid mid |
|
||||
pathStep(mid, node, state, cc, sc, ap) and
|
||||
pragma[only_bind_into](config) = mid.getConfiguration() and
|
||||
Stage5::revFlow(node, state, ap.getApprox(), pragma[only_bind_into](config))
|
||||
Stage4::revFlow(node, state, ap.getApprox(), pragma[only_bind_into](config))
|
||||
)
|
||||
} or
|
||||
TPathNodeSink(NodeEx node, FlowState state, Configuration config) {
|
||||
@@ -3167,7 +2924,7 @@ private class AccessPathCons2 extends AccessPath, TAccessPathCons2 {
|
||||
override TypedContent getHead() { result = head1 }
|
||||
|
||||
override AccessPath getTail() {
|
||||
Stage5::consCand(head1, result.getApprox(), _) and
|
||||
Stage4::consCand(head1, result.getApprox(), _) and
|
||||
result.getHead() = head2 and
|
||||
result.length() = len - 1
|
||||
}
|
||||
@@ -3198,7 +2955,7 @@ private class AccessPathCons1 extends AccessPath, TAccessPathCons1 {
|
||||
override TypedContent getHead() { result = head }
|
||||
|
||||
override AccessPath getTail() {
|
||||
Stage5::consCand(head, result.getApprox(), _) and result.length() = len - 1
|
||||
Stage4::consCand(head, result.getApprox(), _) and result.length() = len - 1
|
||||
}
|
||||
|
||||
override AccessPathFrontHead getFront() { result = TFrontHead(head) }
|
||||
@@ -3590,8 +3347,7 @@ private predicate pathStep(
|
||||
AccessPath ap0, NodeEx midnode, FlowState state0, Configuration conf, LocalCallContext localCC
|
||||
|
|
||||
pathNode(mid, midnode, state0, cc, sc, ap0, conf, localCC) and
|
||||
localFlowBigStep(midnode, state0, node, state, false, ap.(AccessPathNil).getType(), conf,
|
||||
localCC) and
|
||||
localFlowBigStep(midnode, state0, node, state, false, ap.getFront(), conf, localCC) and
|
||||
ap0 instanceof AccessPathNil
|
||||
)
|
||||
or
|
||||
@@ -3633,7 +3389,7 @@ private predicate pathReadStep(
|
||||
) {
|
||||
ap0 = mid.getAp() and
|
||||
tc = ap0.getHead() and
|
||||
Stage5::readStepCand(mid.getNodeEx(), tc.getContent(), node, mid.getConfiguration()) and
|
||||
Stage4::readStepCand(mid.getNodeEx(), tc.getContent(), node, mid.getConfiguration()) and
|
||||
state = mid.getState() and
|
||||
cc = mid.getCallContext()
|
||||
}
|
||||
@@ -3643,7 +3399,7 @@ private predicate pathStoreStep(
|
||||
PathNodeMid mid, NodeEx node, FlowState state, AccessPath ap0, TypedContent tc, CallContext cc
|
||||
) {
|
||||
ap0 = mid.getAp() and
|
||||
Stage5::storeStepCand(mid.getNodeEx(), _, tc, node, _, mid.getConfiguration()) and
|
||||
Stage4::storeStepCand(mid.getNodeEx(), _, tc, node, _, mid.getConfiguration()) and
|
||||
state = mid.getState() and
|
||||
cc = mid.getCallContext()
|
||||
}
|
||||
@@ -3680,7 +3436,7 @@ private NodeEx getAnOutNodeFlow(
|
||||
ReturnKindExt kind, DataFlowCall call, AccessPathApprox apa, Configuration config
|
||||
) {
|
||||
result.asNode() = kind.getAnOutNode(call) and
|
||||
Stage5::revFlow(result, _, apa, config)
|
||||
Stage4::revFlow(result, _, apa, config)
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -3716,7 +3472,7 @@ private predicate parameterCand(
|
||||
DataFlowCallable callable, ParameterPosition pos, AccessPathApprox apa, Configuration config
|
||||
) {
|
||||
exists(ParamNodeEx p |
|
||||
Stage5::revFlow(p, _, apa, config) and
|
||||
Stage4::revFlow(p, _, apa, config) and
|
||||
p.isParameterOf(callable, pos)
|
||||
)
|
||||
}
|
||||
@@ -3995,17 +3751,9 @@ predicate stageStats(
|
||||
n = 45 and
|
||||
Stage4::stats(false, nodes, fields, conscand, states, tuples, config)
|
||||
or
|
||||
stage = "5 Fwd" and
|
||||
n = 50 and
|
||||
Stage5::stats(true, nodes, fields, conscand, states, tuples, config)
|
||||
stage = "5 Fwd" and n = 50 and finalStats(true, nodes, fields, conscand, states, tuples)
|
||||
or
|
||||
stage = "5 Rev" and
|
||||
n = 55 and
|
||||
Stage5::stats(false, nodes, fields, conscand, states, tuples, config)
|
||||
or
|
||||
stage = "6 Fwd" and n = 60 and finalStats(true, nodes, fields, conscand, states, tuples)
|
||||
or
|
||||
stage = "6 Rev" and n = 65 and finalStats(false, nodes, fields, conscand, states, tuples)
|
||||
stage = "5 Rev" and n = 55 and finalStats(false, nodes, fields, conscand, states, tuples)
|
||||
}
|
||||
|
||||
private module FlowExploration {
|
||||
|
||||
@@ -621,6 +621,23 @@ private predicate parameterFlowThroughAllowed(ParamNodeEx p, ReturnKindExt kind)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if flow from a parameter at position `pos` inside `c` to a return node of
|
||||
* kind `kind` is allowed.
|
||||
*
|
||||
* We don't expect a parameter to return stored in itself, unless
|
||||
* explicitly allowed
|
||||
*/
|
||||
bindingset[c, pos, kind]
|
||||
private predicate parameterFlowThroughAllowed(
|
||||
DataFlowCallable c, ParameterPosition pos, ReturnKindExt kind
|
||||
) {
|
||||
exists(ParamNodeEx p |
|
||||
p.isParameterOf(c, pos) and
|
||||
parameterFlowThroughAllowed(p, kind)
|
||||
)
|
||||
}
|
||||
|
||||
private module Stage1 implements StageSig {
|
||||
class Ap = Unit;
|
||||
|
||||
@@ -963,12 +980,6 @@ private module Stage1 implements StageSig {
|
||||
pragma[nomagic]
|
||||
predicate revFlow(NodeEx node, Configuration config) { revFlow(node, _, config) }
|
||||
|
||||
pragma[nomagic]
|
||||
predicate revFlowAp(NodeEx node, Ap ap, Configuration config) {
|
||||
revFlow(node, config) and
|
||||
exists(ap)
|
||||
}
|
||||
|
||||
bindingset[node, state, config]
|
||||
predicate revFlow(NodeEx node, FlowState state, Ap ap, Configuration config) {
|
||||
revFlow(node, _, pragma[only_bind_into](config)) and
|
||||
@@ -1011,13 +1022,9 @@ private module Stage1 implements StageSig {
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
predicate returnMayFlowThrough(
|
||||
RetNodeEx ret, Ap argAp, Ap ap, ReturnKindExt kind, Configuration config
|
||||
) {
|
||||
predicate returnMayFlowThrough(RetNodeEx ret, ReturnKindExt kind, Configuration config) {
|
||||
throughFlowNodeCand(ret, config) and
|
||||
kind = ret.getKind() and
|
||||
exists(argAp) and
|
||||
exists(ap)
|
||||
kind = ret.getKind()
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
@@ -1182,8 +1189,6 @@ private signature module StageSig {
|
||||
|
||||
predicate revFlow(NodeEx node, Configuration config);
|
||||
|
||||
predicate revFlowAp(NodeEx node, Ap ap, Configuration config);
|
||||
|
||||
bindingset[node, state, config]
|
||||
predicate revFlow(NodeEx node, FlowState state, Ap ap, Configuration config);
|
||||
|
||||
@@ -1191,9 +1196,7 @@ private signature module StageSig {
|
||||
|
||||
predicate parameterMayFlowThrough(ParamNodeEx p, Ap ap, Configuration config);
|
||||
|
||||
predicate returnMayFlowThrough(
|
||||
RetNodeEx ret, Ap argAp, Ap ap, ReturnKindExt kind, Configuration config
|
||||
);
|
||||
predicate returnMayFlowThrough(RetNodeEx ret, ReturnKindExt kind, Configuration config);
|
||||
|
||||
predicate storeStepCand(
|
||||
NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, DataFlowType contentType,
|
||||
@@ -1278,43 +1281,21 @@ private module MkStage<StageSig PrevStage> {
|
||||
import Param
|
||||
|
||||
/* Begin: Stage logic. */
|
||||
// use an alias as a workaround for bad functionality-induced joins
|
||||
pragma[nomagic]
|
||||
private predicate revFlowApAlias(NodeEx node, ApApprox apa, Configuration config) {
|
||||
PrevStage::revFlowAp(node, apa, config)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate flowIntoCallApa(
|
||||
DataFlowCall call, ArgNodeEx arg, ParamNodeEx p, boolean allowsFieldFlow, ApApprox apa,
|
||||
Configuration config
|
||||
) {
|
||||
flowIntoCall(call, arg, p, allowsFieldFlow, config) and
|
||||
PrevStage::revFlowAp(p, pragma[only_bind_into](apa), pragma[only_bind_into](config)) and
|
||||
revFlowApAlias(arg, pragma[only_bind_into](apa), pragma[only_bind_into](config))
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate flowOutOfCallApa(
|
||||
DataFlowCall call, RetNodeEx ret, ReturnKindExt kind, NodeEx out, boolean allowsFieldFlow,
|
||||
ApApprox apa, Configuration config
|
||||
) {
|
||||
flowOutOfCall(call, ret, kind, out, allowsFieldFlow, config) and
|
||||
PrevStage::revFlowAp(out, pragma[only_bind_into](apa), pragma[only_bind_into](config)) and
|
||||
revFlowApAlias(ret, pragma[only_bind_into](apa), pragma[only_bind_into](config))
|
||||
bindingset[result, apa]
|
||||
private ApApprox unbindApa(ApApprox apa) {
|
||||
pragma[only_bind_out](apa) = pragma[only_bind_out](result)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate flowThroughOutOfCall(
|
||||
DataFlowCall call, CcCall ccc, RetNodeEx ret, NodeEx out, boolean allowsFieldFlow,
|
||||
ApApprox argApa, ApApprox apa, Configuration config
|
||||
DataFlowCall call, DataFlowCallable c, CcCall ccc, RetNodeEx ret, ReturnKindExt kind,
|
||||
NodeEx out, boolean allowsFieldFlow, Configuration config
|
||||
) {
|
||||
exists(ReturnKindExt kind |
|
||||
flowOutOfCallApa(call, ret, kind, out, allowsFieldFlow, apa, pragma[only_bind_into](config)) and
|
||||
PrevStage::callMayFlowThroughRev(call, pragma[only_bind_into](config)) and
|
||||
PrevStage::returnMayFlowThrough(ret, argApa, apa, kind, pragma[only_bind_into](config)) and
|
||||
matchesCall(ccc, call)
|
||||
)
|
||||
flowOutOfCall(call, ret, kind, out, allowsFieldFlow, pragma[only_bind_into](config)) and
|
||||
PrevStage::callMayFlowThroughRev(call, pragma[only_bind_into](config)) and
|
||||
PrevStage::returnMayFlowThrough(ret, kind, pragma[only_bind_into](config)) and
|
||||
matchesCall(ccc, call) and
|
||||
c = ret.getEnclosingCallable()
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1326,50 +1307,39 @@ private module MkStage<StageSig PrevStage> {
|
||||
* corresponding parameter position and access path of that argument, respectively.
|
||||
*/
|
||||
pragma[nomagic]
|
||||
additional predicate fwdFlow(
|
||||
NodeEx node, FlowState state, Cc cc, ParameterPositionOption summaryCtx, ApOption argAp,
|
||||
Ap ap, ApApprox apa, Configuration config
|
||||
) {
|
||||
fwdFlow0(node, state, cc, summaryCtx, argAp, ap, apa, config) and
|
||||
PrevStage::revFlow(node, state, apa, config) and
|
||||
filter(node, state, ap, config)
|
||||
}
|
||||
|
||||
pragma[inline]
|
||||
additional predicate fwdFlow(
|
||||
NodeEx node, FlowState state, Cc cc, ParameterPositionOption summaryCtx, ApOption argAp,
|
||||
Ap ap, Configuration config
|
||||
) {
|
||||
fwdFlow(node, state, cc, summaryCtx, argAp, ap, _, config)
|
||||
fwdFlow0(node, state, cc, summaryCtx, argAp, ap, config) and
|
||||
PrevStage::revFlow(node, state, unbindApa(getApprox(ap)), config) and
|
||||
filter(node, state, ap, config)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate fwdFlow0(
|
||||
NodeEx node, FlowState state, Cc cc, ParameterPositionOption summaryCtx, ApOption argAp,
|
||||
Ap ap, ApApprox apa, Configuration config
|
||||
Ap ap, Configuration config
|
||||
) {
|
||||
sourceNode(node, state, config) and
|
||||
(if hasSourceCallCtx(config) then cc = ccSomeCall() else cc = ccNone()) and
|
||||
argAp = apNone() and
|
||||
summaryCtx = TParameterPositionNone() and
|
||||
ap = getApNil(node) and
|
||||
apa = getApprox(ap)
|
||||
ap = getApNil(node)
|
||||
or
|
||||
exists(NodeEx mid, FlowState state0, Ap ap0, ApApprox apa0, LocalCc localCc |
|
||||
fwdFlow(mid, state0, cc, summaryCtx, argAp, ap0, apa0, config) and
|
||||
exists(NodeEx mid, FlowState state0, Ap ap0, LocalCc localCc |
|
||||
fwdFlow(mid, state0, cc, summaryCtx, argAp, ap0, config) and
|
||||
localCc = getLocalCc(mid, cc)
|
||||
|
|
||||
localStep(mid, state0, node, state, true, _, config, localCc) and
|
||||
ap = ap0 and
|
||||
apa = apa0
|
||||
ap = ap0
|
||||
or
|
||||
localStep(mid, state0, node, state, false, ap, config, localCc) and
|
||||
ap0 instanceof ApNil and
|
||||
apa = getApprox(ap)
|
||||
ap0 instanceof ApNil
|
||||
)
|
||||
or
|
||||
exists(NodeEx mid |
|
||||
fwdFlow(mid, pragma[only_bind_into](state), _, _, _, ap, apa, pragma[only_bind_into](config)) and
|
||||
fwdFlow(mid, pragma[only_bind_into](state), _, _, _, ap, pragma[only_bind_into](config)) and
|
||||
jumpStep(mid, node, config) and
|
||||
cc = ccNone() and
|
||||
summaryCtx = TParameterPositionNone() and
|
||||
@@ -1382,8 +1352,7 @@ private module MkStage<StageSig PrevStage> {
|
||||
cc = ccNone() and
|
||||
summaryCtx = TParameterPositionNone() and
|
||||
argAp = apNone() and
|
||||
ap = getApNil(node) and
|
||||
apa = getApprox(ap)
|
||||
ap = getApNil(node)
|
||||
)
|
||||
or
|
||||
exists(NodeEx mid, FlowState state0, ApNil nil |
|
||||
@@ -1392,32 +1361,32 @@ private module MkStage<StageSig PrevStage> {
|
||||
cc = ccNone() and
|
||||
summaryCtx = TParameterPositionNone() and
|
||||
argAp = apNone() and
|
||||
ap = getApNil(node) and
|
||||
apa = getApprox(ap)
|
||||
ap = getApNil(node)
|
||||
)
|
||||
or
|
||||
// store
|
||||
exists(TypedContent tc, Ap ap0 |
|
||||
fwdFlowStore(_, ap0, tc, node, state, cc, summaryCtx, argAp, config) and
|
||||
ap = apCons(tc, ap0) and
|
||||
apa = getApprox(ap)
|
||||
ap = apCons(tc, ap0)
|
||||
)
|
||||
or
|
||||
// read
|
||||
exists(Ap ap0, Content c |
|
||||
fwdFlowRead(ap0, c, _, node, state, cc, summaryCtx, argAp, config) and
|
||||
fwdFlowConsCand(ap0, c, ap, config) and
|
||||
apa = getApprox(ap)
|
||||
fwdFlowConsCand(ap0, c, ap, config)
|
||||
)
|
||||
or
|
||||
// flow into a callable
|
||||
fwdFlowIn(_, node, state, _, cc, _, _, ap, apa, config) and
|
||||
if PrevStage::parameterMayFlowThrough(node, apa, config)
|
||||
then (
|
||||
summaryCtx = TParameterPositionSome(node.(ParamNodeEx).getPosition()) and
|
||||
argAp = apSome(ap)
|
||||
) else (
|
||||
summaryCtx = TParameterPositionNone() and argAp = apNone()
|
||||
exists(ApApprox apa |
|
||||
fwdFlowIn(_, node, state, _, cc, _, _, ap, config) and
|
||||
apa = getApprox(ap) and
|
||||
if PrevStage::parameterMayFlowThrough(node, apa, config)
|
||||
then (
|
||||
summaryCtx = TParameterPositionSome(node.(ParamNodeEx).getPosition()) and
|
||||
argAp = apSome(ap)
|
||||
) else (
|
||||
summaryCtx = TParameterPositionNone() and argAp = apNone()
|
||||
)
|
||||
)
|
||||
or
|
||||
// flow out of a callable
|
||||
@@ -1425,8 +1394,8 @@ private module MkStage<StageSig PrevStage> {
|
||||
DataFlowCall call, RetNodeEx ret, boolean allowsFieldFlow, CcNoCall innercc,
|
||||
DataFlowCallable inner
|
||||
|
|
||||
fwdFlow(ret, state, innercc, summaryCtx, argAp, ap, apa, config) and
|
||||
flowOutOfCallApa(call, ret, _, node, allowsFieldFlow, apa, config) and
|
||||
fwdFlow(ret, state, innercc, summaryCtx, argAp, ap, config) and
|
||||
flowOutOfCall(call, ret, _, node, allowsFieldFlow, config) and
|
||||
inner = ret.getEnclosingCallable() and
|
||||
cc = getCallContextReturn(inner, call, innercc) and
|
||||
if allowsFieldFlow = false then ap instanceof ApNil else any()
|
||||
@@ -1434,7 +1403,7 @@ private module MkStage<StageSig PrevStage> {
|
||||
or
|
||||
// flow through a callable
|
||||
exists(DataFlowCall call, ParameterPosition summaryCtx0, Ap argAp0 |
|
||||
fwdFlowOutFromArg(call, node, state, summaryCtx0, argAp0, ap, apa, config) and
|
||||
fwdFlowOutFromArg(call, node, state, summaryCtx0, argAp0, ap, config) and
|
||||
fwdFlowIsEntered(call, cc, summaryCtx, argAp, summaryCtx0, argAp0, config)
|
||||
)
|
||||
}
|
||||
@@ -1444,9 +1413,9 @@ private module MkStage<StageSig PrevStage> {
|
||||
NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, FlowState state, Cc cc,
|
||||
ParameterPositionOption summaryCtx, ApOption argAp, Configuration config
|
||||
) {
|
||||
exists(DataFlowType contentType, ApApprox apa1 |
|
||||
fwdFlow(node1, state, cc, summaryCtx, argAp, ap1, apa1, config) and
|
||||
PrevStage::storeStepCand(node1, apa1, tc, node2, contentType, config) and
|
||||
exists(DataFlowType contentType |
|
||||
fwdFlow(node1, state, cc, summaryCtx, argAp, ap1, config) and
|
||||
PrevStage::storeStepCand(node1, unbindApa(getApprox(ap1)), tc, node2, contentType, config) and
|
||||
typecheckStore(ap1, contentType)
|
||||
)
|
||||
}
|
||||
@@ -1464,104 +1433,43 @@ private module MkStage<StageSig PrevStage> {
|
||||
)
|
||||
}
|
||||
|
||||
private class ApNonNil instanceof Ap {
|
||||
pragma[nomagic]
|
||||
ApNonNil() { not this instanceof ApNil }
|
||||
|
||||
string toString() { result = "" }
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate fwdFlowRead0(
|
||||
NodeEx node1, FlowState state, Cc cc, ParameterPositionOption summaryCtx, ApOption argAp,
|
||||
ApNonNil ap, Configuration config
|
||||
) {
|
||||
fwdFlow(node1, state, cc, summaryCtx, argAp, ap, config) and
|
||||
PrevStage::readStepCand(node1, _, _, config)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate fwdFlowRead(
|
||||
Ap ap, Content c, NodeEx node1, NodeEx node2, FlowState state, Cc cc,
|
||||
ParameterPositionOption summaryCtx, ApOption argAp, Configuration config
|
||||
) {
|
||||
fwdFlowRead0(node1, state, cc, summaryCtx, argAp, ap, config) and
|
||||
fwdFlow(node1, state, cc, summaryCtx, argAp, ap, config) and
|
||||
PrevStage::readStepCand(node1, c, node2, config) and
|
||||
getHeadContent(ap) = c
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate fwdFlowIn(
|
||||
DataFlowCall call, ParamNodeEx p, FlowState state, Cc outercc, CcCall innercc,
|
||||
ParameterPositionOption summaryCtx, ApOption argAp, Ap ap, ApApprox apa, Configuration config
|
||||
DataFlowCall call, ParamNodeEx p, FlowState state, Cc outercc, Cc innercc,
|
||||
ParameterPositionOption summaryCtx, ApOption argAp, Ap ap, Configuration config
|
||||
) {
|
||||
exists(ArgNodeEx arg, boolean allowsFieldFlow |
|
||||
fwdFlow(arg, state, outercc, summaryCtx, argAp, ap, apa, config) and
|
||||
flowIntoCallApa(call, arg, p, allowsFieldFlow, apa, config) and
|
||||
fwdFlow(arg, state, outercc, summaryCtx, argAp, ap, config) and
|
||||
flowIntoCall(call, arg, p, allowsFieldFlow, config) and
|
||||
innercc = getCallContextCall(call, p.getEnclosingCallable(), outercc) and
|
||||
if allowsFieldFlow = false then ap instanceof ApNil else any()
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate fwdFlowRetFromArg(
|
||||
RetNodeEx ret, FlowState state, CcCall ccc, ParameterPosition summaryCtx, ParamNodeEx p,
|
||||
Ap argAp, ApApprox argApa, Ap ap, ApApprox apa, Configuration config
|
||||
) {
|
||||
exists(DataFlowCallable c, ReturnKindExt kind |
|
||||
fwdFlow(pragma[only_bind_into](ret), state, ccc,
|
||||
TParameterPositionSome(pragma[only_bind_into](summaryCtx)), apSome(argAp), ap, apa, config) and
|
||||
getApprox(argAp) = argApa and
|
||||
c = ret.getEnclosingCallable() and
|
||||
kind = ret.getKind() and
|
||||
p.isParameterOf(c, pragma[only_bind_into](summaryCtx)) and
|
||||
parameterFlowThroughAllowed(p, kind)
|
||||
)
|
||||
}
|
||||
|
||||
pragma[inline]
|
||||
private predicate fwdFlowInMayFlowThrough(
|
||||
DataFlowCall call, Cc cc, CcCall innerCc, ParameterPositionOption summaryCtx, ApOption argAp,
|
||||
ParamNodeEx param, Ap ap, ApApprox apa, Configuration config
|
||||
) {
|
||||
fwdFlowIn(call, pragma[only_bind_into](param), _, cc, innerCc, summaryCtx, argAp, ap,
|
||||
pragma[only_bind_into](apa), pragma[only_bind_into](config)) and
|
||||
PrevStage::parameterMayFlowThrough(param, apa, config)
|
||||
}
|
||||
|
||||
// dedup before joining with `flowThroughOutOfCall`
|
||||
pragma[nomagic]
|
||||
private predicate fwdFlowInMayFlowThroughProj(
|
||||
DataFlowCall call, CcCall innerCc, ApApprox apa, Configuration config
|
||||
) {
|
||||
fwdFlowInMayFlowThrough(call, _, innerCc, _, _, _, _, apa, config)
|
||||
}
|
||||
|
||||
/**
|
||||
* Same as `flowThroughOutOfCall`, but restricted to calls that are reached
|
||||
* in the flow covered by `fwdFlow`, where data might flow through the target
|
||||
* callable and back out at `call`.
|
||||
*/
|
||||
pragma[nomagic]
|
||||
private predicate fwdFlowThroughOutOfCall(
|
||||
DataFlowCall call, CcCall ccc, RetNodeEx ret, NodeEx out, boolean allowsFieldFlow,
|
||||
ApApprox argApa, ApApprox apa, Configuration config
|
||||
) {
|
||||
fwdFlowInMayFlowThroughProj(call, ccc, argApa, config) and
|
||||
flowThroughOutOfCall(call, ccc, ret, out, allowsFieldFlow, argApa, apa, config)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate fwdFlowOutFromArg(
|
||||
DataFlowCall call, NodeEx out, FlowState state, ParameterPosition summaryCtx, Ap argAp, Ap ap,
|
||||
ApApprox apa, Configuration config
|
||||
Configuration config
|
||||
) {
|
||||
exists(RetNodeEx ret, boolean allowsFieldFlow, CcCall ccc, ApApprox argApa |
|
||||
fwdFlowRetFromArg(pragma[only_bind_into](ret), state, pragma[only_bind_into](ccc),
|
||||
summaryCtx, _, argAp, pragma[only_bind_into](argApa), ap, pragma[only_bind_into](apa),
|
||||
exists(
|
||||
DataFlowCallable c, RetNodeEx ret, ReturnKindExt kind, boolean allowsFieldFlow, CcCall ccc
|
||||
|
|
||||
fwdFlow(pragma[only_bind_into](ret), state, pragma[only_bind_into](ccc),
|
||||
TParameterPositionSome(pragma[only_bind_into](summaryCtx)), apSome(argAp), ap, config) and
|
||||
flowThroughOutOfCall(call, pragma[only_bind_into](c), ccc, ret, kind, out, allowsFieldFlow,
|
||||
config) and
|
||||
fwdFlowThroughOutOfCall(call, ccc, ret, out, allowsFieldFlow, argApa, apa, config) and
|
||||
(if allowsFieldFlow = false then ap instanceof ApNil else any())
|
||||
(if allowsFieldFlow = false then ap instanceof ApNil else any()) and
|
||||
parameterFlowThroughAllowed(c, pragma[only_bind_into](summaryCtx), kind)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1575,7 +1483,8 @@ private module MkStage<StageSig PrevStage> {
|
||||
ParameterPosition pos, Ap ap, Configuration config
|
||||
) {
|
||||
exists(ParamNodeEx param |
|
||||
fwdFlowInMayFlowThrough(call, cc, _, summaryCtx, argAp, param, ap, _, config) and
|
||||
fwdFlowIn(call, param, _, cc, _, summaryCtx, argAp, ap, config) and
|
||||
PrevStage::parameterMayFlowThrough(param, unbindApa(getApprox(ap)), config) and
|
||||
pos = param.getPosition()
|
||||
)
|
||||
}
|
||||
@@ -1596,55 +1505,41 @@ private module MkStage<StageSig PrevStage> {
|
||||
fwdFlowConsCand(ap1, c, ap2, config)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate returnFlowsThrough0(
|
||||
RetNodeEx ret, ReturnKindExt kind, FlowState state, CcCall ccc, DataFlowCallable c,
|
||||
ParameterPosition ppos, Ap argAp, Ap ap, Configuration config
|
||||
) {
|
||||
exists(boolean allowsFieldFlow |
|
||||
fwdFlow(ret, state, ccc, TParameterPositionSome(ppos), apSome(argAp), ap, config) and
|
||||
flowThroughOutOfCall(_, c, _, pragma[only_bind_into](ret), kind, _, allowsFieldFlow,
|
||||
pragma[only_bind_into](config)) and
|
||||
if allowsFieldFlow = false then ap instanceof ApNil else any()
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate returnFlowsThrough(
|
||||
RetNodeEx ret, ReturnKindExt kind, FlowState state, CcCall ccc, ParamNodeEx p, Ap argAp,
|
||||
Ap ap, Configuration config
|
||||
) {
|
||||
exists(boolean allowsFieldFlow, ApApprox argApa, ApApprox apa |
|
||||
fwdFlowRetFromArg(pragma[only_bind_into](ret), state, pragma[only_bind_into](ccc), _, p,
|
||||
argAp, pragma[only_bind_into](argApa), ap, pragma[only_bind_into](apa), config) and
|
||||
kind = ret.getKind() and
|
||||
fwdFlowThroughOutOfCall(_, ccc, ret, _, allowsFieldFlow, argApa, apa, config) and
|
||||
(if allowsFieldFlow = false then ap instanceof ApNil else any())
|
||||
exists(DataFlowCallable c, ParameterPosition ppos |
|
||||
returnFlowsThrough0(ret, kind, state, ccc, c, ppos, argAp, ap, config) and
|
||||
p.isParameterOf(c, ppos) and
|
||||
parameterFlowThroughAllowed(p, kind)
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate flowThroughIntoCall(
|
||||
DataFlowCall call, ArgNodeEx arg, ParamNodeEx p, boolean allowsFieldFlow, Ap argAp,
|
||||
Configuration config
|
||||
DataFlowCall call, ArgNodeEx arg, ParamNodeEx p, boolean allowsFieldFlow, Configuration config
|
||||
) {
|
||||
exists(ApApprox argApa |
|
||||
flowIntoCallApa(call, pragma[only_bind_into](arg), pragma[only_bind_into](p),
|
||||
allowsFieldFlow, argApa, pragma[only_bind_into](config)) and
|
||||
fwdFlow(arg, _, _, _, _, pragma[only_bind_into](argAp), argApa,
|
||||
exists(Ap argAp |
|
||||
flowIntoCall(call, pragma[only_bind_into](arg), pragma[only_bind_into](p), allowsFieldFlow,
|
||||
pragma[only_bind_into](config)) and
|
||||
fwdFlow(arg, _, _, _, _, pragma[only_bind_into](argAp), pragma[only_bind_into](config)) and
|
||||
returnFlowsThrough(_, _, _, _, p, pragma[only_bind_into](argAp), _,
|
||||
pragma[only_bind_into](config)) and
|
||||
if allowsFieldFlow = false then argAp instanceof ApNil else any()
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate flowIntoCallAp(
|
||||
DataFlowCall call, ArgNodeEx arg, ParamNodeEx p, boolean allowsFieldFlow, Ap ap,
|
||||
Configuration config
|
||||
) {
|
||||
exists(ApApprox apa |
|
||||
flowIntoCallApa(call, arg, p, allowsFieldFlow, apa, config) and
|
||||
fwdFlow(arg, _, _, _, _, ap, apa, config)
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate flowOutOfCallAp(
|
||||
DataFlowCall call, RetNodeEx ret, ReturnKindExt kind, NodeEx out, boolean allowsFieldFlow,
|
||||
Ap ap, Configuration config
|
||||
) {
|
||||
exists(ApApprox apa |
|
||||
flowOutOfCallApa(call, ret, kind, out, allowsFieldFlow, apa, config) and
|
||||
fwdFlow(ret, _, _, _, _, ap, apa, config)
|
||||
pragma[only_bind_into](config))
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1733,7 +1628,7 @@ private module MkStage<StageSig PrevStage> {
|
||||
// flow into a callable
|
||||
exists(ParamNodeEx p, boolean allowsFieldFlow |
|
||||
revFlow(p, state, TReturnCtxNone(), returnAp, ap, config) and
|
||||
flowIntoCallAp(_, node, p, allowsFieldFlow, ap, config) and
|
||||
flowIntoCall(_, node, p, allowsFieldFlow, config) and
|
||||
(if allowsFieldFlow = false then ap instanceof ApNil else any()) and
|
||||
returnCtx = TReturnCtxNone()
|
||||
)
|
||||
@@ -1787,41 +1682,22 @@ private module MkStage<StageSig PrevStage> {
|
||||
) {
|
||||
exists(NodeEx out, boolean allowsFieldFlow |
|
||||
revFlow(out, state, returnCtx, returnAp, ap, config) and
|
||||
flowOutOfCallAp(call, ret, kind, out, allowsFieldFlow, ap, config) and
|
||||
flowOutOfCall(call, ret, kind, out, allowsFieldFlow, config) and
|
||||
if allowsFieldFlow = false then ap instanceof ApNil else any()
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Same as `flowThroughIntoCall`, but restricted to calls that are reached
|
||||
* in the flow covered by `revFlow`, where data might flow through the target
|
||||
* callable and back out at `call`.
|
||||
*/
|
||||
pragma[nomagic]
|
||||
private predicate revFlowThroughIntoCall(
|
||||
DataFlowCall call, ArgNodeEx arg, ParamNodeEx p, boolean allowsFieldFlow, Ap argAp,
|
||||
Configuration config
|
||||
) {
|
||||
flowThroughIntoCall(call, arg, p, allowsFieldFlow, argAp, config) and
|
||||
revFlowIsReturned(call, _, _, _, _, config)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate revFlowParamToReturn(
|
||||
ParamNodeEx p, FlowState state, ReturnKindExt kind, Ap returnAp, Ap ap, Configuration config
|
||||
) {
|
||||
revFlow(p, state, TReturnCtxMaybeFlowThrough(kind), apSome(returnAp), ap, config) and
|
||||
parameterFlowThroughAllowed(p, kind)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate revFlowInToReturn(
|
||||
DataFlowCall call, ArgNodeEx arg, FlowState state, ReturnKindExt kind, Ap returnAp, Ap ap,
|
||||
Configuration config
|
||||
) {
|
||||
exists(ParamNodeEx p, boolean allowsFieldFlow |
|
||||
revFlowParamToReturn(p, state, kind, returnAp, ap, config) and
|
||||
revFlowThroughIntoCall(call, arg, p, allowsFieldFlow, ap, config)
|
||||
revFlow(pragma[only_bind_into](p), state,
|
||||
TReturnCtxMaybeFlowThrough(pragma[only_bind_into](kind)), apSome(returnAp), ap, config) and
|
||||
flowThroughIntoCall(call, arg, p, allowsFieldFlow, config) and
|
||||
(if allowsFieldFlow = false then ap instanceof ApNil else any()) and
|
||||
parameterFlowThroughAllowed(p, kind)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1874,11 +1750,6 @@ private module MkStage<StageSig PrevStage> {
|
||||
pragma[nomagic]
|
||||
predicate revFlow(NodeEx node, Configuration config) { revFlow(node, _, _, _, _, config) }
|
||||
|
||||
pragma[nomagic]
|
||||
predicate revFlowAp(NodeEx node, Ap ap, Configuration config) {
|
||||
revFlow(node, _, _, _, ap, config)
|
||||
}
|
||||
|
||||
// use an alias as a workaround for bad functionality-induced joins
|
||||
pragma[nomagic]
|
||||
additional predicate revFlowAlias(NodeEx node, Configuration config) {
|
||||
@@ -1915,9 +1786,9 @@ private module MkStage<StageSig PrevStage> {
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate parameterFlowsThroughRev(
|
||||
ParamNodeEx p, Ap ap, ReturnKindExt kind, Ap returnAp, Configuration config
|
||||
ParamNodeEx p, Ap ap, ReturnKindExt kind, Configuration config
|
||||
) {
|
||||
revFlow(p, _, TReturnCtxMaybeFlowThrough(kind), apSome(returnAp), ap, config) and
|
||||
revFlow(p, _, TReturnCtxMaybeFlowThrough(kind), apSome(_), ap, config) and
|
||||
parameterFlowThroughAllowed(p, kind)
|
||||
}
|
||||
|
||||
@@ -1925,36 +1796,27 @@ private module MkStage<StageSig PrevStage> {
|
||||
predicate parameterMayFlowThrough(ParamNodeEx p, Ap ap, Configuration config) {
|
||||
exists(RetNodeEx ret, ReturnKindExt kind |
|
||||
returnFlowsThrough(ret, kind, _, _, p, ap, _, config) and
|
||||
parameterFlowsThroughRev(p, ap, kind, _, config)
|
||||
parameterFlowsThroughRev(p, ap, kind, config)
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
predicate returnMayFlowThrough(
|
||||
RetNodeEx ret, Ap argAp, Ap ap, ReturnKindExt kind, Configuration config
|
||||
) {
|
||||
exists(ParamNodeEx p |
|
||||
returnFlowsThrough(ret, kind, _, _, p, argAp, ap, config) and
|
||||
parameterFlowsThroughRev(p, argAp, kind, ap, config)
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
predicate revFlowInToReturnIsReturned(
|
||||
DataFlowCall call, ArgNodeEx arg, FlowState state, ReturnCtx returnCtx, ApOption returnAp,
|
||||
Ap ap, Configuration config
|
||||
) {
|
||||
exists(ReturnKindExt returnKind0, Ap returnAp0 |
|
||||
revFlowInToReturn(call, arg, state, returnKind0, returnAp0, ap, config) and
|
||||
revFlowIsReturned(call, returnCtx, returnAp, returnKind0, returnAp0, config)
|
||||
predicate returnMayFlowThrough(RetNodeEx ret, ReturnKindExt kind, Configuration config) {
|
||||
exists(ParamNodeEx p, Ap ap |
|
||||
returnFlowsThrough(ret, kind, _, _, p, ap, _, config) and
|
||||
parameterFlowsThroughRev(p, ap, kind, config)
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
predicate callMayFlowThroughRev(DataFlowCall call, Configuration config) {
|
||||
exists(ArgNodeEx arg, FlowState state, ReturnCtx returnCtx, ApOption returnAp, Ap ap |
|
||||
exists(
|
||||
ReturnKindExt returnKind0, Ap returnAp0, ArgNodeEx arg, FlowState state,
|
||||
ReturnCtx returnCtx, ApOption returnAp, Ap ap
|
||||
|
|
||||
revFlow(arg, state, returnCtx, returnAp, ap, config) and
|
||||
revFlowInToReturnIsReturned(call, arg, state, returnCtx, returnAp, ap, config)
|
||||
revFlowInToReturn(call, arg, state, returnKind0, returnAp0, ap, config) and
|
||||
revFlowIsReturned(call, returnCtx, returnAp, returnKind0, returnAp0, config)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -2317,16 +2179,16 @@ private module LocalFlowBigStep {
|
||||
pragma[nomagic]
|
||||
predicate localFlowBigStep(
|
||||
NodeEx node1, FlowState state1, NodeEx node2, FlowState state2, boolean preservesValue,
|
||||
DataFlowType t, Configuration config, LocalCallContext callContext
|
||||
AccessPathFrontNil apf, Configuration config, LocalCallContext callContext
|
||||
) {
|
||||
localFlowStepPlus(node1, state1, node2, preservesValue, t, config, callContext) and
|
||||
localFlowStepPlus(node1, state1, node2, preservesValue, apf.getType(), config, callContext) and
|
||||
localFlowExit(node2, state1, config) and
|
||||
state1 = state2
|
||||
or
|
||||
additionalLocalFlowStepNodeCand2(node1, state1, node2, state2, config) and
|
||||
state1 != state2 and
|
||||
preservesValue = false and
|
||||
t = node2.getDataFlowType() and
|
||||
apf = TFrontNil(node2.getDataFlowType()) and
|
||||
callContext.relevantFor(node1.getEnclosingCallable()) and
|
||||
not exists(DataFlowCall call | call = callContext.(LocalCallContextSpecificCall).getCall() |
|
||||
isUnreachableInCallCached(node1.asNode(), call) or
|
||||
@@ -2340,87 +2202,11 @@ private import LocalFlowBigStep
|
||||
private module Stage3Param implements MkStage<Stage2>::StageParam {
|
||||
private module PrevStage = Stage2;
|
||||
|
||||
class Ap = ApproxAccessPathFront;
|
||||
|
||||
class ApNil = ApproxAccessPathFrontNil;
|
||||
|
||||
PrevStage::Ap getApprox(Ap ap) { result = ap.toBoolNonEmpty() }
|
||||
|
||||
ApNil getApNil(NodeEx node) {
|
||||
PrevStage::revFlow(node, _) and result = TApproxFrontNil(node.getDataFlowType())
|
||||
}
|
||||
|
||||
bindingset[tc, tail]
|
||||
Ap apCons(TypedContent tc, Ap tail) { result.getAHead() = tc and exists(tail) }
|
||||
|
||||
pragma[noinline]
|
||||
Content getHeadContent(Ap ap) { result = ap.getAHead().getContent() }
|
||||
|
||||
class ApOption = ApproxAccessPathFrontOption;
|
||||
|
||||
ApOption apNone() { result = TApproxAccessPathFrontNone() }
|
||||
|
||||
ApOption apSome(Ap ap) { result = TApproxAccessPathFrontSome(ap) }
|
||||
|
||||
import BooleanCallContext
|
||||
|
||||
predicate localStep(
|
||||
NodeEx node1, FlowState state1, NodeEx node2, FlowState state2, boolean preservesValue,
|
||||
ApproxAccessPathFrontNil ap, Configuration config, LocalCc lcc
|
||||
) {
|
||||
localFlowBigStep(node1, state1, node2, state2, preservesValue, ap.getType(), config, _) and
|
||||
exists(lcc)
|
||||
}
|
||||
|
||||
predicate flowOutOfCall = flowOutOfCallNodeCand2/6;
|
||||
|
||||
predicate flowIntoCall = flowIntoCallNodeCand2/5;
|
||||
|
||||
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.getAHead().getContent()
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate castingNodeEx(NodeEx node) { node.asNode() instanceof CastingNode }
|
||||
|
||||
bindingset[node, state, ap, config]
|
||||
predicate filter(NodeEx node, FlowState state, Ap ap, Configuration config) {
|
||||
exists(state) and
|
||||
exists(config) and
|
||||
(if castingNodeEx(node) then compatibleTypes(node.getDataFlowType(), ap.getType()) else any()) and
|
||||
(
|
||||
notExpectsContent(node)
|
||||
or
|
||||
expectsContentCand(node, ap, config)
|
||||
)
|
||||
}
|
||||
|
||||
bindingset[ap, contentType]
|
||||
predicate typecheckStore(Ap ap, DataFlowType contentType) {
|
||||
// We need to typecheck stores here, since reverse flow through a getter
|
||||
// might have a different type here compared to inside the getter.
|
||||
compatibleTypes(ap.getType(), contentType)
|
||||
}
|
||||
}
|
||||
|
||||
private module Stage3 implements StageSig {
|
||||
import MkStage<Stage2>::Stage<Stage3Param>
|
||||
}
|
||||
|
||||
private module Stage4Param implements MkStage<Stage3>::StageParam {
|
||||
private module PrevStage = Stage3;
|
||||
|
||||
class Ap = AccessPathFront;
|
||||
|
||||
class ApNil = AccessPathFrontNil;
|
||||
|
||||
PrevStage::Ap getApprox(Ap ap) { result = ap.toApprox() }
|
||||
PrevStage::Ap getApprox(Ap ap) { result = ap.toBoolNonEmpty() }
|
||||
|
||||
ApNil getApNil(NodeEx node) {
|
||||
PrevStage::revFlow(node, _) and result = TFrontNil(node.getDataFlowType())
|
||||
@@ -2440,42 +2226,16 @@ private module Stage4Param implements MkStage<Stage3>::StageParam {
|
||||
|
||||
import BooleanCallContext
|
||||
|
||||
pragma[nomagic]
|
||||
predicate localStep(
|
||||
NodeEx node1, FlowState state1, NodeEx node2, FlowState state2, boolean preservesValue,
|
||||
ApNil ap, Configuration config, LocalCc lcc
|
||||
) {
|
||||
localFlowBigStep(node1, state1, node2, state2, preservesValue, ap.getType(), config, _) and
|
||||
PrevStage::revFlow(node1, pragma[only_bind_into](state1), _, pragma[only_bind_into](config)) and
|
||||
PrevStage::revFlowAlias(node2, pragma[only_bind_into](state2), _, pragma[only_bind_into](config)) and
|
||||
exists(lcc)
|
||||
localFlowBigStep(node1, state1, node2, state2, preservesValue, ap, config, _) and exists(lcc)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
predicate flowOutOfCall(
|
||||
DataFlowCall call, RetNodeEx node1, ReturnKindExt kind, NodeEx node2, boolean allowsFieldFlow,
|
||||
Configuration config
|
||||
) {
|
||||
exists(FlowState state |
|
||||
flowOutOfCallNodeCand2(call, node1, kind, node2, allowsFieldFlow, config) and
|
||||
PrevStage::revFlow(node2, pragma[only_bind_into](state), _, pragma[only_bind_into](config)) and
|
||||
PrevStage::revFlowAlias(node1, pragma[only_bind_into](state), _,
|
||||
pragma[only_bind_into](config))
|
||||
)
|
||||
}
|
||||
predicate flowOutOfCall = flowOutOfCallNodeCand2/6;
|
||||
|
||||
pragma[nomagic]
|
||||
predicate flowIntoCall(
|
||||
DataFlowCall call, ArgNodeEx node1, ParamNodeEx node2, boolean allowsFieldFlow,
|
||||
Configuration config
|
||||
) {
|
||||
exists(FlowState state |
|
||||
flowIntoCallNodeCand2(call, node1, node2, allowsFieldFlow, config) and
|
||||
PrevStage::revFlow(node2, pragma[only_bind_into](state), _, pragma[only_bind_into](config)) and
|
||||
PrevStage::revFlowAlias(node1, pragma[only_bind_into](state), _,
|
||||
pragma[only_bind_into](config))
|
||||
)
|
||||
}
|
||||
predicate flowIntoCall = flowIntoCallNodeCand2/5;
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate clearSet(NodeEx node, ContentSet c, Configuration config) {
|
||||
@@ -2531,8 +2291,8 @@ private module Stage4Param implements MkStage<Stage3>::StageParam {
|
||||
}
|
||||
}
|
||||
|
||||
private module Stage4 implements StageSig {
|
||||
import MkStage<Stage3>::Stage<Stage4Param>
|
||||
private module Stage3 implements StageSig {
|
||||
import MkStage<Stage2>::Stage<Stage3Param>
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -2543,8 +2303,8 @@ private predicate flowCandSummaryCtx(
|
||||
NodeEx node, FlowState state, AccessPathFront argApf, Configuration config
|
||||
) {
|
||||
exists(AccessPathFront apf |
|
||||
Stage4::revFlow(node, state, TReturnCtxMaybeFlowThrough(_), _, apf, config) and
|
||||
Stage4::fwdFlow(node, state, any(Stage4::CcCall ccc), _, TAccessPathFrontSome(argApf), apf,
|
||||
Stage3::revFlow(node, state, TReturnCtxMaybeFlowThrough(_), _, apf, config) and
|
||||
Stage3::fwdFlow(node, state, any(Stage3::CcCall ccc), _, TAccessPathFrontSome(argApf), apf,
|
||||
config)
|
||||
)
|
||||
}
|
||||
@@ -2555,10 +2315,10 @@ private predicate flowCandSummaryCtx(
|
||||
*/
|
||||
private predicate expensiveLen2unfolding(TypedContent tc, Configuration config) {
|
||||
exists(int tails, int nodes, int apLimit, int tupleLimit |
|
||||
tails = strictcount(AccessPathFront apf | Stage4::consCand(tc, apf, config)) and
|
||||
tails = strictcount(AccessPathFront apf | Stage3::consCand(tc, apf, config)) and
|
||||
nodes =
|
||||
strictcount(NodeEx n, FlowState state |
|
||||
Stage4::revFlow(n, state, any(AccessPathFrontHead apf | apf.getHead() = tc), config)
|
||||
Stage3::revFlow(n, state, any(AccessPathFrontHead apf | apf.getHead() = tc), config)
|
||||
or
|
||||
flowCandSummaryCtx(n, state, any(AccessPathFrontHead apf | apf.getHead() = tc), config)
|
||||
) and
|
||||
@@ -2572,11 +2332,11 @@ private predicate expensiveLen2unfolding(TypedContent tc, Configuration config)
|
||||
private newtype TAccessPathApprox =
|
||||
TNil(DataFlowType t) or
|
||||
TConsNil(TypedContent tc, DataFlowType t) {
|
||||
Stage4::consCand(tc, TFrontNil(t), _) and
|
||||
Stage3::consCand(tc, TFrontNil(t), _) and
|
||||
not expensiveLen2unfolding(tc, _)
|
||||
} or
|
||||
TConsCons(TypedContent tc1, TypedContent tc2, int len) {
|
||||
Stage4::consCand(tc1, TFrontHead(tc2), _) and
|
||||
Stage3::consCand(tc1, TFrontHead(tc2), _) and
|
||||
len in [2 .. accessPathLimit()] and
|
||||
not expensiveLen2unfolding(tc1, _)
|
||||
} or
|
||||
@@ -2706,7 +2466,7 @@ private class AccessPathApproxCons1 extends AccessPathApproxCons, TCons1 {
|
||||
override AccessPathApprox pop(TypedContent head) {
|
||||
head = tc and
|
||||
(
|
||||
exists(TypedContent tc2 | Stage4::consCand(tc, TFrontHead(tc2), _) |
|
||||
exists(TypedContent tc2 | Stage3::consCand(tc, TFrontHead(tc2), _) |
|
||||
result = TConsCons(tc2, _, len - 1)
|
||||
or
|
||||
len = 2 and
|
||||
@@ -2717,7 +2477,7 @@ private class AccessPathApproxCons1 extends AccessPathApproxCons, TCons1 {
|
||||
or
|
||||
exists(DataFlowType t |
|
||||
len = 1 and
|
||||
Stage4::consCand(tc, TFrontNil(t), _) and
|
||||
Stage3::consCand(tc, TFrontNil(t), _) and
|
||||
result = TNil(t)
|
||||
)
|
||||
)
|
||||
@@ -2742,14 +2502,13 @@ private class AccessPathApproxOption extends TAccessPathApproxOption {
|
||||
}
|
||||
}
|
||||
|
||||
private module Stage5Param implements MkStage<Stage4>::StageParam {
|
||||
private module PrevStage = Stage4;
|
||||
private module Stage4Param implements MkStage<Stage3>::StageParam {
|
||||
private module PrevStage = Stage3;
|
||||
|
||||
class Ap = AccessPathApprox;
|
||||
|
||||
class ApNil = AccessPathApproxNil;
|
||||
|
||||
pragma[nomagic]
|
||||
PrevStage::Ap getApprox(Ap ap) { result = ap.getFront() }
|
||||
|
||||
ApNil getApNil(NodeEx node) {
|
||||
@@ -2775,9 +2534,7 @@ private module Stage5Param implements MkStage<Stage4>::StageParam {
|
||||
NodeEx node1, FlowState state1, NodeEx node2, FlowState state2, boolean preservesValue,
|
||||
ApNil ap, Configuration config, LocalCc lcc
|
||||
) {
|
||||
localFlowBigStep(node1, state1, node2, state2, preservesValue, ap.getType(), config, lcc) and
|
||||
PrevStage::revFlow(node1, pragma[only_bind_into](state1), _, pragma[only_bind_into](config)) and
|
||||
PrevStage::revFlowAlias(node2, pragma[only_bind_into](state2), _, pragma[only_bind_into](config))
|
||||
localFlowBigStep(node1, state1, node2, state2, preservesValue, ap.getFront(), config, lcc)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
@@ -2814,7 +2571,7 @@ private module Stage5Param implements MkStage<Stage4>::StageParam {
|
||||
predicate typecheckStore(Ap ap, DataFlowType contentType) { any() }
|
||||
}
|
||||
|
||||
private module Stage5 = MkStage<Stage4>::Stage<Stage5Param>;
|
||||
private module Stage4 = MkStage<Stage3>::Stage<Stage4Param>;
|
||||
|
||||
bindingset[conf, result]
|
||||
private Configuration unbindConf(Configuration conf) {
|
||||
@@ -2828,8 +2585,8 @@ private predicate nodeMayUseSummary0(
|
||||
) {
|
||||
exists(AccessPathApprox apa0 |
|
||||
c = n.getEnclosingCallable() and
|
||||
Stage5::revFlow(n, state, TReturnCtxMaybeFlowThrough(_), _, apa0, config) and
|
||||
Stage5::fwdFlow(n, state, any(CallContextCall ccc), TParameterPositionSome(pos),
|
||||
Stage4::revFlow(n, state, TReturnCtxMaybeFlowThrough(_), _, apa0, config) and
|
||||
Stage4::fwdFlow(n, state, any(CallContextCall ccc), TParameterPositionSome(pos),
|
||||
TAccessPathApproxSome(apa), apa0, config)
|
||||
)
|
||||
}
|
||||
@@ -2839,7 +2596,7 @@ private predicate nodeMayUseSummary(
|
||||
NodeEx n, FlowState state, AccessPathApprox apa, Configuration config
|
||||
) {
|
||||
exists(DataFlowCallable c, ParameterPosition pos, ParamNodeEx p |
|
||||
Stage5::parameterMayFlowThrough(p, apa, config) and
|
||||
Stage4::parameterMayFlowThrough(p, apa, config) and
|
||||
nodeMayUseSummary0(n, c, pos, state, apa, config) and
|
||||
p.isParameterOf(c, pos)
|
||||
)
|
||||
@@ -2849,8 +2606,8 @@ private newtype TSummaryCtx =
|
||||
TSummaryCtxNone() or
|
||||
TSummaryCtxSome(ParamNodeEx p, FlowState state, AccessPath ap) {
|
||||
exists(Configuration config |
|
||||
Stage5::parameterMayFlowThrough(p, ap.getApprox(), config) and
|
||||
Stage5::revFlow(p, state, _, config)
|
||||
Stage4::parameterMayFlowThrough(p, ap.getApprox(), config) and
|
||||
Stage4::revFlow(p, state, _, config)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -2899,7 +2656,7 @@ private int count1to2unfold(AccessPathApproxCons1 apa, Configuration config) {
|
||||
len = apa.len() and
|
||||
result =
|
||||
strictcount(AccessPathFront apf |
|
||||
Stage5::consCand(tc, any(AccessPathApprox ap | ap.getFront() = apf and ap.len() = len - 1),
|
||||
Stage4::consCand(tc, any(AccessPathApprox ap | ap.getFront() = apf and ap.len() = len - 1),
|
||||
config)
|
||||
)
|
||||
)
|
||||
@@ -2908,7 +2665,7 @@ private int count1to2unfold(AccessPathApproxCons1 apa, Configuration config) {
|
||||
private int countNodesUsingAccessPath(AccessPathApprox apa, Configuration config) {
|
||||
result =
|
||||
strictcount(NodeEx n, FlowState state |
|
||||
Stage5::revFlow(n, state, apa, config) or nodeMayUseSummary(n, state, apa, config)
|
||||
Stage4::revFlow(n, state, apa, config) or nodeMayUseSummary(n, state, apa, config)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -2929,7 +2686,7 @@ private predicate expensiveLen1to2unfolding(AccessPathApproxCons1 apa, Configura
|
||||
private AccessPathApprox getATail(AccessPathApprox apa, Configuration config) {
|
||||
exists(TypedContent head |
|
||||
apa.pop(head) = result and
|
||||
Stage5::consCand(head, result, config)
|
||||
Stage4::consCand(head, result, config)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -3011,7 +2768,7 @@ private newtype TPathNode =
|
||||
NodeEx node, FlowState state, CallContext cc, SummaryCtx sc, AccessPath ap, Configuration config
|
||||
) {
|
||||
// A PathNode is introduced by a source ...
|
||||
Stage5::revFlow(node, state, config) and
|
||||
Stage4::revFlow(node, state, config) and
|
||||
sourceNode(node, state, config) and
|
||||
(
|
||||
if hasSourceCallCtx(config)
|
||||
@@ -3025,7 +2782,7 @@ private newtype TPathNode =
|
||||
exists(PathNodeMid mid |
|
||||
pathStep(mid, node, state, cc, sc, ap) and
|
||||
pragma[only_bind_into](config) = mid.getConfiguration() and
|
||||
Stage5::revFlow(node, state, ap.getApprox(), pragma[only_bind_into](config))
|
||||
Stage4::revFlow(node, state, ap.getApprox(), pragma[only_bind_into](config))
|
||||
)
|
||||
} or
|
||||
TPathNodeSink(NodeEx node, FlowState state, Configuration config) {
|
||||
@@ -3167,7 +2924,7 @@ private class AccessPathCons2 extends AccessPath, TAccessPathCons2 {
|
||||
override TypedContent getHead() { result = head1 }
|
||||
|
||||
override AccessPath getTail() {
|
||||
Stage5::consCand(head1, result.getApprox(), _) and
|
||||
Stage4::consCand(head1, result.getApprox(), _) and
|
||||
result.getHead() = head2 and
|
||||
result.length() = len - 1
|
||||
}
|
||||
@@ -3198,7 +2955,7 @@ private class AccessPathCons1 extends AccessPath, TAccessPathCons1 {
|
||||
override TypedContent getHead() { result = head }
|
||||
|
||||
override AccessPath getTail() {
|
||||
Stage5::consCand(head, result.getApprox(), _) and result.length() = len - 1
|
||||
Stage4::consCand(head, result.getApprox(), _) and result.length() = len - 1
|
||||
}
|
||||
|
||||
override AccessPathFrontHead getFront() { result = TFrontHead(head) }
|
||||
@@ -3590,8 +3347,7 @@ private predicate pathStep(
|
||||
AccessPath ap0, NodeEx midnode, FlowState state0, Configuration conf, LocalCallContext localCC
|
||||
|
|
||||
pathNode(mid, midnode, state0, cc, sc, ap0, conf, localCC) and
|
||||
localFlowBigStep(midnode, state0, node, state, false, ap.(AccessPathNil).getType(), conf,
|
||||
localCC) and
|
||||
localFlowBigStep(midnode, state0, node, state, false, ap.getFront(), conf, localCC) and
|
||||
ap0 instanceof AccessPathNil
|
||||
)
|
||||
or
|
||||
@@ -3633,7 +3389,7 @@ private predicate pathReadStep(
|
||||
) {
|
||||
ap0 = mid.getAp() and
|
||||
tc = ap0.getHead() and
|
||||
Stage5::readStepCand(mid.getNodeEx(), tc.getContent(), node, mid.getConfiguration()) and
|
||||
Stage4::readStepCand(mid.getNodeEx(), tc.getContent(), node, mid.getConfiguration()) and
|
||||
state = mid.getState() and
|
||||
cc = mid.getCallContext()
|
||||
}
|
||||
@@ -3643,7 +3399,7 @@ private predicate pathStoreStep(
|
||||
PathNodeMid mid, NodeEx node, FlowState state, AccessPath ap0, TypedContent tc, CallContext cc
|
||||
) {
|
||||
ap0 = mid.getAp() and
|
||||
Stage5::storeStepCand(mid.getNodeEx(), _, tc, node, _, mid.getConfiguration()) and
|
||||
Stage4::storeStepCand(mid.getNodeEx(), _, tc, node, _, mid.getConfiguration()) and
|
||||
state = mid.getState() and
|
||||
cc = mid.getCallContext()
|
||||
}
|
||||
@@ -3680,7 +3436,7 @@ private NodeEx getAnOutNodeFlow(
|
||||
ReturnKindExt kind, DataFlowCall call, AccessPathApprox apa, Configuration config
|
||||
) {
|
||||
result.asNode() = kind.getAnOutNode(call) and
|
||||
Stage5::revFlow(result, _, apa, config)
|
||||
Stage4::revFlow(result, _, apa, config)
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -3716,7 +3472,7 @@ private predicate parameterCand(
|
||||
DataFlowCallable callable, ParameterPosition pos, AccessPathApprox apa, Configuration config
|
||||
) {
|
||||
exists(ParamNodeEx p |
|
||||
Stage5::revFlow(p, _, apa, config) and
|
||||
Stage4::revFlow(p, _, apa, config) and
|
||||
p.isParameterOf(callable, pos)
|
||||
)
|
||||
}
|
||||
@@ -3995,17 +3751,9 @@ predicate stageStats(
|
||||
n = 45 and
|
||||
Stage4::stats(false, nodes, fields, conscand, states, tuples, config)
|
||||
or
|
||||
stage = "5 Fwd" and
|
||||
n = 50 and
|
||||
Stage5::stats(true, nodes, fields, conscand, states, tuples, config)
|
||||
stage = "5 Fwd" and n = 50 and finalStats(true, nodes, fields, conscand, states, tuples)
|
||||
or
|
||||
stage = "5 Rev" and
|
||||
n = 55 and
|
||||
Stage5::stats(false, nodes, fields, conscand, states, tuples, config)
|
||||
or
|
||||
stage = "6 Fwd" and n = 60 and finalStats(true, nodes, fields, conscand, states, tuples)
|
||||
or
|
||||
stage = "6 Rev" and n = 65 and finalStats(false, nodes, fields, conscand, states, tuples)
|
||||
stage = "5 Rev" and n = 55 and finalStats(false, nodes, fields, conscand, states, tuples)
|
||||
}
|
||||
|
||||
private module FlowExploration {
|
||||
|
||||
@@ -926,46 +926,18 @@ private module Cached {
|
||||
TReturnCtxNoFlowThrough() or
|
||||
TReturnCtxMaybeFlowThrough(ReturnKindExt kind)
|
||||
|
||||
cached
|
||||
newtype TTypedContentApprox =
|
||||
MkTypedContentApprox(ContentApprox c, DataFlowType t) {
|
||||
exists(Content cont |
|
||||
c = getContentApprox(cont) and
|
||||
store(_, cont, _, _, t)
|
||||
)
|
||||
}
|
||||
|
||||
cached
|
||||
newtype TTypedContent = MkTypedContent(Content c, DataFlowType t) { store(_, c, _, _, t) }
|
||||
|
||||
cached
|
||||
TypedContent getATypedContent(TypedContentApprox c) {
|
||||
exists(ContentApprox cls, DataFlowType t, Content cont |
|
||||
c = MkTypedContentApprox(cls, pragma[only_bind_into](t)) and
|
||||
result = MkTypedContent(cont, pragma[only_bind_into](t)) and
|
||||
cls = getContentApprox(cont)
|
||||
)
|
||||
}
|
||||
|
||||
cached
|
||||
newtype TAccessPathFront =
|
||||
TFrontNil(DataFlowType t) or
|
||||
TFrontHead(TypedContent tc)
|
||||
|
||||
cached
|
||||
newtype TApproxAccessPathFront =
|
||||
TApproxFrontNil(DataFlowType t) or
|
||||
TApproxFrontHead(TypedContentApprox tc)
|
||||
|
||||
cached
|
||||
newtype TAccessPathFrontOption =
|
||||
TAccessPathFrontNone() or
|
||||
TAccessPathFrontSome(AccessPathFront apf)
|
||||
|
||||
cached
|
||||
newtype TApproxAccessPathFrontOption =
|
||||
TApproxAccessPathFrontNone() or
|
||||
TApproxAccessPathFrontSome(ApproxAccessPathFront apf)
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1381,75 +1353,6 @@ class ReturnCtx extends TReturnCtx {
|
||||
}
|
||||
}
|
||||
|
||||
/** An approximated `Content` tagged with the type of a containing object. */
|
||||
class TypedContentApprox extends MkTypedContentApprox {
|
||||
private ContentApprox c;
|
||||
private DataFlowType t;
|
||||
|
||||
TypedContentApprox() { this = MkTypedContentApprox(c, t) }
|
||||
|
||||
/** Gets a typed content approximated by this value. */
|
||||
TypedContent getATypedContent() { result = getATypedContent(this) }
|
||||
|
||||
/** Gets the container type. */
|
||||
DataFlowType getContainerType() { result = t }
|
||||
|
||||
/** Gets a textual representation of this approximated content. */
|
||||
string toString() { result = c.toString() }
|
||||
}
|
||||
|
||||
/**
|
||||
* The front of an approximated access path. This is either a head or a nil.
|
||||
*/
|
||||
abstract class ApproxAccessPathFront extends TApproxAccessPathFront {
|
||||
abstract string toString();
|
||||
|
||||
abstract DataFlowType getType();
|
||||
|
||||
abstract boolean toBoolNonEmpty();
|
||||
|
||||
pragma[nomagic]
|
||||
TypedContent getAHead() {
|
||||
exists(TypedContentApprox cont |
|
||||
this = TApproxFrontHead(cont) and
|
||||
result = cont.getATypedContent()
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
class ApproxAccessPathFrontNil extends ApproxAccessPathFront, TApproxFrontNil {
|
||||
private DataFlowType t;
|
||||
|
||||
ApproxAccessPathFrontNil() { this = TApproxFrontNil(t) }
|
||||
|
||||
override string toString() { result = ppReprType(t) }
|
||||
|
||||
override DataFlowType getType() { result = t }
|
||||
|
||||
override boolean toBoolNonEmpty() { result = false }
|
||||
}
|
||||
|
||||
class ApproxAccessPathFrontHead extends ApproxAccessPathFront, TApproxFrontHead {
|
||||
private TypedContentApprox tc;
|
||||
|
||||
ApproxAccessPathFrontHead() { this = TApproxFrontHead(tc) }
|
||||
|
||||
override string toString() { result = tc.toString() }
|
||||
|
||||
override DataFlowType getType() { result = tc.getContainerType() }
|
||||
|
||||
override boolean toBoolNonEmpty() { result = true }
|
||||
}
|
||||
|
||||
/** An optional approximated access path front. */
|
||||
class ApproxAccessPathFrontOption extends TApproxAccessPathFrontOption {
|
||||
string toString() {
|
||||
this = TApproxAccessPathFrontNone() and result = "<none>"
|
||||
or
|
||||
this = TApproxAccessPathFrontSome(any(ApproxAccessPathFront apf | result = apf.toString()))
|
||||
}
|
||||
}
|
||||
|
||||
/** A `Content` tagged with the type of a containing object. */
|
||||
class TypedContent extends MkTypedContent {
|
||||
private Content c;
|
||||
@@ -1482,7 +1385,7 @@ abstract class AccessPathFront extends TAccessPathFront {
|
||||
|
||||
abstract DataFlowType getType();
|
||||
|
||||
abstract ApproxAccessPathFront toApprox();
|
||||
abstract boolean toBoolNonEmpty();
|
||||
|
||||
TypedContent getHead() { this = TFrontHead(result) }
|
||||
}
|
||||
@@ -1496,7 +1399,7 @@ class AccessPathFrontNil extends AccessPathFront, TFrontNil {
|
||||
|
||||
override DataFlowType getType() { result = t }
|
||||
|
||||
override ApproxAccessPathFront toApprox() { result = TApproxFrontNil(t) }
|
||||
override boolean toBoolNonEmpty() { result = false }
|
||||
}
|
||||
|
||||
class AccessPathFrontHead extends AccessPathFront, TFrontHead {
|
||||
@@ -1508,7 +1411,7 @@ class AccessPathFrontHead extends AccessPathFront, TFrontHead {
|
||||
|
||||
override DataFlowType getType() { result = tc.getContainerType() }
|
||||
|
||||
override ApproxAccessPathFront toApprox() { result.getAHead() = tc }
|
||||
override boolean toBoolNonEmpty() { result = true }
|
||||
}
|
||||
|
||||
/** An optional access path front. */
|
||||
|
||||
@@ -260,9 +260,4 @@ module Consistency {
|
||||
not exists(unique(ParameterPosition pos0 | isParameterNode(p, c, pos0))) and
|
||||
msg = "Parameter node with multiple positions."
|
||||
}
|
||||
|
||||
query predicate uniqueContentApprox(Content c, string msg) {
|
||||
not exists(unique(ContentApprox approx | approx = getContentApprox(c))) and
|
||||
msg = "Non-unique content approximation."
|
||||
}
|
||||
}
|
||||
|
||||
@@ -551,13 +551,6 @@ predicate additionalLambdaFlowStep(Node nodeFrom, Node nodeTo, boolean preserves
|
||||
*/
|
||||
predicate allowParameterReturnInSelf(ParameterNode p) { none() }
|
||||
|
||||
/** An approximated `Content`. */
|
||||
class ContentApprox = Unit;
|
||||
|
||||
/** Gets an approximated value for content `c`. */
|
||||
pragma[inline]
|
||||
ContentApprox getContentApprox(Content c) { any() }
|
||||
|
||||
private class MyConsistencyConfiguration extends Consistency::ConsistencyConfiguration {
|
||||
override predicate argHasPostUpdateExclude(ArgumentNode n) {
|
||||
// The rules for whether an IR argument gets a post-update node are too
|
||||
|
||||
@@ -292,8 +292,12 @@ module SemanticExprConfig {
|
||||
final Location getLocation() { result = super.getLocation() }
|
||||
}
|
||||
|
||||
private class ValueNumberBound extends Bound instanceof IRBound::ValueNumberBound {
|
||||
override string toString() { result = IRBound::ValueNumberBound.super.toString() }
|
||||
private class ValueNumberBound extends Bound {
|
||||
IRBound::ValueNumberBound bound;
|
||||
|
||||
ValueNumberBound() { bound = this }
|
||||
|
||||
override string toString() { result = bound.toString() }
|
||||
}
|
||||
|
||||
predicate zeroBound(Bound bound) { bound instanceof IRBound::ZeroBound }
|
||||
|
||||
@@ -33,15 +33,23 @@ abstract private class FlowSignDef extends SignDef {
|
||||
}
|
||||
|
||||
/** An SSA definition whose sign is determined by the sign of that definitions source expression. */
|
||||
private class ExplicitSignDef extends FlowSignDef instanceof SemSsaExplicitUpdate {
|
||||
final override Sign getSign() { result = semExprSign(super.getSourceExpr()) }
|
||||
private class ExplicitSignDef extends FlowSignDef {
|
||||
SemSsaExplicitUpdate update;
|
||||
|
||||
ExplicitSignDef() { update = this }
|
||||
|
||||
final override Sign getSign() { result = semExprSign(update.getSourceExpr()) }
|
||||
}
|
||||
|
||||
/** An SSA Phi definition, whose sign is the union of the signs of its inputs. */
|
||||
private class PhiSignDef extends FlowSignDef instanceof SemSsaPhiNode {
|
||||
private class PhiSignDef extends FlowSignDef {
|
||||
SemSsaPhiNode phi;
|
||||
|
||||
PhiSignDef() { phi = this }
|
||||
|
||||
final override Sign getSign() {
|
||||
exists(SemSsaVariable inp, SemSsaReadPositionPhiInputEdge edge |
|
||||
edge.phiInput(this, inp) and
|
||||
edge.phiInput(phi, inp) and
|
||||
result = semSsaSign(inp, edge)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
name: codeql/cpp-all
|
||||
version: 0.4.6-dev
|
||||
version: 0.4.5-dev
|
||||
groups: cpp
|
||||
dbscheme: semmlecode.cpp.dbscheme
|
||||
extractor: cpp
|
||||
|
||||
@@ -63,6 +63,7 @@ class Location extends @location {
|
||||
* For more information, see
|
||||
* [Locations](https://codeql.github.com/docs/writing-codeql-queries/providing-locations-in-codeql-queries/).
|
||||
*/
|
||||
pragma[inline]
|
||||
predicate hasLocationInfo(
|
||||
string filepath, int startline, int startcolumn, int endline, int endcolumn
|
||||
) {
|
||||
|
||||
@@ -397,8 +397,11 @@ private int lengthInBase16(float f) {
|
||||
/**
|
||||
* A class to represent format strings that occur as arguments to invocations of formatting functions.
|
||||
*/
|
||||
class FormatLiteral extends Literal instanceof StringLiteral {
|
||||
FormatLiteral() { exists(FormattingFunctionCall ffc | ffc.getFormat() = this) }
|
||||
class FormatLiteral extends Literal {
|
||||
FormatLiteral() {
|
||||
exists(FormattingFunctionCall ffc | ffc.getFormat() = this) and
|
||||
this instanceof StringLiteral
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the function call where this format string is used.
|
||||
|
||||
@@ -30,12 +30,15 @@ abstract class ScanfFunction extends Function {
|
||||
/**
|
||||
* The standard function `scanf` (and variations).
|
||||
*/
|
||||
class Scanf extends ScanfFunction instanceof TopLevelFunction {
|
||||
class Scanf extends ScanfFunction {
|
||||
Scanf() {
|
||||
this.hasGlobalOrStdOrBslName("scanf") or // scanf(format, args...)
|
||||
this.hasGlobalOrStdOrBslName("wscanf") or // wscanf(format, args...)
|
||||
this.hasGlobalName("_scanf_l") or // _scanf_l(format, locale, args...)
|
||||
this.hasGlobalName("_wscanf_l")
|
||||
this instanceof TopLevelFunction and
|
||||
(
|
||||
this.hasGlobalOrStdOrBslName("scanf") or // scanf(format, args...)
|
||||
this.hasGlobalOrStdOrBslName("wscanf") or // wscanf(format, args...)
|
||||
this.hasGlobalName("_scanf_l") or // _scanf_l(format, locale, args...)
|
||||
this.hasGlobalName("_wscanf_l") // _wscanf_l(format, locale, args...)
|
||||
)
|
||||
}
|
||||
|
||||
override int getInputParameterIndex() { none() }
|
||||
@@ -46,12 +49,15 @@ class Scanf extends ScanfFunction instanceof TopLevelFunction {
|
||||
/**
|
||||
* The standard function `fscanf` (and variations).
|
||||
*/
|
||||
class Fscanf extends ScanfFunction instanceof TopLevelFunction {
|
||||
class Fscanf extends ScanfFunction {
|
||||
Fscanf() {
|
||||
this.hasGlobalOrStdOrBslName("fscanf") or // fscanf(src_stream, format, args...)
|
||||
this.hasGlobalOrStdOrBslName("fwscanf") or // fwscanf(src_stream, format, args...)
|
||||
this.hasGlobalName("_fscanf_l") or // _fscanf_l(src_stream, format, locale, args...)
|
||||
this.hasGlobalName("_fwscanf_l")
|
||||
this instanceof TopLevelFunction and
|
||||
(
|
||||
this.hasGlobalOrStdOrBslName("fscanf") or // fscanf(src_stream, format, args...)
|
||||
this.hasGlobalOrStdOrBslName("fwscanf") or // fwscanf(src_stream, format, args...)
|
||||
this.hasGlobalName("_fscanf_l") or // _fscanf_l(src_stream, format, locale, args...)
|
||||
this.hasGlobalName("_fwscanf_l") // _fwscanf_l(src_stream, format, locale, args...)
|
||||
)
|
||||
}
|
||||
|
||||
override int getInputParameterIndex() { result = 0 }
|
||||
@@ -62,12 +68,15 @@ class Fscanf extends ScanfFunction instanceof TopLevelFunction {
|
||||
/**
|
||||
* The standard function `sscanf` (and variations).
|
||||
*/
|
||||
class Sscanf extends ScanfFunction instanceof TopLevelFunction {
|
||||
class Sscanf extends ScanfFunction {
|
||||
Sscanf() {
|
||||
this.hasGlobalOrStdOrBslName("sscanf") or // sscanf(src_stream, format, args...)
|
||||
this.hasGlobalOrStdOrBslName("swscanf") or // swscanf(src, format, args...)
|
||||
this.hasGlobalName("_sscanf_l") or // _sscanf_l(src, format, locale, args...)
|
||||
this.hasGlobalName("_swscanf_l")
|
||||
this instanceof TopLevelFunction and
|
||||
(
|
||||
this.hasGlobalOrStdOrBslName("sscanf") or // sscanf(src_stream, format, args...)
|
||||
this.hasGlobalOrStdOrBslName("swscanf") or // swscanf(src, format, args...)
|
||||
this.hasGlobalName("_sscanf_l") or // _sscanf_l(src, format, locale, args...)
|
||||
this.hasGlobalName("_swscanf_l") // _swscanf_l(src, format, locale, args...)
|
||||
)
|
||||
}
|
||||
|
||||
override int getInputParameterIndex() { result = 0 }
|
||||
@@ -78,12 +87,17 @@ class Sscanf extends ScanfFunction instanceof TopLevelFunction {
|
||||
/**
|
||||
* The standard(ish) function `snscanf` (and variations).
|
||||
*/
|
||||
class Snscanf extends ScanfFunction instanceof TopLevelFunction {
|
||||
class Snscanf extends ScanfFunction {
|
||||
Snscanf() {
|
||||
this.hasGlobalName("_snscanf") or // _snscanf(src, max_amount, format, args...)
|
||||
this.hasGlobalName("_snwscanf") or // _snwscanf(src, max_amount, format, args...)
|
||||
this.hasGlobalName("_snscanf_l") or // _snscanf_l(src, max_amount, format, locale, args...)
|
||||
this.hasGlobalName("_snwscanf_l")
|
||||
this instanceof TopLevelFunction and
|
||||
(
|
||||
this.hasGlobalName("_snscanf") or // _snscanf(src, max_amount, format, args...)
|
||||
this.hasGlobalName("_snwscanf") or // _snwscanf(src, max_amount, format, args...)
|
||||
this.hasGlobalName("_snscanf_l") or // _snscanf_l(src, max_amount, format, locale, args...)
|
||||
this.hasGlobalName("_snwscanf_l") // _snwscanf_l(src, max_amount, format, locale, args...)
|
||||
// note that the max_amount is not a limit on the output length, it's an input length
|
||||
// limit used with non null-terminated strings.
|
||||
)
|
||||
}
|
||||
|
||||
override int getInputParameterIndex() { result = 0 }
|
||||
|
||||
@@ -621,6 +621,23 @@ private predicate parameterFlowThroughAllowed(ParamNodeEx p, ReturnKindExt kind)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if flow from a parameter at position `pos` inside `c` to a return node of
|
||||
* kind `kind` is allowed.
|
||||
*
|
||||
* We don't expect a parameter to return stored in itself, unless
|
||||
* explicitly allowed
|
||||
*/
|
||||
bindingset[c, pos, kind]
|
||||
private predicate parameterFlowThroughAllowed(
|
||||
DataFlowCallable c, ParameterPosition pos, ReturnKindExt kind
|
||||
) {
|
||||
exists(ParamNodeEx p |
|
||||
p.isParameterOf(c, pos) and
|
||||
parameterFlowThroughAllowed(p, kind)
|
||||
)
|
||||
}
|
||||
|
||||
private module Stage1 implements StageSig {
|
||||
class Ap = Unit;
|
||||
|
||||
@@ -963,12 +980,6 @@ private module Stage1 implements StageSig {
|
||||
pragma[nomagic]
|
||||
predicate revFlow(NodeEx node, Configuration config) { revFlow(node, _, config) }
|
||||
|
||||
pragma[nomagic]
|
||||
predicate revFlowAp(NodeEx node, Ap ap, Configuration config) {
|
||||
revFlow(node, config) and
|
||||
exists(ap)
|
||||
}
|
||||
|
||||
bindingset[node, state, config]
|
||||
predicate revFlow(NodeEx node, FlowState state, Ap ap, Configuration config) {
|
||||
revFlow(node, _, pragma[only_bind_into](config)) and
|
||||
@@ -1011,13 +1022,9 @@ private module Stage1 implements StageSig {
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
predicate returnMayFlowThrough(
|
||||
RetNodeEx ret, Ap argAp, Ap ap, ReturnKindExt kind, Configuration config
|
||||
) {
|
||||
predicate returnMayFlowThrough(RetNodeEx ret, ReturnKindExt kind, Configuration config) {
|
||||
throughFlowNodeCand(ret, config) and
|
||||
kind = ret.getKind() and
|
||||
exists(argAp) and
|
||||
exists(ap)
|
||||
kind = ret.getKind()
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
@@ -1182,8 +1189,6 @@ private signature module StageSig {
|
||||
|
||||
predicate revFlow(NodeEx node, Configuration config);
|
||||
|
||||
predicate revFlowAp(NodeEx node, Ap ap, Configuration config);
|
||||
|
||||
bindingset[node, state, config]
|
||||
predicate revFlow(NodeEx node, FlowState state, Ap ap, Configuration config);
|
||||
|
||||
@@ -1191,9 +1196,7 @@ private signature module StageSig {
|
||||
|
||||
predicate parameterMayFlowThrough(ParamNodeEx p, Ap ap, Configuration config);
|
||||
|
||||
predicate returnMayFlowThrough(
|
||||
RetNodeEx ret, Ap argAp, Ap ap, ReturnKindExt kind, Configuration config
|
||||
);
|
||||
predicate returnMayFlowThrough(RetNodeEx ret, ReturnKindExt kind, Configuration config);
|
||||
|
||||
predicate storeStepCand(
|
||||
NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, DataFlowType contentType,
|
||||
@@ -1278,43 +1281,21 @@ private module MkStage<StageSig PrevStage> {
|
||||
import Param
|
||||
|
||||
/* Begin: Stage logic. */
|
||||
// use an alias as a workaround for bad functionality-induced joins
|
||||
pragma[nomagic]
|
||||
private predicate revFlowApAlias(NodeEx node, ApApprox apa, Configuration config) {
|
||||
PrevStage::revFlowAp(node, apa, config)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate flowIntoCallApa(
|
||||
DataFlowCall call, ArgNodeEx arg, ParamNodeEx p, boolean allowsFieldFlow, ApApprox apa,
|
||||
Configuration config
|
||||
) {
|
||||
flowIntoCall(call, arg, p, allowsFieldFlow, config) and
|
||||
PrevStage::revFlowAp(p, pragma[only_bind_into](apa), pragma[only_bind_into](config)) and
|
||||
revFlowApAlias(arg, pragma[only_bind_into](apa), pragma[only_bind_into](config))
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate flowOutOfCallApa(
|
||||
DataFlowCall call, RetNodeEx ret, ReturnKindExt kind, NodeEx out, boolean allowsFieldFlow,
|
||||
ApApprox apa, Configuration config
|
||||
) {
|
||||
flowOutOfCall(call, ret, kind, out, allowsFieldFlow, config) and
|
||||
PrevStage::revFlowAp(out, pragma[only_bind_into](apa), pragma[only_bind_into](config)) and
|
||||
revFlowApAlias(ret, pragma[only_bind_into](apa), pragma[only_bind_into](config))
|
||||
bindingset[result, apa]
|
||||
private ApApprox unbindApa(ApApprox apa) {
|
||||
pragma[only_bind_out](apa) = pragma[only_bind_out](result)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate flowThroughOutOfCall(
|
||||
DataFlowCall call, CcCall ccc, RetNodeEx ret, NodeEx out, boolean allowsFieldFlow,
|
||||
ApApprox argApa, ApApprox apa, Configuration config
|
||||
DataFlowCall call, DataFlowCallable c, CcCall ccc, RetNodeEx ret, ReturnKindExt kind,
|
||||
NodeEx out, boolean allowsFieldFlow, Configuration config
|
||||
) {
|
||||
exists(ReturnKindExt kind |
|
||||
flowOutOfCallApa(call, ret, kind, out, allowsFieldFlow, apa, pragma[only_bind_into](config)) and
|
||||
PrevStage::callMayFlowThroughRev(call, pragma[only_bind_into](config)) and
|
||||
PrevStage::returnMayFlowThrough(ret, argApa, apa, kind, pragma[only_bind_into](config)) and
|
||||
matchesCall(ccc, call)
|
||||
)
|
||||
flowOutOfCall(call, ret, kind, out, allowsFieldFlow, pragma[only_bind_into](config)) and
|
||||
PrevStage::callMayFlowThroughRev(call, pragma[only_bind_into](config)) and
|
||||
PrevStage::returnMayFlowThrough(ret, kind, pragma[only_bind_into](config)) and
|
||||
matchesCall(ccc, call) and
|
||||
c = ret.getEnclosingCallable()
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1326,50 +1307,39 @@ private module MkStage<StageSig PrevStage> {
|
||||
* corresponding parameter position and access path of that argument, respectively.
|
||||
*/
|
||||
pragma[nomagic]
|
||||
additional predicate fwdFlow(
|
||||
NodeEx node, FlowState state, Cc cc, ParameterPositionOption summaryCtx, ApOption argAp,
|
||||
Ap ap, ApApprox apa, Configuration config
|
||||
) {
|
||||
fwdFlow0(node, state, cc, summaryCtx, argAp, ap, apa, config) and
|
||||
PrevStage::revFlow(node, state, apa, config) and
|
||||
filter(node, state, ap, config)
|
||||
}
|
||||
|
||||
pragma[inline]
|
||||
additional predicate fwdFlow(
|
||||
NodeEx node, FlowState state, Cc cc, ParameterPositionOption summaryCtx, ApOption argAp,
|
||||
Ap ap, Configuration config
|
||||
) {
|
||||
fwdFlow(node, state, cc, summaryCtx, argAp, ap, _, config)
|
||||
fwdFlow0(node, state, cc, summaryCtx, argAp, ap, config) and
|
||||
PrevStage::revFlow(node, state, unbindApa(getApprox(ap)), config) and
|
||||
filter(node, state, ap, config)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate fwdFlow0(
|
||||
NodeEx node, FlowState state, Cc cc, ParameterPositionOption summaryCtx, ApOption argAp,
|
||||
Ap ap, ApApprox apa, Configuration config
|
||||
Ap ap, Configuration config
|
||||
) {
|
||||
sourceNode(node, state, config) and
|
||||
(if hasSourceCallCtx(config) then cc = ccSomeCall() else cc = ccNone()) and
|
||||
argAp = apNone() and
|
||||
summaryCtx = TParameterPositionNone() and
|
||||
ap = getApNil(node) and
|
||||
apa = getApprox(ap)
|
||||
ap = getApNil(node)
|
||||
or
|
||||
exists(NodeEx mid, FlowState state0, Ap ap0, ApApprox apa0, LocalCc localCc |
|
||||
fwdFlow(mid, state0, cc, summaryCtx, argAp, ap0, apa0, config) and
|
||||
exists(NodeEx mid, FlowState state0, Ap ap0, LocalCc localCc |
|
||||
fwdFlow(mid, state0, cc, summaryCtx, argAp, ap0, config) and
|
||||
localCc = getLocalCc(mid, cc)
|
||||
|
|
||||
localStep(mid, state0, node, state, true, _, config, localCc) and
|
||||
ap = ap0 and
|
||||
apa = apa0
|
||||
ap = ap0
|
||||
or
|
||||
localStep(mid, state0, node, state, false, ap, config, localCc) and
|
||||
ap0 instanceof ApNil and
|
||||
apa = getApprox(ap)
|
||||
ap0 instanceof ApNil
|
||||
)
|
||||
or
|
||||
exists(NodeEx mid |
|
||||
fwdFlow(mid, pragma[only_bind_into](state), _, _, _, ap, apa, pragma[only_bind_into](config)) and
|
||||
fwdFlow(mid, pragma[only_bind_into](state), _, _, _, ap, pragma[only_bind_into](config)) and
|
||||
jumpStep(mid, node, config) and
|
||||
cc = ccNone() and
|
||||
summaryCtx = TParameterPositionNone() and
|
||||
@@ -1382,8 +1352,7 @@ private module MkStage<StageSig PrevStage> {
|
||||
cc = ccNone() and
|
||||
summaryCtx = TParameterPositionNone() and
|
||||
argAp = apNone() and
|
||||
ap = getApNil(node) and
|
||||
apa = getApprox(ap)
|
||||
ap = getApNil(node)
|
||||
)
|
||||
or
|
||||
exists(NodeEx mid, FlowState state0, ApNil nil |
|
||||
@@ -1392,32 +1361,32 @@ private module MkStage<StageSig PrevStage> {
|
||||
cc = ccNone() and
|
||||
summaryCtx = TParameterPositionNone() and
|
||||
argAp = apNone() and
|
||||
ap = getApNil(node) and
|
||||
apa = getApprox(ap)
|
||||
ap = getApNil(node)
|
||||
)
|
||||
or
|
||||
// store
|
||||
exists(TypedContent tc, Ap ap0 |
|
||||
fwdFlowStore(_, ap0, tc, node, state, cc, summaryCtx, argAp, config) and
|
||||
ap = apCons(tc, ap0) and
|
||||
apa = getApprox(ap)
|
||||
ap = apCons(tc, ap0)
|
||||
)
|
||||
or
|
||||
// read
|
||||
exists(Ap ap0, Content c |
|
||||
fwdFlowRead(ap0, c, _, node, state, cc, summaryCtx, argAp, config) and
|
||||
fwdFlowConsCand(ap0, c, ap, config) and
|
||||
apa = getApprox(ap)
|
||||
fwdFlowConsCand(ap0, c, ap, config)
|
||||
)
|
||||
or
|
||||
// flow into a callable
|
||||
fwdFlowIn(_, node, state, _, cc, _, _, ap, apa, config) and
|
||||
if PrevStage::parameterMayFlowThrough(node, apa, config)
|
||||
then (
|
||||
summaryCtx = TParameterPositionSome(node.(ParamNodeEx).getPosition()) and
|
||||
argAp = apSome(ap)
|
||||
) else (
|
||||
summaryCtx = TParameterPositionNone() and argAp = apNone()
|
||||
exists(ApApprox apa |
|
||||
fwdFlowIn(_, node, state, _, cc, _, _, ap, config) and
|
||||
apa = getApprox(ap) and
|
||||
if PrevStage::parameterMayFlowThrough(node, apa, config)
|
||||
then (
|
||||
summaryCtx = TParameterPositionSome(node.(ParamNodeEx).getPosition()) and
|
||||
argAp = apSome(ap)
|
||||
) else (
|
||||
summaryCtx = TParameterPositionNone() and argAp = apNone()
|
||||
)
|
||||
)
|
||||
or
|
||||
// flow out of a callable
|
||||
@@ -1425,8 +1394,8 @@ private module MkStage<StageSig PrevStage> {
|
||||
DataFlowCall call, RetNodeEx ret, boolean allowsFieldFlow, CcNoCall innercc,
|
||||
DataFlowCallable inner
|
||||
|
|
||||
fwdFlow(ret, state, innercc, summaryCtx, argAp, ap, apa, config) and
|
||||
flowOutOfCallApa(call, ret, _, node, allowsFieldFlow, apa, config) and
|
||||
fwdFlow(ret, state, innercc, summaryCtx, argAp, ap, config) and
|
||||
flowOutOfCall(call, ret, _, node, allowsFieldFlow, config) and
|
||||
inner = ret.getEnclosingCallable() and
|
||||
cc = getCallContextReturn(inner, call, innercc) and
|
||||
if allowsFieldFlow = false then ap instanceof ApNil else any()
|
||||
@@ -1434,7 +1403,7 @@ private module MkStage<StageSig PrevStage> {
|
||||
or
|
||||
// flow through a callable
|
||||
exists(DataFlowCall call, ParameterPosition summaryCtx0, Ap argAp0 |
|
||||
fwdFlowOutFromArg(call, node, state, summaryCtx0, argAp0, ap, apa, config) and
|
||||
fwdFlowOutFromArg(call, node, state, summaryCtx0, argAp0, ap, config) and
|
||||
fwdFlowIsEntered(call, cc, summaryCtx, argAp, summaryCtx0, argAp0, config)
|
||||
)
|
||||
}
|
||||
@@ -1444,9 +1413,9 @@ private module MkStage<StageSig PrevStage> {
|
||||
NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, FlowState state, Cc cc,
|
||||
ParameterPositionOption summaryCtx, ApOption argAp, Configuration config
|
||||
) {
|
||||
exists(DataFlowType contentType, ApApprox apa1 |
|
||||
fwdFlow(node1, state, cc, summaryCtx, argAp, ap1, apa1, config) and
|
||||
PrevStage::storeStepCand(node1, apa1, tc, node2, contentType, config) and
|
||||
exists(DataFlowType contentType |
|
||||
fwdFlow(node1, state, cc, summaryCtx, argAp, ap1, config) and
|
||||
PrevStage::storeStepCand(node1, unbindApa(getApprox(ap1)), tc, node2, contentType, config) and
|
||||
typecheckStore(ap1, contentType)
|
||||
)
|
||||
}
|
||||
@@ -1464,104 +1433,43 @@ private module MkStage<StageSig PrevStage> {
|
||||
)
|
||||
}
|
||||
|
||||
private class ApNonNil instanceof Ap {
|
||||
pragma[nomagic]
|
||||
ApNonNil() { not this instanceof ApNil }
|
||||
|
||||
string toString() { result = "" }
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate fwdFlowRead0(
|
||||
NodeEx node1, FlowState state, Cc cc, ParameterPositionOption summaryCtx, ApOption argAp,
|
||||
ApNonNil ap, Configuration config
|
||||
) {
|
||||
fwdFlow(node1, state, cc, summaryCtx, argAp, ap, config) and
|
||||
PrevStage::readStepCand(node1, _, _, config)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate fwdFlowRead(
|
||||
Ap ap, Content c, NodeEx node1, NodeEx node2, FlowState state, Cc cc,
|
||||
ParameterPositionOption summaryCtx, ApOption argAp, Configuration config
|
||||
) {
|
||||
fwdFlowRead0(node1, state, cc, summaryCtx, argAp, ap, config) and
|
||||
fwdFlow(node1, state, cc, summaryCtx, argAp, ap, config) and
|
||||
PrevStage::readStepCand(node1, c, node2, config) and
|
||||
getHeadContent(ap) = c
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate fwdFlowIn(
|
||||
DataFlowCall call, ParamNodeEx p, FlowState state, Cc outercc, CcCall innercc,
|
||||
ParameterPositionOption summaryCtx, ApOption argAp, Ap ap, ApApprox apa, Configuration config
|
||||
DataFlowCall call, ParamNodeEx p, FlowState state, Cc outercc, Cc innercc,
|
||||
ParameterPositionOption summaryCtx, ApOption argAp, Ap ap, Configuration config
|
||||
) {
|
||||
exists(ArgNodeEx arg, boolean allowsFieldFlow |
|
||||
fwdFlow(arg, state, outercc, summaryCtx, argAp, ap, apa, config) and
|
||||
flowIntoCallApa(call, arg, p, allowsFieldFlow, apa, config) and
|
||||
fwdFlow(arg, state, outercc, summaryCtx, argAp, ap, config) and
|
||||
flowIntoCall(call, arg, p, allowsFieldFlow, config) and
|
||||
innercc = getCallContextCall(call, p.getEnclosingCallable(), outercc) and
|
||||
if allowsFieldFlow = false then ap instanceof ApNil else any()
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate fwdFlowRetFromArg(
|
||||
RetNodeEx ret, FlowState state, CcCall ccc, ParameterPosition summaryCtx, ParamNodeEx p,
|
||||
Ap argAp, ApApprox argApa, Ap ap, ApApprox apa, Configuration config
|
||||
) {
|
||||
exists(DataFlowCallable c, ReturnKindExt kind |
|
||||
fwdFlow(pragma[only_bind_into](ret), state, ccc,
|
||||
TParameterPositionSome(pragma[only_bind_into](summaryCtx)), apSome(argAp), ap, apa, config) and
|
||||
getApprox(argAp) = argApa and
|
||||
c = ret.getEnclosingCallable() and
|
||||
kind = ret.getKind() and
|
||||
p.isParameterOf(c, pragma[only_bind_into](summaryCtx)) and
|
||||
parameterFlowThroughAllowed(p, kind)
|
||||
)
|
||||
}
|
||||
|
||||
pragma[inline]
|
||||
private predicate fwdFlowInMayFlowThrough(
|
||||
DataFlowCall call, Cc cc, CcCall innerCc, ParameterPositionOption summaryCtx, ApOption argAp,
|
||||
ParamNodeEx param, Ap ap, ApApprox apa, Configuration config
|
||||
) {
|
||||
fwdFlowIn(call, pragma[only_bind_into](param), _, cc, innerCc, summaryCtx, argAp, ap,
|
||||
pragma[only_bind_into](apa), pragma[only_bind_into](config)) and
|
||||
PrevStage::parameterMayFlowThrough(param, apa, config)
|
||||
}
|
||||
|
||||
// dedup before joining with `flowThroughOutOfCall`
|
||||
pragma[nomagic]
|
||||
private predicate fwdFlowInMayFlowThroughProj(
|
||||
DataFlowCall call, CcCall innerCc, ApApprox apa, Configuration config
|
||||
) {
|
||||
fwdFlowInMayFlowThrough(call, _, innerCc, _, _, _, _, apa, config)
|
||||
}
|
||||
|
||||
/**
|
||||
* Same as `flowThroughOutOfCall`, but restricted to calls that are reached
|
||||
* in the flow covered by `fwdFlow`, where data might flow through the target
|
||||
* callable and back out at `call`.
|
||||
*/
|
||||
pragma[nomagic]
|
||||
private predicate fwdFlowThroughOutOfCall(
|
||||
DataFlowCall call, CcCall ccc, RetNodeEx ret, NodeEx out, boolean allowsFieldFlow,
|
||||
ApApprox argApa, ApApprox apa, Configuration config
|
||||
) {
|
||||
fwdFlowInMayFlowThroughProj(call, ccc, argApa, config) and
|
||||
flowThroughOutOfCall(call, ccc, ret, out, allowsFieldFlow, argApa, apa, config)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate fwdFlowOutFromArg(
|
||||
DataFlowCall call, NodeEx out, FlowState state, ParameterPosition summaryCtx, Ap argAp, Ap ap,
|
||||
ApApprox apa, Configuration config
|
||||
Configuration config
|
||||
) {
|
||||
exists(RetNodeEx ret, boolean allowsFieldFlow, CcCall ccc, ApApprox argApa |
|
||||
fwdFlowRetFromArg(pragma[only_bind_into](ret), state, pragma[only_bind_into](ccc),
|
||||
summaryCtx, _, argAp, pragma[only_bind_into](argApa), ap, pragma[only_bind_into](apa),
|
||||
exists(
|
||||
DataFlowCallable c, RetNodeEx ret, ReturnKindExt kind, boolean allowsFieldFlow, CcCall ccc
|
||||
|
|
||||
fwdFlow(pragma[only_bind_into](ret), state, pragma[only_bind_into](ccc),
|
||||
TParameterPositionSome(pragma[only_bind_into](summaryCtx)), apSome(argAp), ap, config) and
|
||||
flowThroughOutOfCall(call, pragma[only_bind_into](c), ccc, ret, kind, out, allowsFieldFlow,
|
||||
config) and
|
||||
fwdFlowThroughOutOfCall(call, ccc, ret, out, allowsFieldFlow, argApa, apa, config) and
|
||||
(if allowsFieldFlow = false then ap instanceof ApNil else any())
|
||||
(if allowsFieldFlow = false then ap instanceof ApNil else any()) and
|
||||
parameterFlowThroughAllowed(c, pragma[only_bind_into](summaryCtx), kind)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1575,7 +1483,8 @@ private module MkStage<StageSig PrevStage> {
|
||||
ParameterPosition pos, Ap ap, Configuration config
|
||||
) {
|
||||
exists(ParamNodeEx param |
|
||||
fwdFlowInMayFlowThrough(call, cc, _, summaryCtx, argAp, param, ap, _, config) and
|
||||
fwdFlowIn(call, param, _, cc, _, summaryCtx, argAp, ap, config) and
|
||||
PrevStage::parameterMayFlowThrough(param, unbindApa(getApprox(ap)), config) and
|
||||
pos = param.getPosition()
|
||||
)
|
||||
}
|
||||
@@ -1596,55 +1505,41 @@ private module MkStage<StageSig PrevStage> {
|
||||
fwdFlowConsCand(ap1, c, ap2, config)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate returnFlowsThrough0(
|
||||
RetNodeEx ret, ReturnKindExt kind, FlowState state, CcCall ccc, DataFlowCallable c,
|
||||
ParameterPosition ppos, Ap argAp, Ap ap, Configuration config
|
||||
) {
|
||||
exists(boolean allowsFieldFlow |
|
||||
fwdFlow(ret, state, ccc, TParameterPositionSome(ppos), apSome(argAp), ap, config) and
|
||||
flowThroughOutOfCall(_, c, _, pragma[only_bind_into](ret), kind, _, allowsFieldFlow,
|
||||
pragma[only_bind_into](config)) and
|
||||
if allowsFieldFlow = false then ap instanceof ApNil else any()
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate returnFlowsThrough(
|
||||
RetNodeEx ret, ReturnKindExt kind, FlowState state, CcCall ccc, ParamNodeEx p, Ap argAp,
|
||||
Ap ap, Configuration config
|
||||
) {
|
||||
exists(boolean allowsFieldFlow, ApApprox argApa, ApApprox apa |
|
||||
fwdFlowRetFromArg(pragma[only_bind_into](ret), state, pragma[only_bind_into](ccc), _, p,
|
||||
argAp, pragma[only_bind_into](argApa), ap, pragma[only_bind_into](apa), config) and
|
||||
kind = ret.getKind() and
|
||||
fwdFlowThroughOutOfCall(_, ccc, ret, _, allowsFieldFlow, argApa, apa, config) and
|
||||
(if allowsFieldFlow = false then ap instanceof ApNil else any())
|
||||
exists(DataFlowCallable c, ParameterPosition ppos |
|
||||
returnFlowsThrough0(ret, kind, state, ccc, c, ppos, argAp, ap, config) and
|
||||
p.isParameterOf(c, ppos) and
|
||||
parameterFlowThroughAllowed(p, kind)
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate flowThroughIntoCall(
|
||||
DataFlowCall call, ArgNodeEx arg, ParamNodeEx p, boolean allowsFieldFlow, Ap argAp,
|
||||
Configuration config
|
||||
DataFlowCall call, ArgNodeEx arg, ParamNodeEx p, boolean allowsFieldFlow, Configuration config
|
||||
) {
|
||||
exists(ApApprox argApa |
|
||||
flowIntoCallApa(call, pragma[only_bind_into](arg), pragma[only_bind_into](p),
|
||||
allowsFieldFlow, argApa, pragma[only_bind_into](config)) and
|
||||
fwdFlow(arg, _, _, _, _, pragma[only_bind_into](argAp), argApa,
|
||||
exists(Ap argAp |
|
||||
flowIntoCall(call, pragma[only_bind_into](arg), pragma[only_bind_into](p), allowsFieldFlow,
|
||||
pragma[only_bind_into](config)) and
|
||||
fwdFlow(arg, _, _, _, _, pragma[only_bind_into](argAp), pragma[only_bind_into](config)) and
|
||||
returnFlowsThrough(_, _, _, _, p, pragma[only_bind_into](argAp), _,
|
||||
pragma[only_bind_into](config)) and
|
||||
if allowsFieldFlow = false then argAp instanceof ApNil else any()
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate flowIntoCallAp(
|
||||
DataFlowCall call, ArgNodeEx arg, ParamNodeEx p, boolean allowsFieldFlow, Ap ap,
|
||||
Configuration config
|
||||
) {
|
||||
exists(ApApprox apa |
|
||||
flowIntoCallApa(call, arg, p, allowsFieldFlow, apa, config) and
|
||||
fwdFlow(arg, _, _, _, _, ap, apa, config)
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate flowOutOfCallAp(
|
||||
DataFlowCall call, RetNodeEx ret, ReturnKindExt kind, NodeEx out, boolean allowsFieldFlow,
|
||||
Ap ap, Configuration config
|
||||
) {
|
||||
exists(ApApprox apa |
|
||||
flowOutOfCallApa(call, ret, kind, out, allowsFieldFlow, apa, config) and
|
||||
fwdFlow(ret, _, _, _, _, ap, apa, config)
|
||||
pragma[only_bind_into](config))
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1733,7 +1628,7 @@ private module MkStage<StageSig PrevStage> {
|
||||
// flow into a callable
|
||||
exists(ParamNodeEx p, boolean allowsFieldFlow |
|
||||
revFlow(p, state, TReturnCtxNone(), returnAp, ap, config) and
|
||||
flowIntoCallAp(_, node, p, allowsFieldFlow, ap, config) and
|
||||
flowIntoCall(_, node, p, allowsFieldFlow, config) and
|
||||
(if allowsFieldFlow = false then ap instanceof ApNil else any()) and
|
||||
returnCtx = TReturnCtxNone()
|
||||
)
|
||||
@@ -1787,41 +1682,22 @@ private module MkStage<StageSig PrevStage> {
|
||||
) {
|
||||
exists(NodeEx out, boolean allowsFieldFlow |
|
||||
revFlow(out, state, returnCtx, returnAp, ap, config) and
|
||||
flowOutOfCallAp(call, ret, kind, out, allowsFieldFlow, ap, config) and
|
||||
flowOutOfCall(call, ret, kind, out, allowsFieldFlow, config) and
|
||||
if allowsFieldFlow = false then ap instanceof ApNil else any()
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Same as `flowThroughIntoCall`, but restricted to calls that are reached
|
||||
* in the flow covered by `revFlow`, where data might flow through the target
|
||||
* callable and back out at `call`.
|
||||
*/
|
||||
pragma[nomagic]
|
||||
private predicate revFlowThroughIntoCall(
|
||||
DataFlowCall call, ArgNodeEx arg, ParamNodeEx p, boolean allowsFieldFlow, Ap argAp,
|
||||
Configuration config
|
||||
) {
|
||||
flowThroughIntoCall(call, arg, p, allowsFieldFlow, argAp, config) and
|
||||
revFlowIsReturned(call, _, _, _, _, config)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate revFlowParamToReturn(
|
||||
ParamNodeEx p, FlowState state, ReturnKindExt kind, Ap returnAp, Ap ap, Configuration config
|
||||
) {
|
||||
revFlow(p, state, TReturnCtxMaybeFlowThrough(kind), apSome(returnAp), ap, config) and
|
||||
parameterFlowThroughAllowed(p, kind)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate revFlowInToReturn(
|
||||
DataFlowCall call, ArgNodeEx arg, FlowState state, ReturnKindExt kind, Ap returnAp, Ap ap,
|
||||
Configuration config
|
||||
) {
|
||||
exists(ParamNodeEx p, boolean allowsFieldFlow |
|
||||
revFlowParamToReturn(p, state, kind, returnAp, ap, config) and
|
||||
revFlowThroughIntoCall(call, arg, p, allowsFieldFlow, ap, config)
|
||||
revFlow(pragma[only_bind_into](p), state,
|
||||
TReturnCtxMaybeFlowThrough(pragma[only_bind_into](kind)), apSome(returnAp), ap, config) and
|
||||
flowThroughIntoCall(call, arg, p, allowsFieldFlow, config) and
|
||||
(if allowsFieldFlow = false then ap instanceof ApNil else any()) and
|
||||
parameterFlowThroughAllowed(p, kind)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1874,11 +1750,6 @@ private module MkStage<StageSig PrevStage> {
|
||||
pragma[nomagic]
|
||||
predicate revFlow(NodeEx node, Configuration config) { revFlow(node, _, _, _, _, config) }
|
||||
|
||||
pragma[nomagic]
|
||||
predicate revFlowAp(NodeEx node, Ap ap, Configuration config) {
|
||||
revFlow(node, _, _, _, ap, config)
|
||||
}
|
||||
|
||||
// use an alias as a workaround for bad functionality-induced joins
|
||||
pragma[nomagic]
|
||||
additional predicate revFlowAlias(NodeEx node, Configuration config) {
|
||||
@@ -1915,9 +1786,9 @@ private module MkStage<StageSig PrevStage> {
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate parameterFlowsThroughRev(
|
||||
ParamNodeEx p, Ap ap, ReturnKindExt kind, Ap returnAp, Configuration config
|
||||
ParamNodeEx p, Ap ap, ReturnKindExt kind, Configuration config
|
||||
) {
|
||||
revFlow(p, _, TReturnCtxMaybeFlowThrough(kind), apSome(returnAp), ap, config) and
|
||||
revFlow(p, _, TReturnCtxMaybeFlowThrough(kind), apSome(_), ap, config) and
|
||||
parameterFlowThroughAllowed(p, kind)
|
||||
}
|
||||
|
||||
@@ -1925,36 +1796,27 @@ private module MkStage<StageSig PrevStage> {
|
||||
predicate parameterMayFlowThrough(ParamNodeEx p, Ap ap, Configuration config) {
|
||||
exists(RetNodeEx ret, ReturnKindExt kind |
|
||||
returnFlowsThrough(ret, kind, _, _, p, ap, _, config) and
|
||||
parameterFlowsThroughRev(p, ap, kind, _, config)
|
||||
parameterFlowsThroughRev(p, ap, kind, config)
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
predicate returnMayFlowThrough(
|
||||
RetNodeEx ret, Ap argAp, Ap ap, ReturnKindExt kind, Configuration config
|
||||
) {
|
||||
exists(ParamNodeEx p |
|
||||
returnFlowsThrough(ret, kind, _, _, p, argAp, ap, config) and
|
||||
parameterFlowsThroughRev(p, argAp, kind, ap, config)
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
predicate revFlowInToReturnIsReturned(
|
||||
DataFlowCall call, ArgNodeEx arg, FlowState state, ReturnCtx returnCtx, ApOption returnAp,
|
||||
Ap ap, Configuration config
|
||||
) {
|
||||
exists(ReturnKindExt returnKind0, Ap returnAp0 |
|
||||
revFlowInToReturn(call, arg, state, returnKind0, returnAp0, ap, config) and
|
||||
revFlowIsReturned(call, returnCtx, returnAp, returnKind0, returnAp0, config)
|
||||
predicate returnMayFlowThrough(RetNodeEx ret, ReturnKindExt kind, Configuration config) {
|
||||
exists(ParamNodeEx p, Ap ap |
|
||||
returnFlowsThrough(ret, kind, _, _, p, ap, _, config) and
|
||||
parameterFlowsThroughRev(p, ap, kind, config)
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
predicate callMayFlowThroughRev(DataFlowCall call, Configuration config) {
|
||||
exists(ArgNodeEx arg, FlowState state, ReturnCtx returnCtx, ApOption returnAp, Ap ap |
|
||||
exists(
|
||||
ReturnKindExt returnKind0, Ap returnAp0, ArgNodeEx arg, FlowState state,
|
||||
ReturnCtx returnCtx, ApOption returnAp, Ap ap
|
||||
|
|
||||
revFlow(arg, state, returnCtx, returnAp, ap, config) and
|
||||
revFlowInToReturnIsReturned(call, arg, state, returnCtx, returnAp, ap, config)
|
||||
revFlowInToReturn(call, arg, state, returnKind0, returnAp0, ap, config) and
|
||||
revFlowIsReturned(call, returnCtx, returnAp, returnKind0, returnAp0, config)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -2317,16 +2179,16 @@ private module LocalFlowBigStep {
|
||||
pragma[nomagic]
|
||||
predicate localFlowBigStep(
|
||||
NodeEx node1, FlowState state1, NodeEx node2, FlowState state2, boolean preservesValue,
|
||||
DataFlowType t, Configuration config, LocalCallContext callContext
|
||||
AccessPathFrontNil apf, Configuration config, LocalCallContext callContext
|
||||
) {
|
||||
localFlowStepPlus(node1, state1, node2, preservesValue, t, config, callContext) and
|
||||
localFlowStepPlus(node1, state1, node2, preservesValue, apf.getType(), config, callContext) and
|
||||
localFlowExit(node2, state1, config) and
|
||||
state1 = state2
|
||||
or
|
||||
additionalLocalFlowStepNodeCand2(node1, state1, node2, state2, config) and
|
||||
state1 != state2 and
|
||||
preservesValue = false and
|
||||
t = node2.getDataFlowType() and
|
||||
apf = TFrontNil(node2.getDataFlowType()) and
|
||||
callContext.relevantFor(node1.getEnclosingCallable()) and
|
||||
not exists(DataFlowCall call | call = callContext.(LocalCallContextSpecificCall).getCall() |
|
||||
isUnreachableInCallCached(node1.asNode(), call) or
|
||||
@@ -2340,87 +2202,11 @@ private import LocalFlowBigStep
|
||||
private module Stage3Param implements MkStage<Stage2>::StageParam {
|
||||
private module PrevStage = Stage2;
|
||||
|
||||
class Ap = ApproxAccessPathFront;
|
||||
|
||||
class ApNil = ApproxAccessPathFrontNil;
|
||||
|
||||
PrevStage::Ap getApprox(Ap ap) { result = ap.toBoolNonEmpty() }
|
||||
|
||||
ApNil getApNil(NodeEx node) {
|
||||
PrevStage::revFlow(node, _) and result = TApproxFrontNil(node.getDataFlowType())
|
||||
}
|
||||
|
||||
bindingset[tc, tail]
|
||||
Ap apCons(TypedContent tc, Ap tail) { result.getAHead() = tc and exists(tail) }
|
||||
|
||||
pragma[noinline]
|
||||
Content getHeadContent(Ap ap) { result = ap.getAHead().getContent() }
|
||||
|
||||
class ApOption = ApproxAccessPathFrontOption;
|
||||
|
||||
ApOption apNone() { result = TApproxAccessPathFrontNone() }
|
||||
|
||||
ApOption apSome(Ap ap) { result = TApproxAccessPathFrontSome(ap) }
|
||||
|
||||
import BooleanCallContext
|
||||
|
||||
predicate localStep(
|
||||
NodeEx node1, FlowState state1, NodeEx node2, FlowState state2, boolean preservesValue,
|
||||
ApproxAccessPathFrontNil ap, Configuration config, LocalCc lcc
|
||||
) {
|
||||
localFlowBigStep(node1, state1, node2, state2, preservesValue, ap.getType(), config, _) and
|
||||
exists(lcc)
|
||||
}
|
||||
|
||||
predicate flowOutOfCall = flowOutOfCallNodeCand2/6;
|
||||
|
||||
predicate flowIntoCall = flowIntoCallNodeCand2/5;
|
||||
|
||||
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.getAHead().getContent()
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate castingNodeEx(NodeEx node) { node.asNode() instanceof CastingNode }
|
||||
|
||||
bindingset[node, state, ap, config]
|
||||
predicate filter(NodeEx node, FlowState state, Ap ap, Configuration config) {
|
||||
exists(state) and
|
||||
exists(config) and
|
||||
(if castingNodeEx(node) then compatibleTypes(node.getDataFlowType(), ap.getType()) else any()) and
|
||||
(
|
||||
notExpectsContent(node)
|
||||
or
|
||||
expectsContentCand(node, ap, config)
|
||||
)
|
||||
}
|
||||
|
||||
bindingset[ap, contentType]
|
||||
predicate typecheckStore(Ap ap, DataFlowType contentType) {
|
||||
// We need to typecheck stores here, since reverse flow through a getter
|
||||
// might have a different type here compared to inside the getter.
|
||||
compatibleTypes(ap.getType(), contentType)
|
||||
}
|
||||
}
|
||||
|
||||
private module Stage3 implements StageSig {
|
||||
import MkStage<Stage2>::Stage<Stage3Param>
|
||||
}
|
||||
|
||||
private module Stage4Param implements MkStage<Stage3>::StageParam {
|
||||
private module PrevStage = Stage3;
|
||||
|
||||
class Ap = AccessPathFront;
|
||||
|
||||
class ApNil = AccessPathFrontNil;
|
||||
|
||||
PrevStage::Ap getApprox(Ap ap) { result = ap.toApprox() }
|
||||
PrevStage::Ap getApprox(Ap ap) { result = ap.toBoolNonEmpty() }
|
||||
|
||||
ApNil getApNil(NodeEx node) {
|
||||
PrevStage::revFlow(node, _) and result = TFrontNil(node.getDataFlowType())
|
||||
@@ -2440,42 +2226,16 @@ private module Stage4Param implements MkStage<Stage3>::StageParam {
|
||||
|
||||
import BooleanCallContext
|
||||
|
||||
pragma[nomagic]
|
||||
predicate localStep(
|
||||
NodeEx node1, FlowState state1, NodeEx node2, FlowState state2, boolean preservesValue,
|
||||
ApNil ap, Configuration config, LocalCc lcc
|
||||
) {
|
||||
localFlowBigStep(node1, state1, node2, state2, preservesValue, ap.getType(), config, _) and
|
||||
PrevStage::revFlow(node1, pragma[only_bind_into](state1), _, pragma[only_bind_into](config)) and
|
||||
PrevStage::revFlowAlias(node2, pragma[only_bind_into](state2), _, pragma[only_bind_into](config)) and
|
||||
exists(lcc)
|
||||
localFlowBigStep(node1, state1, node2, state2, preservesValue, ap, config, _) and exists(lcc)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
predicate flowOutOfCall(
|
||||
DataFlowCall call, RetNodeEx node1, ReturnKindExt kind, NodeEx node2, boolean allowsFieldFlow,
|
||||
Configuration config
|
||||
) {
|
||||
exists(FlowState state |
|
||||
flowOutOfCallNodeCand2(call, node1, kind, node2, allowsFieldFlow, config) and
|
||||
PrevStage::revFlow(node2, pragma[only_bind_into](state), _, pragma[only_bind_into](config)) and
|
||||
PrevStage::revFlowAlias(node1, pragma[only_bind_into](state), _,
|
||||
pragma[only_bind_into](config))
|
||||
)
|
||||
}
|
||||
predicate flowOutOfCall = flowOutOfCallNodeCand2/6;
|
||||
|
||||
pragma[nomagic]
|
||||
predicate flowIntoCall(
|
||||
DataFlowCall call, ArgNodeEx node1, ParamNodeEx node2, boolean allowsFieldFlow,
|
||||
Configuration config
|
||||
) {
|
||||
exists(FlowState state |
|
||||
flowIntoCallNodeCand2(call, node1, node2, allowsFieldFlow, config) and
|
||||
PrevStage::revFlow(node2, pragma[only_bind_into](state), _, pragma[only_bind_into](config)) and
|
||||
PrevStage::revFlowAlias(node1, pragma[only_bind_into](state), _,
|
||||
pragma[only_bind_into](config))
|
||||
)
|
||||
}
|
||||
predicate flowIntoCall = flowIntoCallNodeCand2/5;
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate clearSet(NodeEx node, ContentSet c, Configuration config) {
|
||||
@@ -2531,8 +2291,8 @@ private module Stage4Param implements MkStage<Stage3>::StageParam {
|
||||
}
|
||||
}
|
||||
|
||||
private module Stage4 implements StageSig {
|
||||
import MkStage<Stage3>::Stage<Stage4Param>
|
||||
private module Stage3 implements StageSig {
|
||||
import MkStage<Stage2>::Stage<Stage3Param>
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -2543,8 +2303,8 @@ private predicate flowCandSummaryCtx(
|
||||
NodeEx node, FlowState state, AccessPathFront argApf, Configuration config
|
||||
) {
|
||||
exists(AccessPathFront apf |
|
||||
Stage4::revFlow(node, state, TReturnCtxMaybeFlowThrough(_), _, apf, config) and
|
||||
Stage4::fwdFlow(node, state, any(Stage4::CcCall ccc), _, TAccessPathFrontSome(argApf), apf,
|
||||
Stage3::revFlow(node, state, TReturnCtxMaybeFlowThrough(_), _, apf, config) and
|
||||
Stage3::fwdFlow(node, state, any(Stage3::CcCall ccc), _, TAccessPathFrontSome(argApf), apf,
|
||||
config)
|
||||
)
|
||||
}
|
||||
@@ -2555,10 +2315,10 @@ private predicate flowCandSummaryCtx(
|
||||
*/
|
||||
private predicate expensiveLen2unfolding(TypedContent tc, Configuration config) {
|
||||
exists(int tails, int nodes, int apLimit, int tupleLimit |
|
||||
tails = strictcount(AccessPathFront apf | Stage4::consCand(tc, apf, config)) and
|
||||
tails = strictcount(AccessPathFront apf | Stage3::consCand(tc, apf, config)) and
|
||||
nodes =
|
||||
strictcount(NodeEx n, FlowState state |
|
||||
Stage4::revFlow(n, state, any(AccessPathFrontHead apf | apf.getHead() = tc), config)
|
||||
Stage3::revFlow(n, state, any(AccessPathFrontHead apf | apf.getHead() = tc), config)
|
||||
or
|
||||
flowCandSummaryCtx(n, state, any(AccessPathFrontHead apf | apf.getHead() = tc), config)
|
||||
) and
|
||||
@@ -2572,11 +2332,11 @@ private predicate expensiveLen2unfolding(TypedContent tc, Configuration config)
|
||||
private newtype TAccessPathApprox =
|
||||
TNil(DataFlowType t) or
|
||||
TConsNil(TypedContent tc, DataFlowType t) {
|
||||
Stage4::consCand(tc, TFrontNil(t), _) and
|
||||
Stage3::consCand(tc, TFrontNil(t), _) and
|
||||
not expensiveLen2unfolding(tc, _)
|
||||
} or
|
||||
TConsCons(TypedContent tc1, TypedContent tc2, int len) {
|
||||
Stage4::consCand(tc1, TFrontHead(tc2), _) and
|
||||
Stage3::consCand(tc1, TFrontHead(tc2), _) and
|
||||
len in [2 .. accessPathLimit()] and
|
||||
not expensiveLen2unfolding(tc1, _)
|
||||
} or
|
||||
@@ -2706,7 +2466,7 @@ private class AccessPathApproxCons1 extends AccessPathApproxCons, TCons1 {
|
||||
override AccessPathApprox pop(TypedContent head) {
|
||||
head = tc and
|
||||
(
|
||||
exists(TypedContent tc2 | Stage4::consCand(tc, TFrontHead(tc2), _) |
|
||||
exists(TypedContent tc2 | Stage3::consCand(tc, TFrontHead(tc2), _) |
|
||||
result = TConsCons(tc2, _, len - 1)
|
||||
or
|
||||
len = 2 and
|
||||
@@ -2717,7 +2477,7 @@ private class AccessPathApproxCons1 extends AccessPathApproxCons, TCons1 {
|
||||
or
|
||||
exists(DataFlowType t |
|
||||
len = 1 and
|
||||
Stage4::consCand(tc, TFrontNil(t), _) and
|
||||
Stage3::consCand(tc, TFrontNil(t), _) and
|
||||
result = TNil(t)
|
||||
)
|
||||
)
|
||||
@@ -2742,14 +2502,13 @@ private class AccessPathApproxOption extends TAccessPathApproxOption {
|
||||
}
|
||||
}
|
||||
|
||||
private module Stage5Param implements MkStage<Stage4>::StageParam {
|
||||
private module PrevStage = Stage4;
|
||||
private module Stage4Param implements MkStage<Stage3>::StageParam {
|
||||
private module PrevStage = Stage3;
|
||||
|
||||
class Ap = AccessPathApprox;
|
||||
|
||||
class ApNil = AccessPathApproxNil;
|
||||
|
||||
pragma[nomagic]
|
||||
PrevStage::Ap getApprox(Ap ap) { result = ap.getFront() }
|
||||
|
||||
ApNil getApNil(NodeEx node) {
|
||||
@@ -2775,9 +2534,7 @@ private module Stage5Param implements MkStage<Stage4>::StageParam {
|
||||
NodeEx node1, FlowState state1, NodeEx node2, FlowState state2, boolean preservesValue,
|
||||
ApNil ap, Configuration config, LocalCc lcc
|
||||
) {
|
||||
localFlowBigStep(node1, state1, node2, state2, preservesValue, ap.getType(), config, lcc) and
|
||||
PrevStage::revFlow(node1, pragma[only_bind_into](state1), _, pragma[only_bind_into](config)) and
|
||||
PrevStage::revFlowAlias(node2, pragma[only_bind_into](state2), _, pragma[only_bind_into](config))
|
||||
localFlowBigStep(node1, state1, node2, state2, preservesValue, ap.getFront(), config, lcc)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
@@ -2814,7 +2571,7 @@ private module Stage5Param implements MkStage<Stage4>::StageParam {
|
||||
predicate typecheckStore(Ap ap, DataFlowType contentType) { any() }
|
||||
}
|
||||
|
||||
private module Stage5 = MkStage<Stage4>::Stage<Stage5Param>;
|
||||
private module Stage4 = MkStage<Stage3>::Stage<Stage4Param>;
|
||||
|
||||
bindingset[conf, result]
|
||||
private Configuration unbindConf(Configuration conf) {
|
||||
@@ -2828,8 +2585,8 @@ private predicate nodeMayUseSummary0(
|
||||
) {
|
||||
exists(AccessPathApprox apa0 |
|
||||
c = n.getEnclosingCallable() and
|
||||
Stage5::revFlow(n, state, TReturnCtxMaybeFlowThrough(_), _, apa0, config) and
|
||||
Stage5::fwdFlow(n, state, any(CallContextCall ccc), TParameterPositionSome(pos),
|
||||
Stage4::revFlow(n, state, TReturnCtxMaybeFlowThrough(_), _, apa0, config) and
|
||||
Stage4::fwdFlow(n, state, any(CallContextCall ccc), TParameterPositionSome(pos),
|
||||
TAccessPathApproxSome(apa), apa0, config)
|
||||
)
|
||||
}
|
||||
@@ -2839,7 +2596,7 @@ private predicate nodeMayUseSummary(
|
||||
NodeEx n, FlowState state, AccessPathApprox apa, Configuration config
|
||||
) {
|
||||
exists(DataFlowCallable c, ParameterPosition pos, ParamNodeEx p |
|
||||
Stage5::parameterMayFlowThrough(p, apa, config) and
|
||||
Stage4::parameterMayFlowThrough(p, apa, config) and
|
||||
nodeMayUseSummary0(n, c, pos, state, apa, config) and
|
||||
p.isParameterOf(c, pos)
|
||||
)
|
||||
@@ -2849,8 +2606,8 @@ private newtype TSummaryCtx =
|
||||
TSummaryCtxNone() or
|
||||
TSummaryCtxSome(ParamNodeEx p, FlowState state, AccessPath ap) {
|
||||
exists(Configuration config |
|
||||
Stage5::parameterMayFlowThrough(p, ap.getApprox(), config) and
|
||||
Stage5::revFlow(p, state, _, config)
|
||||
Stage4::parameterMayFlowThrough(p, ap.getApprox(), config) and
|
||||
Stage4::revFlow(p, state, _, config)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -2899,7 +2656,7 @@ private int count1to2unfold(AccessPathApproxCons1 apa, Configuration config) {
|
||||
len = apa.len() and
|
||||
result =
|
||||
strictcount(AccessPathFront apf |
|
||||
Stage5::consCand(tc, any(AccessPathApprox ap | ap.getFront() = apf and ap.len() = len - 1),
|
||||
Stage4::consCand(tc, any(AccessPathApprox ap | ap.getFront() = apf and ap.len() = len - 1),
|
||||
config)
|
||||
)
|
||||
)
|
||||
@@ -2908,7 +2665,7 @@ private int count1to2unfold(AccessPathApproxCons1 apa, Configuration config) {
|
||||
private int countNodesUsingAccessPath(AccessPathApprox apa, Configuration config) {
|
||||
result =
|
||||
strictcount(NodeEx n, FlowState state |
|
||||
Stage5::revFlow(n, state, apa, config) or nodeMayUseSummary(n, state, apa, config)
|
||||
Stage4::revFlow(n, state, apa, config) or nodeMayUseSummary(n, state, apa, config)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -2929,7 +2686,7 @@ private predicate expensiveLen1to2unfolding(AccessPathApproxCons1 apa, Configura
|
||||
private AccessPathApprox getATail(AccessPathApprox apa, Configuration config) {
|
||||
exists(TypedContent head |
|
||||
apa.pop(head) = result and
|
||||
Stage5::consCand(head, result, config)
|
||||
Stage4::consCand(head, result, config)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -3011,7 +2768,7 @@ private newtype TPathNode =
|
||||
NodeEx node, FlowState state, CallContext cc, SummaryCtx sc, AccessPath ap, Configuration config
|
||||
) {
|
||||
// A PathNode is introduced by a source ...
|
||||
Stage5::revFlow(node, state, config) and
|
||||
Stage4::revFlow(node, state, config) and
|
||||
sourceNode(node, state, config) and
|
||||
(
|
||||
if hasSourceCallCtx(config)
|
||||
@@ -3025,7 +2782,7 @@ private newtype TPathNode =
|
||||
exists(PathNodeMid mid |
|
||||
pathStep(mid, node, state, cc, sc, ap) and
|
||||
pragma[only_bind_into](config) = mid.getConfiguration() and
|
||||
Stage5::revFlow(node, state, ap.getApprox(), pragma[only_bind_into](config))
|
||||
Stage4::revFlow(node, state, ap.getApprox(), pragma[only_bind_into](config))
|
||||
)
|
||||
} or
|
||||
TPathNodeSink(NodeEx node, FlowState state, Configuration config) {
|
||||
@@ -3167,7 +2924,7 @@ private class AccessPathCons2 extends AccessPath, TAccessPathCons2 {
|
||||
override TypedContent getHead() { result = head1 }
|
||||
|
||||
override AccessPath getTail() {
|
||||
Stage5::consCand(head1, result.getApprox(), _) and
|
||||
Stage4::consCand(head1, result.getApprox(), _) and
|
||||
result.getHead() = head2 and
|
||||
result.length() = len - 1
|
||||
}
|
||||
@@ -3198,7 +2955,7 @@ private class AccessPathCons1 extends AccessPath, TAccessPathCons1 {
|
||||
override TypedContent getHead() { result = head }
|
||||
|
||||
override AccessPath getTail() {
|
||||
Stage5::consCand(head, result.getApprox(), _) and result.length() = len - 1
|
||||
Stage4::consCand(head, result.getApprox(), _) and result.length() = len - 1
|
||||
}
|
||||
|
||||
override AccessPathFrontHead getFront() { result = TFrontHead(head) }
|
||||
@@ -3590,8 +3347,7 @@ private predicate pathStep(
|
||||
AccessPath ap0, NodeEx midnode, FlowState state0, Configuration conf, LocalCallContext localCC
|
||||
|
|
||||
pathNode(mid, midnode, state0, cc, sc, ap0, conf, localCC) and
|
||||
localFlowBigStep(midnode, state0, node, state, false, ap.(AccessPathNil).getType(), conf,
|
||||
localCC) and
|
||||
localFlowBigStep(midnode, state0, node, state, false, ap.getFront(), conf, localCC) and
|
||||
ap0 instanceof AccessPathNil
|
||||
)
|
||||
or
|
||||
@@ -3633,7 +3389,7 @@ private predicate pathReadStep(
|
||||
) {
|
||||
ap0 = mid.getAp() and
|
||||
tc = ap0.getHead() and
|
||||
Stage5::readStepCand(mid.getNodeEx(), tc.getContent(), node, mid.getConfiguration()) and
|
||||
Stage4::readStepCand(mid.getNodeEx(), tc.getContent(), node, mid.getConfiguration()) and
|
||||
state = mid.getState() and
|
||||
cc = mid.getCallContext()
|
||||
}
|
||||
@@ -3643,7 +3399,7 @@ private predicate pathStoreStep(
|
||||
PathNodeMid mid, NodeEx node, FlowState state, AccessPath ap0, TypedContent tc, CallContext cc
|
||||
) {
|
||||
ap0 = mid.getAp() and
|
||||
Stage5::storeStepCand(mid.getNodeEx(), _, tc, node, _, mid.getConfiguration()) and
|
||||
Stage4::storeStepCand(mid.getNodeEx(), _, tc, node, _, mid.getConfiguration()) and
|
||||
state = mid.getState() and
|
||||
cc = mid.getCallContext()
|
||||
}
|
||||
@@ -3680,7 +3436,7 @@ private NodeEx getAnOutNodeFlow(
|
||||
ReturnKindExt kind, DataFlowCall call, AccessPathApprox apa, Configuration config
|
||||
) {
|
||||
result.asNode() = kind.getAnOutNode(call) and
|
||||
Stage5::revFlow(result, _, apa, config)
|
||||
Stage4::revFlow(result, _, apa, config)
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -3716,7 +3472,7 @@ private predicate parameterCand(
|
||||
DataFlowCallable callable, ParameterPosition pos, AccessPathApprox apa, Configuration config
|
||||
) {
|
||||
exists(ParamNodeEx p |
|
||||
Stage5::revFlow(p, _, apa, config) and
|
||||
Stage4::revFlow(p, _, apa, config) and
|
||||
p.isParameterOf(callable, pos)
|
||||
)
|
||||
}
|
||||
@@ -3995,17 +3751,9 @@ predicate stageStats(
|
||||
n = 45 and
|
||||
Stage4::stats(false, nodes, fields, conscand, states, tuples, config)
|
||||
or
|
||||
stage = "5 Fwd" and
|
||||
n = 50 and
|
||||
Stage5::stats(true, nodes, fields, conscand, states, tuples, config)
|
||||
stage = "5 Fwd" and n = 50 and finalStats(true, nodes, fields, conscand, states, tuples)
|
||||
or
|
||||
stage = "5 Rev" and
|
||||
n = 55 and
|
||||
Stage5::stats(false, nodes, fields, conscand, states, tuples, config)
|
||||
or
|
||||
stage = "6 Fwd" and n = 60 and finalStats(true, nodes, fields, conscand, states, tuples)
|
||||
or
|
||||
stage = "6 Rev" and n = 65 and finalStats(false, nodes, fields, conscand, states, tuples)
|
||||
stage = "5 Rev" and n = 55 and finalStats(false, nodes, fields, conscand, states, tuples)
|
||||
}
|
||||
|
||||
private module FlowExploration {
|
||||
|
||||
@@ -621,6 +621,23 @@ private predicate parameterFlowThroughAllowed(ParamNodeEx p, ReturnKindExt kind)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if flow from a parameter at position `pos` inside `c` to a return node of
|
||||
* kind `kind` is allowed.
|
||||
*
|
||||
* We don't expect a parameter to return stored in itself, unless
|
||||
* explicitly allowed
|
||||
*/
|
||||
bindingset[c, pos, kind]
|
||||
private predicate parameterFlowThroughAllowed(
|
||||
DataFlowCallable c, ParameterPosition pos, ReturnKindExt kind
|
||||
) {
|
||||
exists(ParamNodeEx p |
|
||||
p.isParameterOf(c, pos) and
|
||||
parameterFlowThroughAllowed(p, kind)
|
||||
)
|
||||
}
|
||||
|
||||
private module Stage1 implements StageSig {
|
||||
class Ap = Unit;
|
||||
|
||||
@@ -963,12 +980,6 @@ private module Stage1 implements StageSig {
|
||||
pragma[nomagic]
|
||||
predicate revFlow(NodeEx node, Configuration config) { revFlow(node, _, config) }
|
||||
|
||||
pragma[nomagic]
|
||||
predicate revFlowAp(NodeEx node, Ap ap, Configuration config) {
|
||||
revFlow(node, config) and
|
||||
exists(ap)
|
||||
}
|
||||
|
||||
bindingset[node, state, config]
|
||||
predicate revFlow(NodeEx node, FlowState state, Ap ap, Configuration config) {
|
||||
revFlow(node, _, pragma[only_bind_into](config)) and
|
||||
@@ -1011,13 +1022,9 @@ private module Stage1 implements StageSig {
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
predicate returnMayFlowThrough(
|
||||
RetNodeEx ret, Ap argAp, Ap ap, ReturnKindExt kind, Configuration config
|
||||
) {
|
||||
predicate returnMayFlowThrough(RetNodeEx ret, ReturnKindExt kind, Configuration config) {
|
||||
throughFlowNodeCand(ret, config) and
|
||||
kind = ret.getKind() and
|
||||
exists(argAp) and
|
||||
exists(ap)
|
||||
kind = ret.getKind()
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
@@ -1182,8 +1189,6 @@ private signature module StageSig {
|
||||
|
||||
predicate revFlow(NodeEx node, Configuration config);
|
||||
|
||||
predicate revFlowAp(NodeEx node, Ap ap, Configuration config);
|
||||
|
||||
bindingset[node, state, config]
|
||||
predicate revFlow(NodeEx node, FlowState state, Ap ap, Configuration config);
|
||||
|
||||
@@ -1191,9 +1196,7 @@ private signature module StageSig {
|
||||
|
||||
predicate parameterMayFlowThrough(ParamNodeEx p, Ap ap, Configuration config);
|
||||
|
||||
predicate returnMayFlowThrough(
|
||||
RetNodeEx ret, Ap argAp, Ap ap, ReturnKindExt kind, Configuration config
|
||||
);
|
||||
predicate returnMayFlowThrough(RetNodeEx ret, ReturnKindExt kind, Configuration config);
|
||||
|
||||
predicate storeStepCand(
|
||||
NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, DataFlowType contentType,
|
||||
@@ -1278,43 +1281,21 @@ private module MkStage<StageSig PrevStage> {
|
||||
import Param
|
||||
|
||||
/* Begin: Stage logic. */
|
||||
// use an alias as a workaround for bad functionality-induced joins
|
||||
pragma[nomagic]
|
||||
private predicate revFlowApAlias(NodeEx node, ApApprox apa, Configuration config) {
|
||||
PrevStage::revFlowAp(node, apa, config)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate flowIntoCallApa(
|
||||
DataFlowCall call, ArgNodeEx arg, ParamNodeEx p, boolean allowsFieldFlow, ApApprox apa,
|
||||
Configuration config
|
||||
) {
|
||||
flowIntoCall(call, arg, p, allowsFieldFlow, config) and
|
||||
PrevStage::revFlowAp(p, pragma[only_bind_into](apa), pragma[only_bind_into](config)) and
|
||||
revFlowApAlias(arg, pragma[only_bind_into](apa), pragma[only_bind_into](config))
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate flowOutOfCallApa(
|
||||
DataFlowCall call, RetNodeEx ret, ReturnKindExt kind, NodeEx out, boolean allowsFieldFlow,
|
||||
ApApprox apa, Configuration config
|
||||
) {
|
||||
flowOutOfCall(call, ret, kind, out, allowsFieldFlow, config) and
|
||||
PrevStage::revFlowAp(out, pragma[only_bind_into](apa), pragma[only_bind_into](config)) and
|
||||
revFlowApAlias(ret, pragma[only_bind_into](apa), pragma[only_bind_into](config))
|
||||
bindingset[result, apa]
|
||||
private ApApprox unbindApa(ApApprox apa) {
|
||||
pragma[only_bind_out](apa) = pragma[only_bind_out](result)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate flowThroughOutOfCall(
|
||||
DataFlowCall call, CcCall ccc, RetNodeEx ret, NodeEx out, boolean allowsFieldFlow,
|
||||
ApApprox argApa, ApApprox apa, Configuration config
|
||||
DataFlowCall call, DataFlowCallable c, CcCall ccc, RetNodeEx ret, ReturnKindExt kind,
|
||||
NodeEx out, boolean allowsFieldFlow, Configuration config
|
||||
) {
|
||||
exists(ReturnKindExt kind |
|
||||
flowOutOfCallApa(call, ret, kind, out, allowsFieldFlow, apa, pragma[only_bind_into](config)) and
|
||||
PrevStage::callMayFlowThroughRev(call, pragma[only_bind_into](config)) and
|
||||
PrevStage::returnMayFlowThrough(ret, argApa, apa, kind, pragma[only_bind_into](config)) and
|
||||
matchesCall(ccc, call)
|
||||
)
|
||||
flowOutOfCall(call, ret, kind, out, allowsFieldFlow, pragma[only_bind_into](config)) and
|
||||
PrevStage::callMayFlowThroughRev(call, pragma[only_bind_into](config)) and
|
||||
PrevStage::returnMayFlowThrough(ret, kind, pragma[only_bind_into](config)) and
|
||||
matchesCall(ccc, call) and
|
||||
c = ret.getEnclosingCallable()
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1326,50 +1307,39 @@ private module MkStage<StageSig PrevStage> {
|
||||
* corresponding parameter position and access path of that argument, respectively.
|
||||
*/
|
||||
pragma[nomagic]
|
||||
additional predicate fwdFlow(
|
||||
NodeEx node, FlowState state, Cc cc, ParameterPositionOption summaryCtx, ApOption argAp,
|
||||
Ap ap, ApApprox apa, Configuration config
|
||||
) {
|
||||
fwdFlow0(node, state, cc, summaryCtx, argAp, ap, apa, config) and
|
||||
PrevStage::revFlow(node, state, apa, config) and
|
||||
filter(node, state, ap, config)
|
||||
}
|
||||
|
||||
pragma[inline]
|
||||
additional predicate fwdFlow(
|
||||
NodeEx node, FlowState state, Cc cc, ParameterPositionOption summaryCtx, ApOption argAp,
|
||||
Ap ap, Configuration config
|
||||
) {
|
||||
fwdFlow(node, state, cc, summaryCtx, argAp, ap, _, config)
|
||||
fwdFlow0(node, state, cc, summaryCtx, argAp, ap, config) and
|
||||
PrevStage::revFlow(node, state, unbindApa(getApprox(ap)), config) and
|
||||
filter(node, state, ap, config)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate fwdFlow0(
|
||||
NodeEx node, FlowState state, Cc cc, ParameterPositionOption summaryCtx, ApOption argAp,
|
||||
Ap ap, ApApprox apa, Configuration config
|
||||
Ap ap, Configuration config
|
||||
) {
|
||||
sourceNode(node, state, config) and
|
||||
(if hasSourceCallCtx(config) then cc = ccSomeCall() else cc = ccNone()) and
|
||||
argAp = apNone() and
|
||||
summaryCtx = TParameterPositionNone() and
|
||||
ap = getApNil(node) and
|
||||
apa = getApprox(ap)
|
||||
ap = getApNil(node)
|
||||
or
|
||||
exists(NodeEx mid, FlowState state0, Ap ap0, ApApprox apa0, LocalCc localCc |
|
||||
fwdFlow(mid, state0, cc, summaryCtx, argAp, ap0, apa0, config) and
|
||||
exists(NodeEx mid, FlowState state0, Ap ap0, LocalCc localCc |
|
||||
fwdFlow(mid, state0, cc, summaryCtx, argAp, ap0, config) and
|
||||
localCc = getLocalCc(mid, cc)
|
||||
|
|
||||
localStep(mid, state0, node, state, true, _, config, localCc) and
|
||||
ap = ap0 and
|
||||
apa = apa0
|
||||
ap = ap0
|
||||
or
|
||||
localStep(mid, state0, node, state, false, ap, config, localCc) and
|
||||
ap0 instanceof ApNil and
|
||||
apa = getApprox(ap)
|
||||
ap0 instanceof ApNil
|
||||
)
|
||||
or
|
||||
exists(NodeEx mid |
|
||||
fwdFlow(mid, pragma[only_bind_into](state), _, _, _, ap, apa, pragma[only_bind_into](config)) and
|
||||
fwdFlow(mid, pragma[only_bind_into](state), _, _, _, ap, pragma[only_bind_into](config)) and
|
||||
jumpStep(mid, node, config) and
|
||||
cc = ccNone() and
|
||||
summaryCtx = TParameterPositionNone() and
|
||||
@@ -1382,8 +1352,7 @@ private module MkStage<StageSig PrevStage> {
|
||||
cc = ccNone() and
|
||||
summaryCtx = TParameterPositionNone() and
|
||||
argAp = apNone() and
|
||||
ap = getApNil(node) and
|
||||
apa = getApprox(ap)
|
||||
ap = getApNil(node)
|
||||
)
|
||||
or
|
||||
exists(NodeEx mid, FlowState state0, ApNil nil |
|
||||
@@ -1392,32 +1361,32 @@ private module MkStage<StageSig PrevStage> {
|
||||
cc = ccNone() and
|
||||
summaryCtx = TParameterPositionNone() and
|
||||
argAp = apNone() and
|
||||
ap = getApNil(node) and
|
||||
apa = getApprox(ap)
|
||||
ap = getApNil(node)
|
||||
)
|
||||
or
|
||||
// store
|
||||
exists(TypedContent tc, Ap ap0 |
|
||||
fwdFlowStore(_, ap0, tc, node, state, cc, summaryCtx, argAp, config) and
|
||||
ap = apCons(tc, ap0) and
|
||||
apa = getApprox(ap)
|
||||
ap = apCons(tc, ap0)
|
||||
)
|
||||
or
|
||||
// read
|
||||
exists(Ap ap0, Content c |
|
||||
fwdFlowRead(ap0, c, _, node, state, cc, summaryCtx, argAp, config) and
|
||||
fwdFlowConsCand(ap0, c, ap, config) and
|
||||
apa = getApprox(ap)
|
||||
fwdFlowConsCand(ap0, c, ap, config)
|
||||
)
|
||||
or
|
||||
// flow into a callable
|
||||
fwdFlowIn(_, node, state, _, cc, _, _, ap, apa, config) and
|
||||
if PrevStage::parameterMayFlowThrough(node, apa, config)
|
||||
then (
|
||||
summaryCtx = TParameterPositionSome(node.(ParamNodeEx).getPosition()) and
|
||||
argAp = apSome(ap)
|
||||
) else (
|
||||
summaryCtx = TParameterPositionNone() and argAp = apNone()
|
||||
exists(ApApprox apa |
|
||||
fwdFlowIn(_, node, state, _, cc, _, _, ap, config) and
|
||||
apa = getApprox(ap) and
|
||||
if PrevStage::parameterMayFlowThrough(node, apa, config)
|
||||
then (
|
||||
summaryCtx = TParameterPositionSome(node.(ParamNodeEx).getPosition()) and
|
||||
argAp = apSome(ap)
|
||||
) else (
|
||||
summaryCtx = TParameterPositionNone() and argAp = apNone()
|
||||
)
|
||||
)
|
||||
or
|
||||
// flow out of a callable
|
||||
@@ -1425,8 +1394,8 @@ private module MkStage<StageSig PrevStage> {
|
||||
DataFlowCall call, RetNodeEx ret, boolean allowsFieldFlow, CcNoCall innercc,
|
||||
DataFlowCallable inner
|
||||
|
|
||||
fwdFlow(ret, state, innercc, summaryCtx, argAp, ap, apa, config) and
|
||||
flowOutOfCallApa(call, ret, _, node, allowsFieldFlow, apa, config) and
|
||||
fwdFlow(ret, state, innercc, summaryCtx, argAp, ap, config) and
|
||||
flowOutOfCall(call, ret, _, node, allowsFieldFlow, config) and
|
||||
inner = ret.getEnclosingCallable() and
|
||||
cc = getCallContextReturn(inner, call, innercc) and
|
||||
if allowsFieldFlow = false then ap instanceof ApNil else any()
|
||||
@@ -1434,7 +1403,7 @@ private module MkStage<StageSig PrevStage> {
|
||||
or
|
||||
// flow through a callable
|
||||
exists(DataFlowCall call, ParameterPosition summaryCtx0, Ap argAp0 |
|
||||
fwdFlowOutFromArg(call, node, state, summaryCtx0, argAp0, ap, apa, config) and
|
||||
fwdFlowOutFromArg(call, node, state, summaryCtx0, argAp0, ap, config) and
|
||||
fwdFlowIsEntered(call, cc, summaryCtx, argAp, summaryCtx0, argAp0, config)
|
||||
)
|
||||
}
|
||||
@@ -1444,9 +1413,9 @@ private module MkStage<StageSig PrevStage> {
|
||||
NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, FlowState state, Cc cc,
|
||||
ParameterPositionOption summaryCtx, ApOption argAp, Configuration config
|
||||
) {
|
||||
exists(DataFlowType contentType, ApApprox apa1 |
|
||||
fwdFlow(node1, state, cc, summaryCtx, argAp, ap1, apa1, config) and
|
||||
PrevStage::storeStepCand(node1, apa1, tc, node2, contentType, config) and
|
||||
exists(DataFlowType contentType |
|
||||
fwdFlow(node1, state, cc, summaryCtx, argAp, ap1, config) and
|
||||
PrevStage::storeStepCand(node1, unbindApa(getApprox(ap1)), tc, node2, contentType, config) and
|
||||
typecheckStore(ap1, contentType)
|
||||
)
|
||||
}
|
||||
@@ -1464,104 +1433,43 @@ private module MkStage<StageSig PrevStage> {
|
||||
)
|
||||
}
|
||||
|
||||
private class ApNonNil instanceof Ap {
|
||||
pragma[nomagic]
|
||||
ApNonNil() { not this instanceof ApNil }
|
||||
|
||||
string toString() { result = "" }
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate fwdFlowRead0(
|
||||
NodeEx node1, FlowState state, Cc cc, ParameterPositionOption summaryCtx, ApOption argAp,
|
||||
ApNonNil ap, Configuration config
|
||||
) {
|
||||
fwdFlow(node1, state, cc, summaryCtx, argAp, ap, config) and
|
||||
PrevStage::readStepCand(node1, _, _, config)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate fwdFlowRead(
|
||||
Ap ap, Content c, NodeEx node1, NodeEx node2, FlowState state, Cc cc,
|
||||
ParameterPositionOption summaryCtx, ApOption argAp, Configuration config
|
||||
) {
|
||||
fwdFlowRead0(node1, state, cc, summaryCtx, argAp, ap, config) and
|
||||
fwdFlow(node1, state, cc, summaryCtx, argAp, ap, config) and
|
||||
PrevStage::readStepCand(node1, c, node2, config) and
|
||||
getHeadContent(ap) = c
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate fwdFlowIn(
|
||||
DataFlowCall call, ParamNodeEx p, FlowState state, Cc outercc, CcCall innercc,
|
||||
ParameterPositionOption summaryCtx, ApOption argAp, Ap ap, ApApprox apa, Configuration config
|
||||
DataFlowCall call, ParamNodeEx p, FlowState state, Cc outercc, Cc innercc,
|
||||
ParameterPositionOption summaryCtx, ApOption argAp, Ap ap, Configuration config
|
||||
) {
|
||||
exists(ArgNodeEx arg, boolean allowsFieldFlow |
|
||||
fwdFlow(arg, state, outercc, summaryCtx, argAp, ap, apa, config) and
|
||||
flowIntoCallApa(call, arg, p, allowsFieldFlow, apa, config) and
|
||||
fwdFlow(arg, state, outercc, summaryCtx, argAp, ap, config) and
|
||||
flowIntoCall(call, arg, p, allowsFieldFlow, config) and
|
||||
innercc = getCallContextCall(call, p.getEnclosingCallable(), outercc) and
|
||||
if allowsFieldFlow = false then ap instanceof ApNil else any()
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate fwdFlowRetFromArg(
|
||||
RetNodeEx ret, FlowState state, CcCall ccc, ParameterPosition summaryCtx, ParamNodeEx p,
|
||||
Ap argAp, ApApprox argApa, Ap ap, ApApprox apa, Configuration config
|
||||
) {
|
||||
exists(DataFlowCallable c, ReturnKindExt kind |
|
||||
fwdFlow(pragma[only_bind_into](ret), state, ccc,
|
||||
TParameterPositionSome(pragma[only_bind_into](summaryCtx)), apSome(argAp), ap, apa, config) and
|
||||
getApprox(argAp) = argApa and
|
||||
c = ret.getEnclosingCallable() and
|
||||
kind = ret.getKind() and
|
||||
p.isParameterOf(c, pragma[only_bind_into](summaryCtx)) and
|
||||
parameterFlowThroughAllowed(p, kind)
|
||||
)
|
||||
}
|
||||
|
||||
pragma[inline]
|
||||
private predicate fwdFlowInMayFlowThrough(
|
||||
DataFlowCall call, Cc cc, CcCall innerCc, ParameterPositionOption summaryCtx, ApOption argAp,
|
||||
ParamNodeEx param, Ap ap, ApApprox apa, Configuration config
|
||||
) {
|
||||
fwdFlowIn(call, pragma[only_bind_into](param), _, cc, innerCc, summaryCtx, argAp, ap,
|
||||
pragma[only_bind_into](apa), pragma[only_bind_into](config)) and
|
||||
PrevStage::parameterMayFlowThrough(param, apa, config)
|
||||
}
|
||||
|
||||
// dedup before joining with `flowThroughOutOfCall`
|
||||
pragma[nomagic]
|
||||
private predicate fwdFlowInMayFlowThroughProj(
|
||||
DataFlowCall call, CcCall innerCc, ApApprox apa, Configuration config
|
||||
) {
|
||||
fwdFlowInMayFlowThrough(call, _, innerCc, _, _, _, _, apa, config)
|
||||
}
|
||||
|
||||
/**
|
||||
* Same as `flowThroughOutOfCall`, but restricted to calls that are reached
|
||||
* in the flow covered by `fwdFlow`, where data might flow through the target
|
||||
* callable and back out at `call`.
|
||||
*/
|
||||
pragma[nomagic]
|
||||
private predicate fwdFlowThroughOutOfCall(
|
||||
DataFlowCall call, CcCall ccc, RetNodeEx ret, NodeEx out, boolean allowsFieldFlow,
|
||||
ApApprox argApa, ApApprox apa, Configuration config
|
||||
) {
|
||||
fwdFlowInMayFlowThroughProj(call, ccc, argApa, config) and
|
||||
flowThroughOutOfCall(call, ccc, ret, out, allowsFieldFlow, argApa, apa, config)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate fwdFlowOutFromArg(
|
||||
DataFlowCall call, NodeEx out, FlowState state, ParameterPosition summaryCtx, Ap argAp, Ap ap,
|
||||
ApApprox apa, Configuration config
|
||||
Configuration config
|
||||
) {
|
||||
exists(RetNodeEx ret, boolean allowsFieldFlow, CcCall ccc, ApApprox argApa |
|
||||
fwdFlowRetFromArg(pragma[only_bind_into](ret), state, pragma[only_bind_into](ccc),
|
||||
summaryCtx, _, argAp, pragma[only_bind_into](argApa), ap, pragma[only_bind_into](apa),
|
||||
exists(
|
||||
DataFlowCallable c, RetNodeEx ret, ReturnKindExt kind, boolean allowsFieldFlow, CcCall ccc
|
||||
|
|
||||
fwdFlow(pragma[only_bind_into](ret), state, pragma[only_bind_into](ccc),
|
||||
TParameterPositionSome(pragma[only_bind_into](summaryCtx)), apSome(argAp), ap, config) and
|
||||
flowThroughOutOfCall(call, pragma[only_bind_into](c), ccc, ret, kind, out, allowsFieldFlow,
|
||||
config) and
|
||||
fwdFlowThroughOutOfCall(call, ccc, ret, out, allowsFieldFlow, argApa, apa, config) and
|
||||
(if allowsFieldFlow = false then ap instanceof ApNil else any())
|
||||
(if allowsFieldFlow = false then ap instanceof ApNil else any()) and
|
||||
parameterFlowThroughAllowed(c, pragma[only_bind_into](summaryCtx), kind)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1575,7 +1483,8 @@ private module MkStage<StageSig PrevStage> {
|
||||
ParameterPosition pos, Ap ap, Configuration config
|
||||
) {
|
||||
exists(ParamNodeEx param |
|
||||
fwdFlowInMayFlowThrough(call, cc, _, summaryCtx, argAp, param, ap, _, config) and
|
||||
fwdFlowIn(call, param, _, cc, _, summaryCtx, argAp, ap, config) and
|
||||
PrevStage::parameterMayFlowThrough(param, unbindApa(getApprox(ap)), config) and
|
||||
pos = param.getPosition()
|
||||
)
|
||||
}
|
||||
@@ -1596,55 +1505,41 @@ private module MkStage<StageSig PrevStage> {
|
||||
fwdFlowConsCand(ap1, c, ap2, config)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate returnFlowsThrough0(
|
||||
RetNodeEx ret, ReturnKindExt kind, FlowState state, CcCall ccc, DataFlowCallable c,
|
||||
ParameterPosition ppos, Ap argAp, Ap ap, Configuration config
|
||||
) {
|
||||
exists(boolean allowsFieldFlow |
|
||||
fwdFlow(ret, state, ccc, TParameterPositionSome(ppos), apSome(argAp), ap, config) and
|
||||
flowThroughOutOfCall(_, c, _, pragma[only_bind_into](ret), kind, _, allowsFieldFlow,
|
||||
pragma[only_bind_into](config)) and
|
||||
if allowsFieldFlow = false then ap instanceof ApNil else any()
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate returnFlowsThrough(
|
||||
RetNodeEx ret, ReturnKindExt kind, FlowState state, CcCall ccc, ParamNodeEx p, Ap argAp,
|
||||
Ap ap, Configuration config
|
||||
) {
|
||||
exists(boolean allowsFieldFlow, ApApprox argApa, ApApprox apa |
|
||||
fwdFlowRetFromArg(pragma[only_bind_into](ret), state, pragma[only_bind_into](ccc), _, p,
|
||||
argAp, pragma[only_bind_into](argApa), ap, pragma[only_bind_into](apa), config) and
|
||||
kind = ret.getKind() and
|
||||
fwdFlowThroughOutOfCall(_, ccc, ret, _, allowsFieldFlow, argApa, apa, config) and
|
||||
(if allowsFieldFlow = false then ap instanceof ApNil else any())
|
||||
exists(DataFlowCallable c, ParameterPosition ppos |
|
||||
returnFlowsThrough0(ret, kind, state, ccc, c, ppos, argAp, ap, config) and
|
||||
p.isParameterOf(c, ppos) and
|
||||
parameterFlowThroughAllowed(p, kind)
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate flowThroughIntoCall(
|
||||
DataFlowCall call, ArgNodeEx arg, ParamNodeEx p, boolean allowsFieldFlow, Ap argAp,
|
||||
Configuration config
|
||||
DataFlowCall call, ArgNodeEx arg, ParamNodeEx p, boolean allowsFieldFlow, Configuration config
|
||||
) {
|
||||
exists(ApApprox argApa |
|
||||
flowIntoCallApa(call, pragma[only_bind_into](arg), pragma[only_bind_into](p),
|
||||
allowsFieldFlow, argApa, pragma[only_bind_into](config)) and
|
||||
fwdFlow(arg, _, _, _, _, pragma[only_bind_into](argAp), argApa,
|
||||
exists(Ap argAp |
|
||||
flowIntoCall(call, pragma[only_bind_into](arg), pragma[only_bind_into](p), allowsFieldFlow,
|
||||
pragma[only_bind_into](config)) and
|
||||
fwdFlow(arg, _, _, _, _, pragma[only_bind_into](argAp), pragma[only_bind_into](config)) and
|
||||
returnFlowsThrough(_, _, _, _, p, pragma[only_bind_into](argAp), _,
|
||||
pragma[only_bind_into](config)) and
|
||||
if allowsFieldFlow = false then argAp instanceof ApNil else any()
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate flowIntoCallAp(
|
||||
DataFlowCall call, ArgNodeEx arg, ParamNodeEx p, boolean allowsFieldFlow, Ap ap,
|
||||
Configuration config
|
||||
) {
|
||||
exists(ApApprox apa |
|
||||
flowIntoCallApa(call, arg, p, allowsFieldFlow, apa, config) and
|
||||
fwdFlow(arg, _, _, _, _, ap, apa, config)
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate flowOutOfCallAp(
|
||||
DataFlowCall call, RetNodeEx ret, ReturnKindExt kind, NodeEx out, boolean allowsFieldFlow,
|
||||
Ap ap, Configuration config
|
||||
) {
|
||||
exists(ApApprox apa |
|
||||
flowOutOfCallApa(call, ret, kind, out, allowsFieldFlow, apa, config) and
|
||||
fwdFlow(ret, _, _, _, _, ap, apa, config)
|
||||
pragma[only_bind_into](config))
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1733,7 +1628,7 @@ private module MkStage<StageSig PrevStage> {
|
||||
// flow into a callable
|
||||
exists(ParamNodeEx p, boolean allowsFieldFlow |
|
||||
revFlow(p, state, TReturnCtxNone(), returnAp, ap, config) and
|
||||
flowIntoCallAp(_, node, p, allowsFieldFlow, ap, config) and
|
||||
flowIntoCall(_, node, p, allowsFieldFlow, config) and
|
||||
(if allowsFieldFlow = false then ap instanceof ApNil else any()) and
|
||||
returnCtx = TReturnCtxNone()
|
||||
)
|
||||
@@ -1787,41 +1682,22 @@ private module MkStage<StageSig PrevStage> {
|
||||
) {
|
||||
exists(NodeEx out, boolean allowsFieldFlow |
|
||||
revFlow(out, state, returnCtx, returnAp, ap, config) and
|
||||
flowOutOfCallAp(call, ret, kind, out, allowsFieldFlow, ap, config) and
|
||||
flowOutOfCall(call, ret, kind, out, allowsFieldFlow, config) and
|
||||
if allowsFieldFlow = false then ap instanceof ApNil else any()
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Same as `flowThroughIntoCall`, but restricted to calls that are reached
|
||||
* in the flow covered by `revFlow`, where data might flow through the target
|
||||
* callable and back out at `call`.
|
||||
*/
|
||||
pragma[nomagic]
|
||||
private predicate revFlowThroughIntoCall(
|
||||
DataFlowCall call, ArgNodeEx arg, ParamNodeEx p, boolean allowsFieldFlow, Ap argAp,
|
||||
Configuration config
|
||||
) {
|
||||
flowThroughIntoCall(call, arg, p, allowsFieldFlow, argAp, config) and
|
||||
revFlowIsReturned(call, _, _, _, _, config)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate revFlowParamToReturn(
|
||||
ParamNodeEx p, FlowState state, ReturnKindExt kind, Ap returnAp, Ap ap, Configuration config
|
||||
) {
|
||||
revFlow(p, state, TReturnCtxMaybeFlowThrough(kind), apSome(returnAp), ap, config) and
|
||||
parameterFlowThroughAllowed(p, kind)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate revFlowInToReturn(
|
||||
DataFlowCall call, ArgNodeEx arg, FlowState state, ReturnKindExt kind, Ap returnAp, Ap ap,
|
||||
Configuration config
|
||||
) {
|
||||
exists(ParamNodeEx p, boolean allowsFieldFlow |
|
||||
revFlowParamToReturn(p, state, kind, returnAp, ap, config) and
|
||||
revFlowThroughIntoCall(call, arg, p, allowsFieldFlow, ap, config)
|
||||
revFlow(pragma[only_bind_into](p), state,
|
||||
TReturnCtxMaybeFlowThrough(pragma[only_bind_into](kind)), apSome(returnAp), ap, config) and
|
||||
flowThroughIntoCall(call, arg, p, allowsFieldFlow, config) and
|
||||
(if allowsFieldFlow = false then ap instanceof ApNil else any()) and
|
||||
parameterFlowThroughAllowed(p, kind)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1874,11 +1750,6 @@ private module MkStage<StageSig PrevStage> {
|
||||
pragma[nomagic]
|
||||
predicate revFlow(NodeEx node, Configuration config) { revFlow(node, _, _, _, _, config) }
|
||||
|
||||
pragma[nomagic]
|
||||
predicate revFlowAp(NodeEx node, Ap ap, Configuration config) {
|
||||
revFlow(node, _, _, _, ap, config)
|
||||
}
|
||||
|
||||
// use an alias as a workaround for bad functionality-induced joins
|
||||
pragma[nomagic]
|
||||
additional predicate revFlowAlias(NodeEx node, Configuration config) {
|
||||
@@ -1915,9 +1786,9 @@ private module MkStage<StageSig PrevStage> {
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate parameterFlowsThroughRev(
|
||||
ParamNodeEx p, Ap ap, ReturnKindExt kind, Ap returnAp, Configuration config
|
||||
ParamNodeEx p, Ap ap, ReturnKindExt kind, Configuration config
|
||||
) {
|
||||
revFlow(p, _, TReturnCtxMaybeFlowThrough(kind), apSome(returnAp), ap, config) and
|
||||
revFlow(p, _, TReturnCtxMaybeFlowThrough(kind), apSome(_), ap, config) and
|
||||
parameterFlowThroughAllowed(p, kind)
|
||||
}
|
||||
|
||||
@@ -1925,36 +1796,27 @@ private module MkStage<StageSig PrevStage> {
|
||||
predicate parameterMayFlowThrough(ParamNodeEx p, Ap ap, Configuration config) {
|
||||
exists(RetNodeEx ret, ReturnKindExt kind |
|
||||
returnFlowsThrough(ret, kind, _, _, p, ap, _, config) and
|
||||
parameterFlowsThroughRev(p, ap, kind, _, config)
|
||||
parameterFlowsThroughRev(p, ap, kind, config)
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
predicate returnMayFlowThrough(
|
||||
RetNodeEx ret, Ap argAp, Ap ap, ReturnKindExt kind, Configuration config
|
||||
) {
|
||||
exists(ParamNodeEx p |
|
||||
returnFlowsThrough(ret, kind, _, _, p, argAp, ap, config) and
|
||||
parameterFlowsThroughRev(p, argAp, kind, ap, config)
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
predicate revFlowInToReturnIsReturned(
|
||||
DataFlowCall call, ArgNodeEx arg, FlowState state, ReturnCtx returnCtx, ApOption returnAp,
|
||||
Ap ap, Configuration config
|
||||
) {
|
||||
exists(ReturnKindExt returnKind0, Ap returnAp0 |
|
||||
revFlowInToReturn(call, arg, state, returnKind0, returnAp0, ap, config) and
|
||||
revFlowIsReturned(call, returnCtx, returnAp, returnKind0, returnAp0, config)
|
||||
predicate returnMayFlowThrough(RetNodeEx ret, ReturnKindExt kind, Configuration config) {
|
||||
exists(ParamNodeEx p, Ap ap |
|
||||
returnFlowsThrough(ret, kind, _, _, p, ap, _, config) and
|
||||
parameterFlowsThroughRev(p, ap, kind, config)
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
predicate callMayFlowThroughRev(DataFlowCall call, Configuration config) {
|
||||
exists(ArgNodeEx arg, FlowState state, ReturnCtx returnCtx, ApOption returnAp, Ap ap |
|
||||
exists(
|
||||
ReturnKindExt returnKind0, Ap returnAp0, ArgNodeEx arg, FlowState state,
|
||||
ReturnCtx returnCtx, ApOption returnAp, Ap ap
|
||||
|
|
||||
revFlow(arg, state, returnCtx, returnAp, ap, config) and
|
||||
revFlowInToReturnIsReturned(call, arg, state, returnCtx, returnAp, ap, config)
|
||||
revFlowInToReturn(call, arg, state, returnKind0, returnAp0, ap, config) and
|
||||
revFlowIsReturned(call, returnCtx, returnAp, returnKind0, returnAp0, config)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -2317,16 +2179,16 @@ private module LocalFlowBigStep {
|
||||
pragma[nomagic]
|
||||
predicate localFlowBigStep(
|
||||
NodeEx node1, FlowState state1, NodeEx node2, FlowState state2, boolean preservesValue,
|
||||
DataFlowType t, Configuration config, LocalCallContext callContext
|
||||
AccessPathFrontNil apf, Configuration config, LocalCallContext callContext
|
||||
) {
|
||||
localFlowStepPlus(node1, state1, node2, preservesValue, t, config, callContext) and
|
||||
localFlowStepPlus(node1, state1, node2, preservesValue, apf.getType(), config, callContext) and
|
||||
localFlowExit(node2, state1, config) and
|
||||
state1 = state2
|
||||
or
|
||||
additionalLocalFlowStepNodeCand2(node1, state1, node2, state2, config) and
|
||||
state1 != state2 and
|
||||
preservesValue = false and
|
||||
t = node2.getDataFlowType() and
|
||||
apf = TFrontNil(node2.getDataFlowType()) and
|
||||
callContext.relevantFor(node1.getEnclosingCallable()) and
|
||||
not exists(DataFlowCall call | call = callContext.(LocalCallContextSpecificCall).getCall() |
|
||||
isUnreachableInCallCached(node1.asNode(), call) or
|
||||
@@ -2340,87 +2202,11 @@ private import LocalFlowBigStep
|
||||
private module Stage3Param implements MkStage<Stage2>::StageParam {
|
||||
private module PrevStage = Stage2;
|
||||
|
||||
class Ap = ApproxAccessPathFront;
|
||||
|
||||
class ApNil = ApproxAccessPathFrontNil;
|
||||
|
||||
PrevStage::Ap getApprox(Ap ap) { result = ap.toBoolNonEmpty() }
|
||||
|
||||
ApNil getApNil(NodeEx node) {
|
||||
PrevStage::revFlow(node, _) and result = TApproxFrontNil(node.getDataFlowType())
|
||||
}
|
||||
|
||||
bindingset[tc, tail]
|
||||
Ap apCons(TypedContent tc, Ap tail) { result.getAHead() = tc and exists(tail) }
|
||||
|
||||
pragma[noinline]
|
||||
Content getHeadContent(Ap ap) { result = ap.getAHead().getContent() }
|
||||
|
||||
class ApOption = ApproxAccessPathFrontOption;
|
||||
|
||||
ApOption apNone() { result = TApproxAccessPathFrontNone() }
|
||||
|
||||
ApOption apSome(Ap ap) { result = TApproxAccessPathFrontSome(ap) }
|
||||
|
||||
import BooleanCallContext
|
||||
|
||||
predicate localStep(
|
||||
NodeEx node1, FlowState state1, NodeEx node2, FlowState state2, boolean preservesValue,
|
||||
ApproxAccessPathFrontNil ap, Configuration config, LocalCc lcc
|
||||
) {
|
||||
localFlowBigStep(node1, state1, node2, state2, preservesValue, ap.getType(), config, _) and
|
||||
exists(lcc)
|
||||
}
|
||||
|
||||
predicate flowOutOfCall = flowOutOfCallNodeCand2/6;
|
||||
|
||||
predicate flowIntoCall = flowIntoCallNodeCand2/5;
|
||||
|
||||
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.getAHead().getContent()
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate castingNodeEx(NodeEx node) { node.asNode() instanceof CastingNode }
|
||||
|
||||
bindingset[node, state, ap, config]
|
||||
predicate filter(NodeEx node, FlowState state, Ap ap, Configuration config) {
|
||||
exists(state) and
|
||||
exists(config) and
|
||||
(if castingNodeEx(node) then compatibleTypes(node.getDataFlowType(), ap.getType()) else any()) and
|
||||
(
|
||||
notExpectsContent(node)
|
||||
or
|
||||
expectsContentCand(node, ap, config)
|
||||
)
|
||||
}
|
||||
|
||||
bindingset[ap, contentType]
|
||||
predicate typecheckStore(Ap ap, DataFlowType contentType) {
|
||||
// We need to typecheck stores here, since reverse flow through a getter
|
||||
// might have a different type here compared to inside the getter.
|
||||
compatibleTypes(ap.getType(), contentType)
|
||||
}
|
||||
}
|
||||
|
||||
private module Stage3 implements StageSig {
|
||||
import MkStage<Stage2>::Stage<Stage3Param>
|
||||
}
|
||||
|
||||
private module Stage4Param implements MkStage<Stage3>::StageParam {
|
||||
private module PrevStage = Stage3;
|
||||
|
||||
class Ap = AccessPathFront;
|
||||
|
||||
class ApNil = AccessPathFrontNil;
|
||||
|
||||
PrevStage::Ap getApprox(Ap ap) { result = ap.toApprox() }
|
||||
PrevStage::Ap getApprox(Ap ap) { result = ap.toBoolNonEmpty() }
|
||||
|
||||
ApNil getApNil(NodeEx node) {
|
||||
PrevStage::revFlow(node, _) and result = TFrontNil(node.getDataFlowType())
|
||||
@@ -2440,42 +2226,16 @@ private module Stage4Param implements MkStage<Stage3>::StageParam {
|
||||
|
||||
import BooleanCallContext
|
||||
|
||||
pragma[nomagic]
|
||||
predicate localStep(
|
||||
NodeEx node1, FlowState state1, NodeEx node2, FlowState state2, boolean preservesValue,
|
||||
ApNil ap, Configuration config, LocalCc lcc
|
||||
) {
|
||||
localFlowBigStep(node1, state1, node2, state2, preservesValue, ap.getType(), config, _) and
|
||||
PrevStage::revFlow(node1, pragma[only_bind_into](state1), _, pragma[only_bind_into](config)) and
|
||||
PrevStage::revFlowAlias(node2, pragma[only_bind_into](state2), _, pragma[only_bind_into](config)) and
|
||||
exists(lcc)
|
||||
localFlowBigStep(node1, state1, node2, state2, preservesValue, ap, config, _) and exists(lcc)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
predicate flowOutOfCall(
|
||||
DataFlowCall call, RetNodeEx node1, ReturnKindExt kind, NodeEx node2, boolean allowsFieldFlow,
|
||||
Configuration config
|
||||
) {
|
||||
exists(FlowState state |
|
||||
flowOutOfCallNodeCand2(call, node1, kind, node2, allowsFieldFlow, config) and
|
||||
PrevStage::revFlow(node2, pragma[only_bind_into](state), _, pragma[only_bind_into](config)) and
|
||||
PrevStage::revFlowAlias(node1, pragma[only_bind_into](state), _,
|
||||
pragma[only_bind_into](config))
|
||||
)
|
||||
}
|
||||
predicate flowOutOfCall = flowOutOfCallNodeCand2/6;
|
||||
|
||||
pragma[nomagic]
|
||||
predicate flowIntoCall(
|
||||
DataFlowCall call, ArgNodeEx node1, ParamNodeEx node2, boolean allowsFieldFlow,
|
||||
Configuration config
|
||||
) {
|
||||
exists(FlowState state |
|
||||
flowIntoCallNodeCand2(call, node1, node2, allowsFieldFlow, config) and
|
||||
PrevStage::revFlow(node2, pragma[only_bind_into](state), _, pragma[only_bind_into](config)) and
|
||||
PrevStage::revFlowAlias(node1, pragma[only_bind_into](state), _,
|
||||
pragma[only_bind_into](config))
|
||||
)
|
||||
}
|
||||
predicate flowIntoCall = flowIntoCallNodeCand2/5;
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate clearSet(NodeEx node, ContentSet c, Configuration config) {
|
||||
@@ -2531,8 +2291,8 @@ private module Stage4Param implements MkStage<Stage3>::StageParam {
|
||||
}
|
||||
}
|
||||
|
||||
private module Stage4 implements StageSig {
|
||||
import MkStage<Stage3>::Stage<Stage4Param>
|
||||
private module Stage3 implements StageSig {
|
||||
import MkStage<Stage2>::Stage<Stage3Param>
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -2543,8 +2303,8 @@ private predicate flowCandSummaryCtx(
|
||||
NodeEx node, FlowState state, AccessPathFront argApf, Configuration config
|
||||
) {
|
||||
exists(AccessPathFront apf |
|
||||
Stage4::revFlow(node, state, TReturnCtxMaybeFlowThrough(_), _, apf, config) and
|
||||
Stage4::fwdFlow(node, state, any(Stage4::CcCall ccc), _, TAccessPathFrontSome(argApf), apf,
|
||||
Stage3::revFlow(node, state, TReturnCtxMaybeFlowThrough(_), _, apf, config) and
|
||||
Stage3::fwdFlow(node, state, any(Stage3::CcCall ccc), _, TAccessPathFrontSome(argApf), apf,
|
||||
config)
|
||||
)
|
||||
}
|
||||
@@ -2555,10 +2315,10 @@ private predicate flowCandSummaryCtx(
|
||||
*/
|
||||
private predicate expensiveLen2unfolding(TypedContent tc, Configuration config) {
|
||||
exists(int tails, int nodes, int apLimit, int tupleLimit |
|
||||
tails = strictcount(AccessPathFront apf | Stage4::consCand(tc, apf, config)) and
|
||||
tails = strictcount(AccessPathFront apf | Stage3::consCand(tc, apf, config)) and
|
||||
nodes =
|
||||
strictcount(NodeEx n, FlowState state |
|
||||
Stage4::revFlow(n, state, any(AccessPathFrontHead apf | apf.getHead() = tc), config)
|
||||
Stage3::revFlow(n, state, any(AccessPathFrontHead apf | apf.getHead() = tc), config)
|
||||
or
|
||||
flowCandSummaryCtx(n, state, any(AccessPathFrontHead apf | apf.getHead() = tc), config)
|
||||
) and
|
||||
@@ -2572,11 +2332,11 @@ private predicate expensiveLen2unfolding(TypedContent tc, Configuration config)
|
||||
private newtype TAccessPathApprox =
|
||||
TNil(DataFlowType t) or
|
||||
TConsNil(TypedContent tc, DataFlowType t) {
|
||||
Stage4::consCand(tc, TFrontNil(t), _) and
|
||||
Stage3::consCand(tc, TFrontNil(t), _) and
|
||||
not expensiveLen2unfolding(tc, _)
|
||||
} or
|
||||
TConsCons(TypedContent tc1, TypedContent tc2, int len) {
|
||||
Stage4::consCand(tc1, TFrontHead(tc2), _) and
|
||||
Stage3::consCand(tc1, TFrontHead(tc2), _) and
|
||||
len in [2 .. accessPathLimit()] and
|
||||
not expensiveLen2unfolding(tc1, _)
|
||||
} or
|
||||
@@ -2706,7 +2466,7 @@ private class AccessPathApproxCons1 extends AccessPathApproxCons, TCons1 {
|
||||
override AccessPathApprox pop(TypedContent head) {
|
||||
head = tc and
|
||||
(
|
||||
exists(TypedContent tc2 | Stage4::consCand(tc, TFrontHead(tc2), _) |
|
||||
exists(TypedContent tc2 | Stage3::consCand(tc, TFrontHead(tc2), _) |
|
||||
result = TConsCons(tc2, _, len - 1)
|
||||
or
|
||||
len = 2 and
|
||||
@@ -2717,7 +2477,7 @@ private class AccessPathApproxCons1 extends AccessPathApproxCons, TCons1 {
|
||||
or
|
||||
exists(DataFlowType t |
|
||||
len = 1 and
|
||||
Stage4::consCand(tc, TFrontNil(t), _) and
|
||||
Stage3::consCand(tc, TFrontNil(t), _) and
|
||||
result = TNil(t)
|
||||
)
|
||||
)
|
||||
@@ -2742,14 +2502,13 @@ private class AccessPathApproxOption extends TAccessPathApproxOption {
|
||||
}
|
||||
}
|
||||
|
||||
private module Stage5Param implements MkStage<Stage4>::StageParam {
|
||||
private module PrevStage = Stage4;
|
||||
private module Stage4Param implements MkStage<Stage3>::StageParam {
|
||||
private module PrevStage = Stage3;
|
||||
|
||||
class Ap = AccessPathApprox;
|
||||
|
||||
class ApNil = AccessPathApproxNil;
|
||||
|
||||
pragma[nomagic]
|
||||
PrevStage::Ap getApprox(Ap ap) { result = ap.getFront() }
|
||||
|
||||
ApNil getApNil(NodeEx node) {
|
||||
@@ -2775,9 +2534,7 @@ private module Stage5Param implements MkStage<Stage4>::StageParam {
|
||||
NodeEx node1, FlowState state1, NodeEx node2, FlowState state2, boolean preservesValue,
|
||||
ApNil ap, Configuration config, LocalCc lcc
|
||||
) {
|
||||
localFlowBigStep(node1, state1, node2, state2, preservesValue, ap.getType(), config, lcc) and
|
||||
PrevStage::revFlow(node1, pragma[only_bind_into](state1), _, pragma[only_bind_into](config)) and
|
||||
PrevStage::revFlowAlias(node2, pragma[only_bind_into](state2), _, pragma[only_bind_into](config))
|
||||
localFlowBigStep(node1, state1, node2, state2, preservesValue, ap.getFront(), config, lcc)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
@@ -2814,7 +2571,7 @@ private module Stage5Param implements MkStage<Stage4>::StageParam {
|
||||
predicate typecheckStore(Ap ap, DataFlowType contentType) { any() }
|
||||
}
|
||||
|
||||
private module Stage5 = MkStage<Stage4>::Stage<Stage5Param>;
|
||||
private module Stage4 = MkStage<Stage3>::Stage<Stage4Param>;
|
||||
|
||||
bindingset[conf, result]
|
||||
private Configuration unbindConf(Configuration conf) {
|
||||
@@ -2828,8 +2585,8 @@ private predicate nodeMayUseSummary0(
|
||||
) {
|
||||
exists(AccessPathApprox apa0 |
|
||||
c = n.getEnclosingCallable() and
|
||||
Stage5::revFlow(n, state, TReturnCtxMaybeFlowThrough(_), _, apa0, config) and
|
||||
Stage5::fwdFlow(n, state, any(CallContextCall ccc), TParameterPositionSome(pos),
|
||||
Stage4::revFlow(n, state, TReturnCtxMaybeFlowThrough(_), _, apa0, config) and
|
||||
Stage4::fwdFlow(n, state, any(CallContextCall ccc), TParameterPositionSome(pos),
|
||||
TAccessPathApproxSome(apa), apa0, config)
|
||||
)
|
||||
}
|
||||
@@ -2839,7 +2596,7 @@ private predicate nodeMayUseSummary(
|
||||
NodeEx n, FlowState state, AccessPathApprox apa, Configuration config
|
||||
) {
|
||||
exists(DataFlowCallable c, ParameterPosition pos, ParamNodeEx p |
|
||||
Stage5::parameterMayFlowThrough(p, apa, config) and
|
||||
Stage4::parameterMayFlowThrough(p, apa, config) and
|
||||
nodeMayUseSummary0(n, c, pos, state, apa, config) and
|
||||
p.isParameterOf(c, pos)
|
||||
)
|
||||
@@ -2849,8 +2606,8 @@ private newtype TSummaryCtx =
|
||||
TSummaryCtxNone() or
|
||||
TSummaryCtxSome(ParamNodeEx p, FlowState state, AccessPath ap) {
|
||||
exists(Configuration config |
|
||||
Stage5::parameterMayFlowThrough(p, ap.getApprox(), config) and
|
||||
Stage5::revFlow(p, state, _, config)
|
||||
Stage4::parameterMayFlowThrough(p, ap.getApprox(), config) and
|
||||
Stage4::revFlow(p, state, _, config)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -2899,7 +2656,7 @@ private int count1to2unfold(AccessPathApproxCons1 apa, Configuration config) {
|
||||
len = apa.len() and
|
||||
result =
|
||||
strictcount(AccessPathFront apf |
|
||||
Stage5::consCand(tc, any(AccessPathApprox ap | ap.getFront() = apf and ap.len() = len - 1),
|
||||
Stage4::consCand(tc, any(AccessPathApprox ap | ap.getFront() = apf and ap.len() = len - 1),
|
||||
config)
|
||||
)
|
||||
)
|
||||
@@ -2908,7 +2665,7 @@ private int count1to2unfold(AccessPathApproxCons1 apa, Configuration config) {
|
||||
private int countNodesUsingAccessPath(AccessPathApprox apa, Configuration config) {
|
||||
result =
|
||||
strictcount(NodeEx n, FlowState state |
|
||||
Stage5::revFlow(n, state, apa, config) or nodeMayUseSummary(n, state, apa, config)
|
||||
Stage4::revFlow(n, state, apa, config) or nodeMayUseSummary(n, state, apa, config)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -2929,7 +2686,7 @@ private predicate expensiveLen1to2unfolding(AccessPathApproxCons1 apa, Configura
|
||||
private AccessPathApprox getATail(AccessPathApprox apa, Configuration config) {
|
||||
exists(TypedContent head |
|
||||
apa.pop(head) = result and
|
||||
Stage5::consCand(head, result, config)
|
||||
Stage4::consCand(head, result, config)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -3011,7 +2768,7 @@ private newtype TPathNode =
|
||||
NodeEx node, FlowState state, CallContext cc, SummaryCtx sc, AccessPath ap, Configuration config
|
||||
) {
|
||||
// A PathNode is introduced by a source ...
|
||||
Stage5::revFlow(node, state, config) and
|
||||
Stage4::revFlow(node, state, config) and
|
||||
sourceNode(node, state, config) and
|
||||
(
|
||||
if hasSourceCallCtx(config)
|
||||
@@ -3025,7 +2782,7 @@ private newtype TPathNode =
|
||||
exists(PathNodeMid mid |
|
||||
pathStep(mid, node, state, cc, sc, ap) and
|
||||
pragma[only_bind_into](config) = mid.getConfiguration() and
|
||||
Stage5::revFlow(node, state, ap.getApprox(), pragma[only_bind_into](config))
|
||||
Stage4::revFlow(node, state, ap.getApprox(), pragma[only_bind_into](config))
|
||||
)
|
||||
} or
|
||||
TPathNodeSink(NodeEx node, FlowState state, Configuration config) {
|
||||
@@ -3167,7 +2924,7 @@ private class AccessPathCons2 extends AccessPath, TAccessPathCons2 {
|
||||
override TypedContent getHead() { result = head1 }
|
||||
|
||||
override AccessPath getTail() {
|
||||
Stage5::consCand(head1, result.getApprox(), _) and
|
||||
Stage4::consCand(head1, result.getApprox(), _) and
|
||||
result.getHead() = head2 and
|
||||
result.length() = len - 1
|
||||
}
|
||||
@@ -3198,7 +2955,7 @@ private class AccessPathCons1 extends AccessPath, TAccessPathCons1 {
|
||||
override TypedContent getHead() { result = head }
|
||||
|
||||
override AccessPath getTail() {
|
||||
Stage5::consCand(head, result.getApprox(), _) and result.length() = len - 1
|
||||
Stage4::consCand(head, result.getApprox(), _) and result.length() = len - 1
|
||||
}
|
||||
|
||||
override AccessPathFrontHead getFront() { result = TFrontHead(head) }
|
||||
@@ -3590,8 +3347,7 @@ private predicate pathStep(
|
||||
AccessPath ap0, NodeEx midnode, FlowState state0, Configuration conf, LocalCallContext localCC
|
||||
|
|
||||
pathNode(mid, midnode, state0, cc, sc, ap0, conf, localCC) and
|
||||
localFlowBigStep(midnode, state0, node, state, false, ap.(AccessPathNil).getType(), conf,
|
||||
localCC) and
|
||||
localFlowBigStep(midnode, state0, node, state, false, ap.getFront(), conf, localCC) and
|
||||
ap0 instanceof AccessPathNil
|
||||
)
|
||||
or
|
||||
@@ -3633,7 +3389,7 @@ private predicate pathReadStep(
|
||||
) {
|
||||
ap0 = mid.getAp() and
|
||||
tc = ap0.getHead() and
|
||||
Stage5::readStepCand(mid.getNodeEx(), tc.getContent(), node, mid.getConfiguration()) and
|
||||
Stage4::readStepCand(mid.getNodeEx(), tc.getContent(), node, mid.getConfiguration()) and
|
||||
state = mid.getState() and
|
||||
cc = mid.getCallContext()
|
||||
}
|
||||
@@ -3643,7 +3399,7 @@ private predicate pathStoreStep(
|
||||
PathNodeMid mid, NodeEx node, FlowState state, AccessPath ap0, TypedContent tc, CallContext cc
|
||||
) {
|
||||
ap0 = mid.getAp() and
|
||||
Stage5::storeStepCand(mid.getNodeEx(), _, tc, node, _, mid.getConfiguration()) and
|
||||
Stage4::storeStepCand(mid.getNodeEx(), _, tc, node, _, mid.getConfiguration()) and
|
||||
state = mid.getState() and
|
||||
cc = mid.getCallContext()
|
||||
}
|
||||
@@ -3680,7 +3436,7 @@ private NodeEx getAnOutNodeFlow(
|
||||
ReturnKindExt kind, DataFlowCall call, AccessPathApprox apa, Configuration config
|
||||
) {
|
||||
result.asNode() = kind.getAnOutNode(call) and
|
||||
Stage5::revFlow(result, _, apa, config)
|
||||
Stage4::revFlow(result, _, apa, config)
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -3716,7 +3472,7 @@ private predicate parameterCand(
|
||||
DataFlowCallable callable, ParameterPosition pos, AccessPathApprox apa, Configuration config
|
||||
) {
|
||||
exists(ParamNodeEx p |
|
||||
Stage5::revFlow(p, _, apa, config) and
|
||||
Stage4::revFlow(p, _, apa, config) and
|
||||
p.isParameterOf(callable, pos)
|
||||
)
|
||||
}
|
||||
@@ -3995,17 +3751,9 @@ predicate stageStats(
|
||||
n = 45 and
|
||||
Stage4::stats(false, nodes, fields, conscand, states, tuples, config)
|
||||
or
|
||||
stage = "5 Fwd" and
|
||||
n = 50 and
|
||||
Stage5::stats(true, nodes, fields, conscand, states, tuples, config)
|
||||
stage = "5 Fwd" and n = 50 and finalStats(true, nodes, fields, conscand, states, tuples)
|
||||
or
|
||||
stage = "5 Rev" and
|
||||
n = 55 and
|
||||
Stage5::stats(false, nodes, fields, conscand, states, tuples, config)
|
||||
or
|
||||
stage = "6 Fwd" and n = 60 and finalStats(true, nodes, fields, conscand, states, tuples)
|
||||
or
|
||||
stage = "6 Rev" and n = 65 and finalStats(false, nodes, fields, conscand, states, tuples)
|
||||
stage = "5 Rev" and n = 55 and finalStats(false, nodes, fields, conscand, states, tuples)
|
||||
}
|
||||
|
||||
private module FlowExploration {
|
||||
|
||||
@@ -621,6 +621,23 @@ private predicate parameterFlowThroughAllowed(ParamNodeEx p, ReturnKindExt kind)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if flow from a parameter at position `pos` inside `c` to a return node of
|
||||
* kind `kind` is allowed.
|
||||
*
|
||||
* We don't expect a parameter to return stored in itself, unless
|
||||
* explicitly allowed
|
||||
*/
|
||||
bindingset[c, pos, kind]
|
||||
private predicate parameterFlowThroughAllowed(
|
||||
DataFlowCallable c, ParameterPosition pos, ReturnKindExt kind
|
||||
) {
|
||||
exists(ParamNodeEx p |
|
||||
p.isParameterOf(c, pos) and
|
||||
parameterFlowThroughAllowed(p, kind)
|
||||
)
|
||||
}
|
||||
|
||||
private module Stage1 implements StageSig {
|
||||
class Ap = Unit;
|
||||
|
||||
@@ -963,12 +980,6 @@ private module Stage1 implements StageSig {
|
||||
pragma[nomagic]
|
||||
predicate revFlow(NodeEx node, Configuration config) { revFlow(node, _, config) }
|
||||
|
||||
pragma[nomagic]
|
||||
predicate revFlowAp(NodeEx node, Ap ap, Configuration config) {
|
||||
revFlow(node, config) and
|
||||
exists(ap)
|
||||
}
|
||||
|
||||
bindingset[node, state, config]
|
||||
predicate revFlow(NodeEx node, FlowState state, Ap ap, Configuration config) {
|
||||
revFlow(node, _, pragma[only_bind_into](config)) and
|
||||
@@ -1011,13 +1022,9 @@ private module Stage1 implements StageSig {
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
predicate returnMayFlowThrough(
|
||||
RetNodeEx ret, Ap argAp, Ap ap, ReturnKindExt kind, Configuration config
|
||||
) {
|
||||
predicate returnMayFlowThrough(RetNodeEx ret, ReturnKindExt kind, Configuration config) {
|
||||
throughFlowNodeCand(ret, config) and
|
||||
kind = ret.getKind() and
|
||||
exists(argAp) and
|
||||
exists(ap)
|
||||
kind = ret.getKind()
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
@@ -1182,8 +1189,6 @@ private signature module StageSig {
|
||||
|
||||
predicate revFlow(NodeEx node, Configuration config);
|
||||
|
||||
predicate revFlowAp(NodeEx node, Ap ap, Configuration config);
|
||||
|
||||
bindingset[node, state, config]
|
||||
predicate revFlow(NodeEx node, FlowState state, Ap ap, Configuration config);
|
||||
|
||||
@@ -1191,9 +1196,7 @@ private signature module StageSig {
|
||||
|
||||
predicate parameterMayFlowThrough(ParamNodeEx p, Ap ap, Configuration config);
|
||||
|
||||
predicate returnMayFlowThrough(
|
||||
RetNodeEx ret, Ap argAp, Ap ap, ReturnKindExt kind, Configuration config
|
||||
);
|
||||
predicate returnMayFlowThrough(RetNodeEx ret, ReturnKindExt kind, Configuration config);
|
||||
|
||||
predicate storeStepCand(
|
||||
NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, DataFlowType contentType,
|
||||
@@ -1278,43 +1281,21 @@ private module MkStage<StageSig PrevStage> {
|
||||
import Param
|
||||
|
||||
/* Begin: Stage logic. */
|
||||
// use an alias as a workaround for bad functionality-induced joins
|
||||
pragma[nomagic]
|
||||
private predicate revFlowApAlias(NodeEx node, ApApprox apa, Configuration config) {
|
||||
PrevStage::revFlowAp(node, apa, config)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate flowIntoCallApa(
|
||||
DataFlowCall call, ArgNodeEx arg, ParamNodeEx p, boolean allowsFieldFlow, ApApprox apa,
|
||||
Configuration config
|
||||
) {
|
||||
flowIntoCall(call, arg, p, allowsFieldFlow, config) and
|
||||
PrevStage::revFlowAp(p, pragma[only_bind_into](apa), pragma[only_bind_into](config)) and
|
||||
revFlowApAlias(arg, pragma[only_bind_into](apa), pragma[only_bind_into](config))
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate flowOutOfCallApa(
|
||||
DataFlowCall call, RetNodeEx ret, ReturnKindExt kind, NodeEx out, boolean allowsFieldFlow,
|
||||
ApApprox apa, Configuration config
|
||||
) {
|
||||
flowOutOfCall(call, ret, kind, out, allowsFieldFlow, config) and
|
||||
PrevStage::revFlowAp(out, pragma[only_bind_into](apa), pragma[only_bind_into](config)) and
|
||||
revFlowApAlias(ret, pragma[only_bind_into](apa), pragma[only_bind_into](config))
|
||||
bindingset[result, apa]
|
||||
private ApApprox unbindApa(ApApprox apa) {
|
||||
pragma[only_bind_out](apa) = pragma[only_bind_out](result)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate flowThroughOutOfCall(
|
||||
DataFlowCall call, CcCall ccc, RetNodeEx ret, NodeEx out, boolean allowsFieldFlow,
|
||||
ApApprox argApa, ApApprox apa, Configuration config
|
||||
DataFlowCall call, DataFlowCallable c, CcCall ccc, RetNodeEx ret, ReturnKindExt kind,
|
||||
NodeEx out, boolean allowsFieldFlow, Configuration config
|
||||
) {
|
||||
exists(ReturnKindExt kind |
|
||||
flowOutOfCallApa(call, ret, kind, out, allowsFieldFlow, apa, pragma[only_bind_into](config)) and
|
||||
PrevStage::callMayFlowThroughRev(call, pragma[only_bind_into](config)) and
|
||||
PrevStage::returnMayFlowThrough(ret, argApa, apa, kind, pragma[only_bind_into](config)) and
|
||||
matchesCall(ccc, call)
|
||||
)
|
||||
flowOutOfCall(call, ret, kind, out, allowsFieldFlow, pragma[only_bind_into](config)) and
|
||||
PrevStage::callMayFlowThroughRev(call, pragma[only_bind_into](config)) and
|
||||
PrevStage::returnMayFlowThrough(ret, kind, pragma[only_bind_into](config)) and
|
||||
matchesCall(ccc, call) and
|
||||
c = ret.getEnclosingCallable()
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1326,50 +1307,39 @@ private module MkStage<StageSig PrevStage> {
|
||||
* corresponding parameter position and access path of that argument, respectively.
|
||||
*/
|
||||
pragma[nomagic]
|
||||
additional predicate fwdFlow(
|
||||
NodeEx node, FlowState state, Cc cc, ParameterPositionOption summaryCtx, ApOption argAp,
|
||||
Ap ap, ApApprox apa, Configuration config
|
||||
) {
|
||||
fwdFlow0(node, state, cc, summaryCtx, argAp, ap, apa, config) and
|
||||
PrevStage::revFlow(node, state, apa, config) and
|
||||
filter(node, state, ap, config)
|
||||
}
|
||||
|
||||
pragma[inline]
|
||||
additional predicate fwdFlow(
|
||||
NodeEx node, FlowState state, Cc cc, ParameterPositionOption summaryCtx, ApOption argAp,
|
||||
Ap ap, Configuration config
|
||||
) {
|
||||
fwdFlow(node, state, cc, summaryCtx, argAp, ap, _, config)
|
||||
fwdFlow0(node, state, cc, summaryCtx, argAp, ap, config) and
|
||||
PrevStage::revFlow(node, state, unbindApa(getApprox(ap)), config) and
|
||||
filter(node, state, ap, config)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate fwdFlow0(
|
||||
NodeEx node, FlowState state, Cc cc, ParameterPositionOption summaryCtx, ApOption argAp,
|
||||
Ap ap, ApApprox apa, Configuration config
|
||||
Ap ap, Configuration config
|
||||
) {
|
||||
sourceNode(node, state, config) and
|
||||
(if hasSourceCallCtx(config) then cc = ccSomeCall() else cc = ccNone()) and
|
||||
argAp = apNone() and
|
||||
summaryCtx = TParameterPositionNone() and
|
||||
ap = getApNil(node) and
|
||||
apa = getApprox(ap)
|
||||
ap = getApNil(node)
|
||||
or
|
||||
exists(NodeEx mid, FlowState state0, Ap ap0, ApApprox apa0, LocalCc localCc |
|
||||
fwdFlow(mid, state0, cc, summaryCtx, argAp, ap0, apa0, config) and
|
||||
exists(NodeEx mid, FlowState state0, Ap ap0, LocalCc localCc |
|
||||
fwdFlow(mid, state0, cc, summaryCtx, argAp, ap0, config) and
|
||||
localCc = getLocalCc(mid, cc)
|
||||
|
|
||||
localStep(mid, state0, node, state, true, _, config, localCc) and
|
||||
ap = ap0 and
|
||||
apa = apa0
|
||||
ap = ap0
|
||||
or
|
||||
localStep(mid, state0, node, state, false, ap, config, localCc) and
|
||||
ap0 instanceof ApNil and
|
||||
apa = getApprox(ap)
|
||||
ap0 instanceof ApNil
|
||||
)
|
||||
or
|
||||
exists(NodeEx mid |
|
||||
fwdFlow(mid, pragma[only_bind_into](state), _, _, _, ap, apa, pragma[only_bind_into](config)) and
|
||||
fwdFlow(mid, pragma[only_bind_into](state), _, _, _, ap, pragma[only_bind_into](config)) and
|
||||
jumpStep(mid, node, config) and
|
||||
cc = ccNone() and
|
||||
summaryCtx = TParameterPositionNone() and
|
||||
@@ -1382,8 +1352,7 @@ private module MkStage<StageSig PrevStage> {
|
||||
cc = ccNone() and
|
||||
summaryCtx = TParameterPositionNone() and
|
||||
argAp = apNone() and
|
||||
ap = getApNil(node) and
|
||||
apa = getApprox(ap)
|
||||
ap = getApNil(node)
|
||||
)
|
||||
or
|
||||
exists(NodeEx mid, FlowState state0, ApNil nil |
|
||||
@@ -1392,32 +1361,32 @@ private module MkStage<StageSig PrevStage> {
|
||||
cc = ccNone() and
|
||||
summaryCtx = TParameterPositionNone() and
|
||||
argAp = apNone() and
|
||||
ap = getApNil(node) and
|
||||
apa = getApprox(ap)
|
||||
ap = getApNil(node)
|
||||
)
|
||||
or
|
||||
// store
|
||||
exists(TypedContent tc, Ap ap0 |
|
||||
fwdFlowStore(_, ap0, tc, node, state, cc, summaryCtx, argAp, config) and
|
||||
ap = apCons(tc, ap0) and
|
||||
apa = getApprox(ap)
|
||||
ap = apCons(tc, ap0)
|
||||
)
|
||||
or
|
||||
// read
|
||||
exists(Ap ap0, Content c |
|
||||
fwdFlowRead(ap0, c, _, node, state, cc, summaryCtx, argAp, config) and
|
||||
fwdFlowConsCand(ap0, c, ap, config) and
|
||||
apa = getApprox(ap)
|
||||
fwdFlowConsCand(ap0, c, ap, config)
|
||||
)
|
||||
or
|
||||
// flow into a callable
|
||||
fwdFlowIn(_, node, state, _, cc, _, _, ap, apa, config) and
|
||||
if PrevStage::parameterMayFlowThrough(node, apa, config)
|
||||
then (
|
||||
summaryCtx = TParameterPositionSome(node.(ParamNodeEx).getPosition()) and
|
||||
argAp = apSome(ap)
|
||||
) else (
|
||||
summaryCtx = TParameterPositionNone() and argAp = apNone()
|
||||
exists(ApApprox apa |
|
||||
fwdFlowIn(_, node, state, _, cc, _, _, ap, config) and
|
||||
apa = getApprox(ap) and
|
||||
if PrevStage::parameterMayFlowThrough(node, apa, config)
|
||||
then (
|
||||
summaryCtx = TParameterPositionSome(node.(ParamNodeEx).getPosition()) and
|
||||
argAp = apSome(ap)
|
||||
) else (
|
||||
summaryCtx = TParameterPositionNone() and argAp = apNone()
|
||||
)
|
||||
)
|
||||
or
|
||||
// flow out of a callable
|
||||
@@ -1425,8 +1394,8 @@ private module MkStage<StageSig PrevStage> {
|
||||
DataFlowCall call, RetNodeEx ret, boolean allowsFieldFlow, CcNoCall innercc,
|
||||
DataFlowCallable inner
|
||||
|
|
||||
fwdFlow(ret, state, innercc, summaryCtx, argAp, ap, apa, config) and
|
||||
flowOutOfCallApa(call, ret, _, node, allowsFieldFlow, apa, config) and
|
||||
fwdFlow(ret, state, innercc, summaryCtx, argAp, ap, config) and
|
||||
flowOutOfCall(call, ret, _, node, allowsFieldFlow, config) and
|
||||
inner = ret.getEnclosingCallable() and
|
||||
cc = getCallContextReturn(inner, call, innercc) and
|
||||
if allowsFieldFlow = false then ap instanceof ApNil else any()
|
||||
@@ -1434,7 +1403,7 @@ private module MkStage<StageSig PrevStage> {
|
||||
or
|
||||
// flow through a callable
|
||||
exists(DataFlowCall call, ParameterPosition summaryCtx0, Ap argAp0 |
|
||||
fwdFlowOutFromArg(call, node, state, summaryCtx0, argAp0, ap, apa, config) and
|
||||
fwdFlowOutFromArg(call, node, state, summaryCtx0, argAp0, ap, config) and
|
||||
fwdFlowIsEntered(call, cc, summaryCtx, argAp, summaryCtx0, argAp0, config)
|
||||
)
|
||||
}
|
||||
@@ -1444,9 +1413,9 @@ private module MkStage<StageSig PrevStage> {
|
||||
NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, FlowState state, Cc cc,
|
||||
ParameterPositionOption summaryCtx, ApOption argAp, Configuration config
|
||||
) {
|
||||
exists(DataFlowType contentType, ApApprox apa1 |
|
||||
fwdFlow(node1, state, cc, summaryCtx, argAp, ap1, apa1, config) and
|
||||
PrevStage::storeStepCand(node1, apa1, tc, node2, contentType, config) and
|
||||
exists(DataFlowType contentType |
|
||||
fwdFlow(node1, state, cc, summaryCtx, argAp, ap1, config) and
|
||||
PrevStage::storeStepCand(node1, unbindApa(getApprox(ap1)), tc, node2, contentType, config) and
|
||||
typecheckStore(ap1, contentType)
|
||||
)
|
||||
}
|
||||
@@ -1464,104 +1433,43 @@ private module MkStage<StageSig PrevStage> {
|
||||
)
|
||||
}
|
||||
|
||||
private class ApNonNil instanceof Ap {
|
||||
pragma[nomagic]
|
||||
ApNonNil() { not this instanceof ApNil }
|
||||
|
||||
string toString() { result = "" }
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate fwdFlowRead0(
|
||||
NodeEx node1, FlowState state, Cc cc, ParameterPositionOption summaryCtx, ApOption argAp,
|
||||
ApNonNil ap, Configuration config
|
||||
) {
|
||||
fwdFlow(node1, state, cc, summaryCtx, argAp, ap, config) and
|
||||
PrevStage::readStepCand(node1, _, _, config)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate fwdFlowRead(
|
||||
Ap ap, Content c, NodeEx node1, NodeEx node2, FlowState state, Cc cc,
|
||||
ParameterPositionOption summaryCtx, ApOption argAp, Configuration config
|
||||
) {
|
||||
fwdFlowRead0(node1, state, cc, summaryCtx, argAp, ap, config) and
|
||||
fwdFlow(node1, state, cc, summaryCtx, argAp, ap, config) and
|
||||
PrevStage::readStepCand(node1, c, node2, config) and
|
||||
getHeadContent(ap) = c
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate fwdFlowIn(
|
||||
DataFlowCall call, ParamNodeEx p, FlowState state, Cc outercc, CcCall innercc,
|
||||
ParameterPositionOption summaryCtx, ApOption argAp, Ap ap, ApApprox apa, Configuration config
|
||||
DataFlowCall call, ParamNodeEx p, FlowState state, Cc outercc, Cc innercc,
|
||||
ParameterPositionOption summaryCtx, ApOption argAp, Ap ap, Configuration config
|
||||
) {
|
||||
exists(ArgNodeEx arg, boolean allowsFieldFlow |
|
||||
fwdFlow(arg, state, outercc, summaryCtx, argAp, ap, apa, config) and
|
||||
flowIntoCallApa(call, arg, p, allowsFieldFlow, apa, config) and
|
||||
fwdFlow(arg, state, outercc, summaryCtx, argAp, ap, config) and
|
||||
flowIntoCall(call, arg, p, allowsFieldFlow, config) and
|
||||
innercc = getCallContextCall(call, p.getEnclosingCallable(), outercc) and
|
||||
if allowsFieldFlow = false then ap instanceof ApNil else any()
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate fwdFlowRetFromArg(
|
||||
RetNodeEx ret, FlowState state, CcCall ccc, ParameterPosition summaryCtx, ParamNodeEx p,
|
||||
Ap argAp, ApApprox argApa, Ap ap, ApApprox apa, Configuration config
|
||||
) {
|
||||
exists(DataFlowCallable c, ReturnKindExt kind |
|
||||
fwdFlow(pragma[only_bind_into](ret), state, ccc,
|
||||
TParameterPositionSome(pragma[only_bind_into](summaryCtx)), apSome(argAp), ap, apa, config) and
|
||||
getApprox(argAp) = argApa and
|
||||
c = ret.getEnclosingCallable() and
|
||||
kind = ret.getKind() and
|
||||
p.isParameterOf(c, pragma[only_bind_into](summaryCtx)) and
|
||||
parameterFlowThroughAllowed(p, kind)
|
||||
)
|
||||
}
|
||||
|
||||
pragma[inline]
|
||||
private predicate fwdFlowInMayFlowThrough(
|
||||
DataFlowCall call, Cc cc, CcCall innerCc, ParameterPositionOption summaryCtx, ApOption argAp,
|
||||
ParamNodeEx param, Ap ap, ApApprox apa, Configuration config
|
||||
) {
|
||||
fwdFlowIn(call, pragma[only_bind_into](param), _, cc, innerCc, summaryCtx, argAp, ap,
|
||||
pragma[only_bind_into](apa), pragma[only_bind_into](config)) and
|
||||
PrevStage::parameterMayFlowThrough(param, apa, config)
|
||||
}
|
||||
|
||||
// dedup before joining with `flowThroughOutOfCall`
|
||||
pragma[nomagic]
|
||||
private predicate fwdFlowInMayFlowThroughProj(
|
||||
DataFlowCall call, CcCall innerCc, ApApprox apa, Configuration config
|
||||
) {
|
||||
fwdFlowInMayFlowThrough(call, _, innerCc, _, _, _, _, apa, config)
|
||||
}
|
||||
|
||||
/**
|
||||
* Same as `flowThroughOutOfCall`, but restricted to calls that are reached
|
||||
* in the flow covered by `fwdFlow`, where data might flow through the target
|
||||
* callable and back out at `call`.
|
||||
*/
|
||||
pragma[nomagic]
|
||||
private predicate fwdFlowThroughOutOfCall(
|
||||
DataFlowCall call, CcCall ccc, RetNodeEx ret, NodeEx out, boolean allowsFieldFlow,
|
||||
ApApprox argApa, ApApprox apa, Configuration config
|
||||
) {
|
||||
fwdFlowInMayFlowThroughProj(call, ccc, argApa, config) and
|
||||
flowThroughOutOfCall(call, ccc, ret, out, allowsFieldFlow, argApa, apa, config)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate fwdFlowOutFromArg(
|
||||
DataFlowCall call, NodeEx out, FlowState state, ParameterPosition summaryCtx, Ap argAp, Ap ap,
|
||||
ApApprox apa, Configuration config
|
||||
Configuration config
|
||||
) {
|
||||
exists(RetNodeEx ret, boolean allowsFieldFlow, CcCall ccc, ApApprox argApa |
|
||||
fwdFlowRetFromArg(pragma[only_bind_into](ret), state, pragma[only_bind_into](ccc),
|
||||
summaryCtx, _, argAp, pragma[only_bind_into](argApa), ap, pragma[only_bind_into](apa),
|
||||
exists(
|
||||
DataFlowCallable c, RetNodeEx ret, ReturnKindExt kind, boolean allowsFieldFlow, CcCall ccc
|
||||
|
|
||||
fwdFlow(pragma[only_bind_into](ret), state, pragma[only_bind_into](ccc),
|
||||
TParameterPositionSome(pragma[only_bind_into](summaryCtx)), apSome(argAp), ap, config) and
|
||||
flowThroughOutOfCall(call, pragma[only_bind_into](c), ccc, ret, kind, out, allowsFieldFlow,
|
||||
config) and
|
||||
fwdFlowThroughOutOfCall(call, ccc, ret, out, allowsFieldFlow, argApa, apa, config) and
|
||||
(if allowsFieldFlow = false then ap instanceof ApNil else any())
|
||||
(if allowsFieldFlow = false then ap instanceof ApNil else any()) and
|
||||
parameterFlowThroughAllowed(c, pragma[only_bind_into](summaryCtx), kind)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1575,7 +1483,8 @@ private module MkStage<StageSig PrevStage> {
|
||||
ParameterPosition pos, Ap ap, Configuration config
|
||||
) {
|
||||
exists(ParamNodeEx param |
|
||||
fwdFlowInMayFlowThrough(call, cc, _, summaryCtx, argAp, param, ap, _, config) and
|
||||
fwdFlowIn(call, param, _, cc, _, summaryCtx, argAp, ap, config) and
|
||||
PrevStage::parameterMayFlowThrough(param, unbindApa(getApprox(ap)), config) and
|
||||
pos = param.getPosition()
|
||||
)
|
||||
}
|
||||
@@ -1596,55 +1505,41 @@ private module MkStage<StageSig PrevStage> {
|
||||
fwdFlowConsCand(ap1, c, ap2, config)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate returnFlowsThrough0(
|
||||
RetNodeEx ret, ReturnKindExt kind, FlowState state, CcCall ccc, DataFlowCallable c,
|
||||
ParameterPosition ppos, Ap argAp, Ap ap, Configuration config
|
||||
) {
|
||||
exists(boolean allowsFieldFlow |
|
||||
fwdFlow(ret, state, ccc, TParameterPositionSome(ppos), apSome(argAp), ap, config) and
|
||||
flowThroughOutOfCall(_, c, _, pragma[only_bind_into](ret), kind, _, allowsFieldFlow,
|
||||
pragma[only_bind_into](config)) and
|
||||
if allowsFieldFlow = false then ap instanceof ApNil else any()
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate returnFlowsThrough(
|
||||
RetNodeEx ret, ReturnKindExt kind, FlowState state, CcCall ccc, ParamNodeEx p, Ap argAp,
|
||||
Ap ap, Configuration config
|
||||
) {
|
||||
exists(boolean allowsFieldFlow, ApApprox argApa, ApApprox apa |
|
||||
fwdFlowRetFromArg(pragma[only_bind_into](ret), state, pragma[only_bind_into](ccc), _, p,
|
||||
argAp, pragma[only_bind_into](argApa), ap, pragma[only_bind_into](apa), config) and
|
||||
kind = ret.getKind() and
|
||||
fwdFlowThroughOutOfCall(_, ccc, ret, _, allowsFieldFlow, argApa, apa, config) and
|
||||
(if allowsFieldFlow = false then ap instanceof ApNil else any())
|
||||
exists(DataFlowCallable c, ParameterPosition ppos |
|
||||
returnFlowsThrough0(ret, kind, state, ccc, c, ppos, argAp, ap, config) and
|
||||
p.isParameterOf(c, ppos) and
|
||||
parameterFlowThroughAllowed(p, kind)
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate flowThroughIntoCall(
|
||||
DataFlowCall call, ArgNodeEx arg, ParamNodeEx p, boolean allowsFieldFlow, Ap argAp,
|
||||
Configuration config
|
||||
DataFlowCall call, ArgNodeEx arg, ParamNodeEx p, boolean allowsFieldFlow, Configuration config
|
||||
) {
|
||||
exists(ApApprox argApa |
|
||||
flowIntoCallApa(call, pragma[only_bind_into](arg), pragma[only_bind_into](p),
|
||||
allowsFieldFlow, argApa, pragma[only_bind_into](config)) and
|
||||
fwdFlow(arg, _, _, _, _, pragma[only_bind_into](argAp), argApa,
|
||||
exists(Ap argAp |
|
||||
flowIntoCall(call, pragma[only_bind_into](arg), pragma[only_bind_into](p), allowsFieldFlow,
|
||||
pragma[only_bind_into](config)) and
|
||||
fwdFlow(arg, _, _, _, _, pragma[only_bind_into](argAp), pragma[only_bind_into](config)) and
|
||||
returnFlowsThrough(_, _, _, _, p, pragma[only_bind_into](argAp), _,
|
||||
pragma[only_bind_into](config)) and
|
||||
if allowsFieldFlow = false then argAp instanceof ApNil else any()
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate flowIntoCallAp(
|
||||
DataFlowCall call, ArgNodeEx arg, ParamNodeEx p, boolean allowsFieldFlow, Ap ap,
|
||||
Configuration config
|
||||
) {
|
||||
exists(ApApprox apa |
|
||||
flowIntoCallApa(call, arg, p, allowsFieldFlow, apa, config) and
|
||||
fwdFlow(arg, _, _, _, _, ap, apa, config)
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate flowOutOfCallAp(
|
||||
DataFlowCall call, RetNodeEx ret, ReturnKindExt kind, NodeEx out, boolean allowsFieldFlow,
|
||||
Ap ap, Configuration config
|
||||
) {
|
||||
exists(ApApprox apa |
|
||||
flowOutOfCallApa(call, ret, kind, out, allowsFieldFlow, apa, config) and
|
||||
fwdFlow(ret, _, _, _, _, ap, apa, config)
|
||||
pragma[only_bind_into](config))
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1733,7 +1628,7 @@ private module MkStage<StageSig PrevStage> {
|
||||
// flow into a callable
|
||||
exists(ParamNodeEx p, boolean allowsFieldFlow |
|
||||
revFlow(p, state, TReturnCtxNone(), returnAp, ap, config) and
|
||||
flowIntoCallAp(_, node, p, allowsFieldFlow, ap, config) and
|
||||
flowIntoCall(_, node, p, allowsFieldFlow, config) and
|
||||
(if allowsFieldFlow = false then ap instanceof ApNil else any()) and
|
||||
returnCtx = TReturnCtxNone()
|
||||
)
|
||||
@@ -1787,41 +1682,22 @@ private module MkStage<StageSig PrevStage> {
|
||||
) {
|
||||
exists(NodeEx out, boolean allowsFieldFlow |
|
||||
revFlow(out, state, returnCtx, returnAp, ap, config) and
|
||||
flowOutOfCallAp(call, ret, kind, out, allowsFieldFlow, ap, config) and
|
||||
flowOutOfCall(call, ret, kind, out, allowsFieldFlow, config) and
|
||||
if allowsFieldFlow = false then ap instanceof ApNil else any()
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Same as `flowThroughIntoCall`, but restricted to calls that are reached
|
||||
* in the flow covered by `revFlow`, where data might flow through the target
|
||||
* callable and back out at `call`.
|
||||
*/
|
||||
pragma[nomagic]
|
||||
private predicate revFlowThroughIntoCall(
|
||||
DataFlowCall call, ArgNodeEx arg, ParamNodeEx p, boolean allowsFieldFlow, Ap argAp,
|
||||
Configuration config
|
||||
) {
|
||||
flowThroughIntoCall(call, arg, p, allowsFieldFlow, argAp, config) and
|
||||
revFlowIsReturned(call, _, _, _, _, config)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate revFlowParamToReturn(
|
||||
ParamNodeEx p, FlowState state, ReturnKindExt kind, Ap returnAp, Ap ap, Configuration config
|
||||
) {
|
||||
revFlow(p, state, TReturnCtxMaybeFlowThrough(kind), apSome(returnAp), ap, config) and
|
||||
parameterFlowThroughAllowed(p, kind)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate revFlowInToReturn(
|
||||
DataFlowCall call, ArgNodeEx arg, FlowState state, ReturnKindExt kind, Ap returnAp, Ap ap,
|
||||
Configuration config
|
||||
) {
|
||||
exists(ParamNodeEx p, boolean allowsFieldFlow |
|
||||
revFlowParamToReturn(p, state, kind, returnAp, ap, config) and
|
||||
revFlowThroughIntoCall(call, arg, p, allowsFieldFlow, ap, config)
|
||||
revFlow(pragma[only_bind_into](p), state,
|
||||
TReturnCtxMaybeFlowThrough(pragma[only_bind_into](kind)), apSome(returnAp), ap, config) and
|
||||
flowThroughIntoCall(call, arg, p, allowsFieldFlow, config) and
|
||||
(if allowsFieldFlow = false then ap instanceof ApNil else any()) and
|
||||
parameterFlowThroughAllowed(p, kind)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1874,11 +1750,6 @@ private module MkStage<StageSig PrevStage> {
|
||||
pragma[nomagic]
|
||||
predicate revFlow(NodeEx node, Configuration config) { revFlow(node, _, _, _, _, config) }
|
||||
|
||||
pragma[nomagic]
|
||||
predicate revFlowAp(NodeEx node, Ap ap, Configuration config) {
|
||||
revFlow(node, _, _, _, ap, config)
|
||||
}
|
||||
|
||||
// use an alias as a workaround for bad functionality-induced joins
|
||||
pragma[nomagic]
|
||||
additional predicate revFlowAlias(NodeEx node, Configuration config) {
|
||||
@@ -1915,9 +1786,9 @@ private module MkStage<StageSig PrevStage> {
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate parameterFlowsThroughRev(
|
||||
ParamNodeEx p, Ap ap, ReturnKindExt kind, Ap returnAp, Configuration config
|
||||
ParamNodeEx p, Ap ap, ReturnKindExt kind, Configuration config
|
||||
) {
|
||||
revFlow(p, _, TReturnCtxMaybeFlowThrough(kind), apSome(returnAp), ap, config) and
|
||||
revFlow(p, _, TReturnCtxMaybeFlowThrough(kind), apSome(_), ap, config) and
|
||||
parameterFlowThroughAllowed(p, kind)
|
||||
}
|
||||
|
||||
@@ -1925,36 +1796,27 @@ private module MkStage<StageSig PrevStage> {
|
||||
predicate parameterMayFlowThrough(ParamNodeEx p, Ap ap, Configuration config) {
|
||||
exists(RetNodeEx ret, ReturnKindExt kind |
|
||||
returnFlowsThrough(ret, kind, _, _, p, ap, _, config) and
|
||||
parameterFlowsThroughRev(p, ap, kind, _, config)
|
||||
parameterFlowsThroughRev(p, ap, kind, config)
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
predicate returnMayFlowThrough(
|
||||
RetNodeEx ret, Ap argAp, Ap ap, ReturnKindExt kind, Configuration config
|
||||
) {
|
||||
exists(ParamNodeEx p |
|
||||
returnFlowsThrough(ret, kind, _, _, p, argAp, ap, config) and
|
||||
parameterFlowsThroughRev(p, argAp, kind, ap, config)
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
predicate revFlowInToReturnIsReturned(
|
||||
DataFlowCall call, ArgNodeEx arg, FlowState state, ReturnCtx returnCtx, ApOption returnAp,
|
||||
Ap ap, Configuration config
|
||||
) {
|
||||
exists(ReturnKindExt returnKind0, Ap returnAp0 |
|
||||
revFlowInToReturn(call, arg, state, returnKind0, returnAp0, ap, config) and
|
||||
revFlowIsReturned(call, returnCtx, returnAp, returnKind0, returnAp0, config)
|
||||
predicate returnMayFlowThrough(RetNodeEx ret, ReturnKindExt kind, Configuration config) {
|
||||
exists(ParamNodeEx p, Ap ap |
|
||||
returnFlowsThrough(ret, kind, _, _, p, ap, _, config) and
|
||||
parameterFlowsThroughRev(p, ap, kind, config)
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
predicate callMayFlowThroughRev(DataFlowCall call, Configuration config) {
|
||||
exists(ArgNodeEx arg, FlowState state, ReturnCtx returnCtx, ApOption returnAp, Ap ap |
|
||||
exists(
|
||||
ReturnKindExt returnKind0, Ap returnAp0, ArgNodeEx arg, FlowState state,
|
||||
ReturnCtx returnCtx, ApOption returnAp, Ap ap
|
||||
|
|
||||
revFlow(arg, state, returnCtx, returnAp, ap, config) and
|
||||
revFlowInToReturnIsReturned(call, arg, state, returnCtx, returnAp, ap, config)
|
||||
revFlowInToReturn(call, arg, state, returnKind0, returnAp0, ap, config) and
|
||||
revFlowIsReturned(call, returnCtx, returnAp, returnKind0, returnAp0, config)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -2317,16 +2179,16 @@ private module LocalFlowBigStep {
|
||||
pragma[nomagic]
|
||||
predicate localFlowBigStep(
|
||||
NodeEx node1, FlowState state1, NodeEx node2, FlowState state2, boolean preservesValue,
|
||||
DataFlowType t, Configuration config, LocalCallContext callContext
|
||||
AccessPathFrontNil apf, Configuration config, LocalCallContext callContext
|
||||
) {
|
||||
localFlowStepPlus(node1, state1, node2, preservesValue, t, config, callContext) and
|
||||
localFlowStepPlus(node1, state1, node2, preservesValue, apf.getType(), config, callContext) and
|
||||
localFlowExit(node2, state1, config) and
|
||||
state1 = state2
|
||||
or
|
||||
additionalLocalFlowStepNodeCand2(node1, state1, node2, state2, config) and
|
||||
state1 != state2 and
|
||||
preservesValue = false and
|
||||
t = node2.getDataFlowType() and
|
||||
apf = TFrontNil(node2.getDataFlowType()) and
|
||||
callContext.relevantFor(node1.getEnclosingCallable()) and
|
||||
not exists(DataFlowCall call | call = callContext.(LocalCallContextSpecificCall).getCall() |
|
||||
isUnreachableInCallCached(node1.asNode(), call) or
|
||||
@@ -2340,87 +2202,11 @@ private import LocalFlowBigStep
|
||||
private module Stage3Param implements MkStage<Stage2>::StageParam {
|
||||
private module PrevStage = Stage2;
|
||||
|
||||
class Ap = ApproxAccessPathFront;
|
||||
|
||||
class ApNil = ApproxAccessPathFrontNil;
|
||||
|
||||
PrevStage::Ap getApprox(Ap ap) { result = ap.toBoolNonEmpty() }
|
||||
|
||||
ApNil getApNil(NodeEx node) {
|
||||
PrevStage::revFlow(node, _) and result = TApproxFrontNil(node.getDataFlowType())
|
||||
}
|
||||
|
||||
bindingset[tc, tail]
|
||||
Ap apCons(TypedContent tc, Ap tail) { result.getAHead() = tc and exists(tail) }
|
||||
|
||||
pragma[noinline]
|
||||
Content getHeadContent(Ap ap) { result = ap.getAHead().getContent() }
|
||||
|
||||
class ApOption = ApproxAccessPathFrontOption;
|
||||
|
||||
ApOption apNone() { result = TApproxAccessPathFrontNone() }
|
||||
|
||||
ApOption apSome(Ap ap) { result = TApproxAccessPathFrontSome(ap) }
|
||||
|
||||
import BooleanCallContext
|
||||
|
||||
predicate localStep(
|
||||
NodeEx node1, FlowState state1, NodeEx node2, FlowState state2, boolean preservesValue,
|
||||
ApproxAccessPathFrontNil ap, Configuration config, LocalCc lcc
|
||||
) {
|
||||
localFlowBigStep(node1, state1, node2, state2, preservesValue, ap.getType(), config, _) and
|
||||
exists(lcc)
|
||||
}
|
||||
|
||||
predicate flowOutOfCall = flowOutOfCallNodeCand2/6;
|
||||
|
||||
predicate flowIntoCall = flowIntoCallNodeCand2/5;
|
||||
|
||||
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.getAHead().getContent()
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate castingNodeEx(NodeEx node) { node.asNode() instanceof CastingNode }
|
||||
|
||||
bindingset[node, state, ap, config]
|
||||
predicate filter(NodeEx node, FlowState state, Ap ap, Configuration config) {
|
||||
exists(state) and
|
||||
exists(config) and
|
||||
(if castingNodeEx(node) then compatibleTypes(node.getDataFlowType(), ap.getType()) else any()) and
|
||||
(
|
||||
notExpectsContent(node)
|
||||
or
|
||||
expectsContentCand(node, ap, config)
|
||||
)
|
||||
}
|
||||
|
||||
bindingset[ap, contentType]
|
||||
predicate typecheckStore(Ap ap, DataFlowType contentType) {
|
||||
// We need to typecheck stores here, since reverse flow through a getter
|
||||
// might have a different type here compared to inside the getter.
|
||||
compatibleTypes(ap.getType(), contentType)
|
||||
}
|
||||
}
|
||||
|
||||
private module Stage3 implements StageSig {
|
||||
import MkStage<Stage2>::Stage<Stage3Param>
|
||||
}
|
||||
|
||||
private module Stage4Param implements MkStage<Stage3>::StageParam {
|
||||
private module PrevStage = Stage3;
|
||||
|
||||
class Ap = AccessPathFront;
|
||||
|
||||
class ApNil = AccessPathFrontNil;
|
||||
|
||||
PrevStage::Ap getApprox(Ap ap) { result = ap.toApprox() }
|
||||
PrevStage::Ap getApprox(Ap ap) { result = ap.toBoolNonEmpty() }
|
||||
|
||||
ApNil getApNil(NodeEx node) {
|
||||
PrevStage::revFlow(node, _) and result = TFrontNil(node.getDataFlowType())
|
||||
@@ -2440,42 +2226,16 @@ private module Stage4Param implements MkStage<Stage3>::StageParam {
|
||||
|
||||
import BooleanCallContext
|
||||
|
||||
pragma[nomagic]
|
||||
predicate localStep(
|
||||
NodeEx node1, FlowState state1, NodeEx node2, FlowState state2, boolean preservesValue,
|
||||
ApNil ap, Configuration config, LocalCc lcc
|
||||
) {
|
||||
localFlowBigStep(node1, state1, node2, state2, preservesValue, ap.getType(), config, _) and
|
||||
PrevStage::revFlow(node1, pragma[only_bind_into](state1), _, pragma[only_bind_into](config)) and
|
||||
PrevStage::revFlowAlias(node2, pragma[only_bind_into](state2), _, pragma[only_bind_into](config)) and
|
||||
exists(lcc)
|
||||
localFlowBigStep(node1, state1, node2, state2, preservesValue, ap, config, _) and exists(lcc)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
predicate flowOutOfCall(
|
||||
DataFlowCall call, RetNodeEx node1, ReturnKindExt kind, NodeEx node2, boolean allowsFieldFlow,
|
||||
Configuration config
|
||||
) {
|
||||
exists(FlowState state |
|
||||
flowOutOfCallNodeCand2(call, node1, kind, node2, allowsFieldFlow, config) and
|
||||
PrevStage::revFlow(node2, pragma[only_bind_into](state), _, pragma[only_bind_into](config)) and
|
||||
PrevStage::revFlowAlias(node1, pragma[only_bind_into](state), _,
|
||||
pragma[only_bind_into](config))
|
||||
)
|
||||
}
|
||||
predicate flowOutOfCall = flowOutOfCallNodeCand2/6;
|
||||
|
||||
pragma[nomagic]
|
||||
predicate flowIntoCall(
|
||||
DataFlowCall call, ArgNodeEx node1, ParamNodeEx node2, boolean allowsFieldFlow,
|
||||
Configuration config
|
||||
) {
|
||||
exists(FlowState state |
|
||||
flowIntoCallNodeCand2(call, node1, node2, allowsFieldFlow, config) and
|
||||
PrevStage::revFlow(node2, pragma[only_bind_into](state), _, pragma[only_bind_into](config)) and
|
||||
PrevStage::revFlowAlias(node1, pragma[only_bind_into](state), _,
|
||||
pragma[only_bind_into](config))
|
||||
)
|
||||
}
|
||||
predicate flowIntoCall = flowIntoCallNodeCand2/5;
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate clearSet(NodeEx node, ContentSet c, Configuration config) {
|
||||
@@ -2531,8 +2291,8 @@ private module Stage4Param implements MkStage<Stage3>::StageParam {
|
||||
}
|
||||
}
|
||||
|
||||
private module Stage4 implements StageSig {
|
||||
import MkStage<Stage3>::Stage<Stage4Param>
|
||||
private module Stage3 implements StageSig {
|
||||
import MkStage<Stage2>::Stage<Stage3Param>
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -2543,8 +2303,8 @@ private predicate flowCandSummaryCtx(
|
||||
NodeEx node, FlowState state, AccessPathFront argApf, Configuration config
|
||||
) {
|
||||
exists(AccessPathFront apf |
|
||||
Stage4::revFlow(node, state, TReturnCtxMaybeFlowThrough(_), _, apf, config) and
|
||||
Stage4::fwdFlow(node, state, any(Stage4::CcCall ccc), _, TAccessPathFrontSome(argApf), apf,
|
||||
Stage3::revFlow(node, state, TReturnCtxMaybeFlowThrough(_), _, apf, config) and
|
||||
Stage3::fwdFlow(node, state, any(Stage3::CcCall ccc), _, TAccessPathFrontSome(argApf), apf,
|
||||
config)
|
||||
)
|
||||
}
|
||||
@@ -2555,10 +2315,10 @@ private predicate flowCandSummaryCtx(
|
||||
*/
|
||||
private predicate expensiveLen2unfolding(TypedContent tc, Configuration config) {
|
||||
exists(int tails, int nodes, int apLimit, int tupleLimit |
|
||||
tails = strictcount(AccessPathFront apf | Stage4::consCand(tc, apf, config)) and
|
||||
tails = strictcount(AccessPathFront apf | Stage3::consCand(tc, apf, config)) and
|
||||
nodes =
|
||||
strictcount(NodeEx n, FlowState state |
|
||||
Stage4::revFlow(n, state, any(AccessPathFrontHead apf | apf.getHead() = tc), config)
|
||||
Stage3::revFlow(n, state, any(AccessPathFrontHead apf | apf.getHead() = tc), config)
|
||||
or
|
||||
flowCandSummaryCtx(n, state, any(AccessPathFrontHead apf | apf.getHead() = tc), config)
|
||||
) and
|
||||
@@ -2572,11 +2332,11 @@ private predicate expensiveLen2unfolding(TypedContent tc, Configuration config)
|
||||
private newtype TAccessPathApprox =
|
||||
TNil(DataFlowType t) or
|
||||
TConsNil(TypedContent tc, DataFlowType t) {
|
||||
Stage4::consCand(tc, TFrontNil(t), _) and
|
||||
Stage3::consCand(tc, TFrontNil(t), _) and
|
||||
not expensiveLen2unfolding(tc, _)
|
||||
} or
|
||||
TConsCons(TypedContent tc1, TypedContent tc2, int len) {
|
||||
Stage4::consCand(tc1, TFrontHead(tc2), _) and
|
||||
Stage3::consCand(tc1, TFrontHead(tc2), _) and
|
||||
len in [2 .. accessPathLimit()] and
|
||||
not expensiveLen2unfolding(tc1, _)
|
||||
} or
|
||||
@@ -2706,7 +2466,7 @@ private class AccessPathApproxCons1 extends AccessPathApproxCons, TCons1 {
|
||||
override AccessPathApprox pop(TypedContent head) {
|
||||
head = tc and
|
||||
(
|
||||
exists(TypedContent tc2 | Stage4::consCand(tc, TFrontHead(tc2), _) |
|
||||
exists(TypedContent tc2 | Stage3::consCand(tc, TFrontHead(tc2), _) |
|
||||
result = TConsCons(tc2, _, len - 1)
|
||||
or
|
||||
len = 2 and
|
||||
@@ -2717,7 +2477,7 @@ private class AccessPathApproxCons1 extends AccessPathApproxCons, TCons1 {
|
||||
or
|
||||
exists(DataFlowType t |
|
||||
len = 1 and
|
||||
Stage4::consCand(tc, TFrontNil(t), _) and
|
||||
Stage3::consCand(tc, TFrontNil(t), _) and
|
||||
result = TNil(t)
|
||||
)
|
||||
)
|
||||
@@ -2742,14 +2502,13 @@ private class AccessPathApproxOption extends TAccessPathApproxOption {
|
||||
}
|
||||
}
|
||||
|
||||
private module Stage5Param implements MkStage<Stage4>::StageParam {
|
||||
private module PrevStage = Stage4;
|
||||
private module Stage4Param implements MkStage<Stage3>::StageParam {
|
||||
private module PrevStage = Stage3;
|
||||
|
||||
class Ap = AccessPathApprox;
|
||||
|
||||
class ApNil = AccessPathApproxNil;
|
||||
|
||||
pragma[nomagic]
|
||||
PrevStage::Ap getApprox(Ap ap) { result = ap.getFront() }
|
||||
|
||||
ApNil getApNil(NodeEx node) {
|
||||
@@ -2775,9 +2534,7 @@ private module Stage5Param implements MkStage<Stage4>::StageParam {
|
||||
NodeEx node1, FlowState state1, NodeEx node2, FlowState state2, boolean preservesValue,
|
||||
ApNil ap, Configuration config, LocalCc lcc
|
||||
) {
|
||||
localFlowBigStep(node1, state1, node2, state2, preservesValue, ap.getType(), config, lcc) and
|
||||
PrevStage::revFlow(node1, pragma[only_bind_into](state1), _, pragma[only_bind_into](config)) and
|
||||
PrevStage::revFlowAlias(node2, pragma[only_bind_into](state2), _, pragma[only_bind_into](config))
|
||||
localFlowBigStep(node1, state1, node2, state2, preservesValue, ap.getFront(), config, lcc)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
@@ -2814,7 +2571,7 @@ private module Stage5Param implements MkStage<Stage4>::StageParam {
|
||||
predicate typecheckStore(Ap ap, DataFlowType contentType) { any() }
|
||||
}
|
||||
|
||||
private module Stage5 = MkStage<Stage4>::Stage<Stage5Param>;
|
||||
private module Stage4 = MkStage<Stage3>::Stage<Stage4Param>;
|
||||
|
||||
bindingset[conf, result]
|
||||
private Configuration unbindConf(Configuration conf) {
|
||||
@@ -2828,8 +2585,8 @@ private predicate nodeMayUseSummary0(
|
||||
) {
|
||||
exists(AccessPathApprox apa0 |
|
||||
c = n.getEnclosingCallable() and
|
||||
Stage5::revFlow(n, state, TReturnCtxMaybeFlowThrough(_), _, apa0, config) and
|
||||
Stage5::fwdFlow(n, state, any(CallContextCall ccc), TParameterPositionSome(pos),
|
||||
Stage4::revFlow(n, state, TReturnCtxMaybeFlowThrough(_), _, apa0, config) and
|
||||
Stage4::fwdFlow(n, state, any(CallContextCall ccc), TParameterPositionSome(pos),
|
||||
TAccessPathApproxSome(apa), apa0, config)
|
||||
)
|
||||
}
|
||||
@@ -2839,7 +2596,7 @@ private predicate nodeMayUseSummary(
|
||||
NodeEx n, FlowState state, AccessPathApprox apa, Configuration config
|
||||
) {
|
||||
exists(DataFlowCallable c, ParameterPosition pos, ParamNodeEx p |
|
||||
Stage5::parameterMayFlowThrough(p, apa, config) and
|
||||
Stage4::parameterMayFlowThrough(p, apa, config) and
|
||||
nodeMayUseSummary0(n, c, pos, state, apa, config) and
|
||||
p.isParameterOf(c, pos)
|
||||
)
|
||||
@@ -2849,8 +2606,8 @@ private newtype TSummaryCtx =
|
||||
TSummaryCtxNone() or
|
||||
TSummaryCtxSome(ParamNodeEx p, FlowState state, AccessPath ap) {
|
||||
exists(Configuration config |
|
||||
Stage5::parameterMayFlowThrough(p, ap.getApprox(), config) and
|
||||
Stage5::revFlow(p, state, _, config)
|
||||
Stage4::parameterMayFlowThrough(p, ap.getApprox(), config) and
|
||||
Stage4::revFlow(p, state, _, config)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -2899,7 +2656,7 @@ private int count1to2unfold(AccessPathApproxCons1 apa, Configuration config) {
|
||||
len = apa.len() and
|
||||
result =
|
||||
strictcount(AccessPathFront apf |
|
||||
Stage5::consCand(tc, any(AccessPathApprox ap | ap.getFront() = apf and ap.len() = len - 1),
|
||||
Stage4::consCand(tc, any(AccessPathApprox ap | ap.getFront() = apf and ap.len() = len - 1),
|
||||
config)
|
||||
)
|
||||
)
|
||||
@@ -2908,7 +2665,7 @@ private int count1to2unfold(AccessPathApproxCons1 apa, Configuration config) {
|
||||
private int countNodesUsingAccessPath(AccessPathApprox apa, Configuration config) {
|
||||
result =
|
||||
strictcount(NodeEx n, FlowState state |
|
||||
Stage5::revFlow(n, state, apa, config) or nodeMayUseSummary(n, state, apa, config)
|
||||
Stage4::revFlow(n, state, apa, config) or nodeMayUseSummary(n, state, apa, config)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -2929,7 +2686,7 @@ private predicate expensiveLen1to2unfolding(AccessPathApproxCons1 apa, Configura
|
||||
private AccessPathApprox getATail(AccessPathApprox apa, Configuration config) {
|
||||
exists(TypedContent head |
|
||||
apa.pop(head) = result and
|
||||
Stage5::consCand(head, result, config)
|
||||
Stage4::consCand(head, result, config)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -3011,7 +2768,7 @@ private newtype TPathNode =
|
||||
NodeEx node, FlowState state, CallContext cc, SummaryCtx sc, AccessPath ap, Configuration config
|
||||
) {
|
||||
// A PathNode is introduced by a source ...
|
||||
Stage5::revFlow(node, state, config) and
|
||||
Stage4::revFlow(node, state, config) and
|
||||
sourceNode(node, state, config) and
|
||||
(
|
||||
if hasSourceCallCtx(config)
|
||||
@@ -3025,7 +2782,7 @@ private newtype TPathNode =
|
||||
exists(PathNodeMid mid |
|
||||
pathStep(mid, node, state, cc, sc, ap) and
|
||||
pragma[only_bind_into](config) = mid.getConfiguration() and
|
||||
Stage5::revFlow(node, state, ap.getApprox(), pragma[only_bind_into](config))
|
||||
Stage4::revFlow(node, state, ap.getApprox(), pragma[only_bind_into](config))
|
||||
)
|
||||
} or
|
||||
TPathNodeSink(NodeEx node, FlowState state, Configuration config) {
|
||||
@@ -3167,7 +2924,7 @@ private class AccessPathCons2 extends AccessPath, TAccessPathCons2 {
|
||||
override TypedContent getHead() { result = head1 }
|
||||
|
||||
override AccessPath getTail() {
|
||||
Stage5::consCand(head1, result.getApprox(), _) and
|
||||
Stage4::consCand(head1, result.getApprox(), _) and
|
||||
result.getHead() = head2 and
|
||||
result.length() = len - 1
|
||||
}
|
||||
@@ -3198,7 +2955,7 @@ private class AccessPathCons1 extends AccessPath, TAccessPathCons1 {
|
||||
override TypedContent getHead() { result = head }
|
||||
|
||||
override AccessPath getTail() {
|
||||
Stage5::consCand(head, result.getApprox(), _) and result.length() = len - 1
|
||||
Stage4::consCand(head, result.getApprox(), _) and result.length() = len - 1
|
||||
}
|
||||
|
||||
override AccessPathFrontHead getFront() { result = TFrontHead(head) }
|
||||
@@ -3590,8 +3347,7 @@ private predicate pathStep(
|
||||
AccessPath ap0, NodeEx midnode, FlowState state0, Configuration conf, LocalCallContext localCC
|
||||
|
|
||||
pathNode(mid, midnode, state0, cc, sc, ap0, conf, localCC) and
|
||||
localFlowBigStep(midnode, state0, node, state, false, ap.(AccessPathNil).getType(), conf,
|
||||
localCC) and
|
||||
localFlowBigStep(midnode, state0, node, state, false, ap.getFront(), conf, localCC) and
|
||||
ap0 instanceof AccessPathNil
|
||||
)
|
||||
or
|
||||
@@ -3633,7 +3389,7 @@ private predicate pathReadStep(
|
||||
) {
|
||||
ap0 = mid.getAp() and
|
||||
tc = ap0.getHead() and
|
||||
Stage5::readStepCand(mid.getNodeEx(), tc.getContent(), node, mid.getConfiguration()) and
|
||||
Stage4::readStepCand(mid.getNodeEx(), tc.getContent(), node, mid.getConfiguration()) and
|
||||
state = mid.getState() and
|
||||
cc = mid.getCallContext()
|
||||
}
|
||||
@@ -3643,7 +3399,7 @@ private predicate pathStoreStep(
|
||||
PathNodeMid mid, NodeEx node, FlowState state, AccessPath ap0, TypedContent tc, CallContext cc
|
||||
) {
|
||||
ap0 = mid.getAp() and
|
||||
Stage5::storeStepCand(mid.getNodeEx(), _, tc, node, _, mid.getConfiguration()) and
|
||||
Stage4::storeStepCand(mid.getNodeEx(), _, tc, node, _, mid.getConfiguration()) and
|
||||
state = mid.getState() and
|
||||
cc = mid.getCallContext()
|
||||
}
|
||||
@@ -3680,7 +3436,7 @@ private NodeEx getAnOutNodeFlow(
|
||||
ReturnKindExt kind, DataFlowCall call, AccessPathApprox apa, Configuration config
|
||||
) {
|
||||
result.asNode() = kind.getAnOutNode(call) and
|
||||
Stage5::revFlow(result, _, apa, config)
|
||||
Stage4::revFlow(result, _, apa, config)
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -3716,7 +3472,7 @@ private predicate parameterCand(
|
||||
DataFlowCallable callable, ParameterPosition pos, AccessPathApprox apa, Configuration config
|
||||
) {
|
||||
exists(ParamNodeEx p |
|
||||
Stage5::revFlow(p, _, apa, config) and
|
||||
Stage4::revFlow(p, _, apa, config) and
|
||||
p.isParameterOf(callable, pos)
|
||||
)
|
||||
}
|
||||
@@ -3995,17 +3751,9 @@ predicate stageStats(
|
||||
n = 45 and
|
||||
Stage4::stats(false, nodes, fields, conscand, states, tuples, config)
|
||||
or
|
||||
stage = "5 Fwd" and
|
||||
n = 50 and
|
||||
Stage5::stats(true, nodes, fields, conscand, states, tuples, config)
|
||||
stage = "5 Fwd" and n = 50 and finalStats(true, nodes, fields, conscand, states, tuples)
|
||||
or
|
||||
stage = "5 Rev" and
|
||||
n = 55 and
|
||||
Stage5::stats(false, nodes, fields, conscand, states, tuples, config)
|
||||
or
|
||||
stage = "6 Fwd" and n = 60 and finalStats(true, nodes, fields, conscand, states, tuples)
|
||||
or
|
||||
stage = "6 Rev" and n = 65 and finalStats(false, nodes, fields, conscand, states, tuples)
|
||||
stage = "5 Rev" and n = 55 and finalStats(false, nodes, fields, conscand, states, tuples)
|
||||
}
|
||||
|
||||
private module FlowExploration {
|
||||
|
||||
@@ -621,6 +621,23 @@ private predicate parameterFlowThroughAllowed(ParamNodeEx p, ReturnKindExt kind)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if flow from a parameter at position `pos` inside `c` to a return node of
|
||||
* kind `kind` is allowed.
|
||||
*
|
||||
* We don't expect a parameter to return stored in itself, unless
|
||||
* explicitly allowed
|
||||
*/
|
||||
bindingset[c, pos, kind]
|
||||
private predicate parameterFlowThroughAllowed(
|
||||
DataFlowCallable c, ParameterPosition pos, ReturnKindExt kind
|
||||
) {
|
||||
exists(ParamNodeEx p |
|
||||
p.isParameterOf(c, pos) and
|
||||
parameterFlowThroughAllowed(p, kind)
|
||||
)
|
||||
}
|
||||
|
||||
private module Stage1 implements StageSig {
|
||||
class Ap = Unit;
|
||||
|
||||
@@ -963,12 +980,6 @@ private module Stage1 implements StageSig {
|
||||
pragma[nomagic]
|
||||
predicate revFlow(NodeEx node, Configuration config) { revFlow(node, _, config) }
|
||||
|
||||
pragma[nomagic]
|
||||
predicate revFlowAp(NodeEx node, Ap ap, Configuration config) {
|
||||
revFlow(node, config) and
|
||||
exists(ap)
|
||||
}
|
||||
|
||||
bindingset[node, state, config]
|
||||
predicate revFlow(NodeEx node, FlowState state, Ap ap, Configuration config) {
|
||||
revFlow(node, _, pragma[only_bind_into](config)) and
|
||||
@@ -1011,13 +1022,9 @@ private module Stage1 implements StageSig {
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
predicate returnMayFlowThrough(
|
||||
RetNodeEx ret, Ap argAp, Ap ap, ReturnKindExt kind, Configuration config
|
||||
) {
|
||||
predicate returnMayFlowThrough(RetNodeEx ret, ReturnKindExt kind, Configuration config) {
|
||||
throughFlowNodeCand(ret, config) and
|
||||
kind = ret.getKind() and
|
||||
exists(argAp) and
|
||||
exists(ap)
|
||||
kind = ret.getKind()
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
@@ -1182,8 +1189,6 @@ private signature module StageSig {
|
||||
|
||||
predicate revFlow(NodeEx node, Configuration config);
|
||||
|
||||
predicate revFlowAp(NodeEx node, Ap ap, Configuration config);
|
||||
|
||||
bindingset[node, state, config]
|
||||
predicate revFlow(NodeEx node, FlowState state, Ap ap, Configuration config);
|
||||
|
||||
@@ -1191,9 +1196,7 @@ private signature module StageSig {
|
||||
|
||||
predicate parameterMayFlowThrough(ParamNodeEx p, Ap ap, Configuration config);
|
||||
|
||||
predicate returnMayFlowThrough(
|
||||
RetNodeEx ret, Ap argAp, Ap ap, ReturnKindExt kind, Configuration config
|
||||
);
|
||||
predicate returnMayFlowThrough(RetNodeEx ret, ReturnKindExt kind, Configuration config);
|
||||
|
||||
predicate storeStepCand(
|
||||
NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, DataFlowType contentType,
|
||||
@@ -1278,43 +1281,21 @@ private module MkStage<StageSig PrevStage> {
|
||||
import Param
|
||||
|
||||
/* Begin: Stage logic. */
|
||||
// use an alias as a workaround for bad functionality-induced joins
|
||||
pragma[nomagic]
|
||||
private predicate revFlowApAlias(NodeEx node, ApApprox apa, Configuration config) {
|
||||
PrevStage::revFlowAp(node, apa, config)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate flowIntoCallApa(
|
||||
DataFlowCall call, ArgNodeEx arg, ParamNodeEx p, boolean allowsFieldFlow, ApApprox apa,
|
||||
Configuration config
|
||||
) {
|
||||
flowIntoCall(call, arg, p, allowsFieldFlow, config) and
|
||||
PrevStage::revFlowAp(p, pragma[only_bind_into](apa), pragma[only_bind_into](config)) and
|
||||
revFlowApAlias(arg, pragma[only_bind_into](apa), pragma[only_bind_into](config))
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate flowOutOfCallApa(
|
||||
DataFlowCall call, RetNodeEx ret, ReturnKindExt kind, NodeEx out, boolean allowsFieldFlow,
|
||||
ApApprox apa, Configuration config
|
||||
) {
|
||||
flowOutOfCall(call, ret, kind, out, allowsFieldFlow, config) and
|
||||
PrevStage::revFlowAp(out, pragma[only_bind_into](apa), pragma[only_bind_into](config)) and
|
||||
revFlowApAlias(ret, pragma[only_bind_into](apa), pragma[only_bind_into](config))
|
||||
bindingset[result, apa]
|
||||
private ApApprox unbindApa(ApApprox apa) {
|
||||
pragma[only_bind_out](apa) = pragma[only_bind_out](result)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate flowThroughOutOfCall(
|
||||
DataFlowCall call, CcCall ccc, RetNodeEx ret, NodeEx out, boolean allowsFieldFlow,
|
||||
ApApprox argApa, ApApprox apa, Configuration config
|
||||
DataFlowCall call, DataFlowCallable c, CcCall ccc, RetNodeEx ret, ReturnKindExt kind,
|
||||
NodeEx out, boolean allowsFieldFlow, Configuration config
|
||||
) {
|
||||
exists(ReturnKindExt kind |
|
||||
flowOutOfCallApa(call, ret, kind, out, allowsFieldFlow, apa, pragma[only_bind_into](config)) and
|
||||
PrevStage::callMayFlowThroughRev(call, pragma[only_bind_into](config)) and
|
||||
PrevStage::returnMayFlowThrough(ret, argApa, apa, kind, pragma[only_bind_into](config)) and
|
||||
matchesCall(ccc, call)
|
||||
)
|
||||
flowOutOfCall(call, ret, kind, out, allowsFieldFlow, pragma[only_bind_into](config)) and
|
||||
PrevStage::callMayFlowThroughRev(call, pragma[only_bind_into](config)) and
|
||||
PrevStage::returnMayFlowThrough(ret, kind, pragma[only_bind_into](config)) and
|
||||
matchesCall(ccc, call) and
|
||||
c = ret.getEnclosingCallable()
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1326,50 +1307,39 @@ private module MkStage<StageSig PrevStage> {
|
||||
* corresponding parameter position and access path of that argument, respectively.
|
||||
*/
|
||||
pragma[nomagic]
|
||||
additional predicate fwdFlow(
|
||||
NodeEx node, FlowState state, Cc cc, ParameterPositionOption summaryCtx, ApOption argAp,
|
||||
Ap ap, ApApprox apa, Configuration config
|
||||
) {
|
||||
fwdFlow0(node, state, cc, summaryCtx, argAp, ap, apa, config) and
|
||||
PrevStage::revFlow(node, state, apa, config) and
|
||||
filter(node, state, ap, config)
|
||||
}
|
||||
|
||||
pragma[inline]
|
||||
additional predicate fwdFlow(
|
||||
NodeEx node, FlowState state, Cc cc, ParameterPositionOption summaryCtx, ApOption argAp,
|
||||
Ap ap, Configuration config
|
||||
) {
|
||||
fwdFlow(node, state, cc, summaryCtx, argAp, ap, _, config)
|
||||
fwdFlow0(node, state, cc, summaryCtx, argAp, ap, config) and
|
||||
PrevStage::revFlow(node, state, unbindApa(getApprox(ap)), config) and
|
||||
filter(node, state, ap, config)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate fwdFlow0(
|
||||
NodeEx node, FlowState state, Cc cc, ParameterPositionOption summaryCtx, ApOption argAp,
|
||||
Ap ap, ApApprox apa, Configuration config
|
||||
Ap ap, Configuration config
|
||||
) {
|
||||
sourceNode(node, state, config) and
|
||||
(if hasSourceCallCtx(config) then cc = ccSomeCall() else cc = ccNone()) and
|
||||
argAp = apNone() and
|
||||
summaryCtx = TParameterPositionNone() and
|
||||
ap = getApNil(node) and
|
||||
apa = getApprox(ap)
|
||||
ap = getApNil(node)
|
||||
or
|
||||
exists(NodeEx mid, FlowState state0, Ap ap0, ApApprox apa0, LocalCc localCc |
|
||||
fwdFlow(mid, state0, cc, summaryCtx, argAp, ap0, apa0, config) and
|
||||
exists(NodeEx mid, FlowState state0, Ap ap0, LocalCc localCc |
|
||||
fwdFlow(mid, state0, cc, summaryCtx, argAp, ap0, config) and
|
||||
localCc = getLocalCc(mid, cc)
|
||||
|
|
||||
localStep(mid, state0, node, state, true, _, config, localCc) and
|
||||
ap = ap0 and
|
||||
apa = apa0
|
||||
ap = ap0
|
||||
or
|
||||
localStep(mid, state0, node, state, false, ap, config, localCc) and
|
||||
ap0 instanceof ApNil and
|
||||
apa = getApprox(ap)
|
||||
ap0 instanceof ApNil
|
||||
)
|
||||
or
|
||||
exists(NodeEx mid |
|
||||
fwdFlow(mid, pragma[only_bind_into](state), _, _, _, ap, apa, pragma[only_bind_into](config)) and
|
||||
fwdFlow(mid, pragma[only_bind_into](state), _, _, _, ap, pragma[only_bind_into](config)) and
|
||||
jumpStep(mid, node, config) and
|
||||
cc = ccNone() and
|
||||
summaryCtx = TParameterPositionNone() and
|
||||
@@ -1382,8 +1352,7 @@ private module MkStage<StageSig PrevStage> {
|
||||
cc = ccNone() and
|
||||
summaryCtx = TParameterPositionNone() and
|
||||
argAp = apNone() and
|
||||
ap = getApNil(node) and
|
||||
apa = getApprox(ap)
|
||||
ap = getApNil(node)
|
||||
)
|
||||
or
|
||||
exists(NodeEx mid, FlowState state0, ApNil nil |
|
||||
@@ -1392,32 +1361,32 @@ private module MkStage<StageSig PrevStage> {
|
||||
cc = ccNone() and
|
||||
summaryCtx = TParameterPositionNone() and
|
||||
argAp = apNone() and
|
||||
ap = getApNil(node) and
|
||||
apa = getApprox(ap)
|
||||
ap = getApNil(node)
|
||||
)
|
||||
or
|
||||
// store
|
||||
exists(TypedContent tc, Ap ap0 |
|
||||
fwdFlowStore(_, ap0, tc, node, state, cc, summaryCtx, argAp, config) and
|
||||
ap = apCons(tc, ap0) and
|
||||
apa = getApprox(ap)
|
||||
ap = apCons(tc, ap0)
|
||||
)
|
||||
or
|
||||
// read
|
||||
exists(Ap ap0, Content c |
|
||||
fwdFlowRead(ap0, c, _, node, state, cc, summaryCtx, argAp, config) and
|
||||
fwdFlowConsCand(ap0, c, ap, config) and
|
||||
apa = getApprox(ap)
|
||||
fwdFlowConsCand(ap0, c, ap, config)
|
||||
)
|
||||
or
|
||||
// flow into a callable
|
||||
fwdFlowIn(_, node, state, _, cc, _, _, ap, apa, config) and
|
||||
if PrevStage::parameterMayFlowThrough(node, apa, config)
|
||||
then (
|
||||
summaryCtx = TParameterPositionSome(node.(ParamNodeEx).getPosition()) and
|
||||
argAp = apSome(ap)
|
||||
) else (
|
||||
summaryCtx = TParameterPositionNone() and argAp = apNone()
|
||||
exists(ApApprox apa |
|
||||
fwdFlowIn(_, node, state, _, cc, _, _, ap, config) and
|
||||
apa = getApprox(ap) and
|
||||
if PrevStage::parameterMayFlowThrough(node, apa, config)
|
||||
then (
|
||||
summaryCtx = TParameterPositionSome(node.(ParamNodeEx).getPosition()) and
|
||||
argAp = apSome(ap)
|
||||
) else (
|
||||
summaryCtx = TParameterPositionNone() and argAp = apNone()
|
||||
)
|
||||
)
|
||||
or
|
||||
// flow out of a callable
|
||||
@@ -1425,8 +1394,8 @@ private module MkStage<StageSig PrevStage> {
|
||||
DataFlowCall call, RetNodeEx ret, boolean allowsFieldFlow, CcNoCall innercc,
|
||||
DataFlowCallable inner
|
||||
|
|
||||
fwdFlow(ret, state, innercc, summaryCtx, argAp, ap, apa, config) and
|
||||
flowOutOfCallApa(call, ret, _, node, allowsFieldFlow, apa, config) and
|
||||
fwdFlow(ret, state, innercc, summaryCtx, argAp, ap, config) and
|
||||
flowOutOfCall(call, ret, _, node, allowsFieldFlow, config) and
|
||||
inner = ret.getEnclosingCallable() and
|
||||
cc = getCallContextReturn(inner, call, innercc) and
|
||||
if allowsFieldFlow = false then ap instanceof ApNil else any()
|
||||
@@ -1434,7 +1403,7 @@ private module MkStage<StageSig PrevStage> {
|
||||
or
|
||||
// flow through a callable
|
||||
exists(DataFlowCall call, ParameterPosition summaryCtx0, Ap argAp0 |
|
||||
fwdFlowOutFromArg(call, node, state, summaryCtx0, argAp0, ap, apa, config) and
|
||||
fwdFlowOutFromArg(call, node, state, summaryCtx0, argAp0, ap, config) and
|
||||
fwdFlowIsEntered(call, cc, summaryCtx, argAp, summaryCtx0, argAp0, config)
|
||||
)
|
||||
}
|
||||
@@ -1444,9 +1413,9 @@ private module MkStage<StageSig PrevStage> {
|
||||
NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, FlowState state, Cc cc,
|
||||
ParameterPositionOption summaryCtx, ApOption argAp, Configuration config
|
||||
) {
|
||||
exists(DataFlowType contentType, ApApprox apa1 |
|
||||
fwdFlow(node1, state, cc, summaryCtx, argAp, ap1, apa1, config) and
|
||||
PrevStage::storeStepCand(node1, apa1, tc, node2, contentType, config) and
|
||||
exists(DataFlowType contentType |
|
||||
fwdFlow(node1, state, cc, summaryCtx, argAp, ap1, config) and
|
||||
PrevStage::storeStepCand(node1, unbindApa(getApprox(ap1)), tc, node2, contentType, config) and
|
||||
typecheckStore(ap1, contentType)
|
||||
)
|
||||
}
|
||||
@@ -1464,104 +1433,43 @@ private module MkStage<StageSig PrevStage> {
|
||||
)
|
||||
}
|
||||
|
||||
private class ApNonNil instanceof Ap {
|
||||
pragma[nomagic]
|
||||
ApNonNil() { not this instanceof ApNil }
|
||||
|
||||
string toString() { result = "" }
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate fwdFlowRead0(
|
||||
NodeEx node1, FlowState state, Cc cc, ParameterPositionOption summaryCtx, ApOption argAp,
|
||||
ApNonNil ap, Configuration config
|
||||
) {
|
||||
fwdFlow(node1, state, cc, summaryCtx, argAp, ap, config) and
|
||||
PrevStage::readStepCand(node1, _, _, config)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate fwdFlowRead(
|
||||
Ap ap, Content c, NodeEx node1, NodeEx node2, FlowState state, Cc cc,
|
||||
ParameterPositionOption summaryCtx, ApOption argAp, Configuration config
|
||||
) {
|
||||
fwdFlowRead0(node1, state, cc, summaryCtx, argAp, ap, config) and
|
||||
fwdFlow(node1, state, cc, summaryCtx, argAp, ap, config) and
|
||||
PrevStage::readStepCand(node1, c, node2, config) and
|
||||
getHeadContent(ap) = c
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate fwdFlowIn(
|
||||
DataFlowCall call, ParamNodeEx p, FlowState state, Cc outercc, CcCall innercc,
|
||||
ParameterPositionOption summaryCtx, ApOption argAp, Ap ap, ApApprox apa, Configuration config
|
||||
DataFlowCall call, ParamNodeEx p, FlowState state, Cc outercc, Cc innercc,
|
||||
ParameterPositionOption summaryCtx, ApOption argAp, Ap ap, Configuration config
|
||||
) {
|
||||
exists(ArgNodeEx arg, boolean allowsFieldFlow |
|
||||
fwdFlow(arg, state, outercc, summaryCtx, argAp, ap, apa, config) and
|
||||
flowIntoCallApa(call, arg, p, allowsFieldFlow, apa, config) and
|
||||
fwdFlow(arg, state, outercc, summaryCtx, argAp, ap, config) and
|
||||
flowIntoCall(call, arg, p, allowsFieldFlow, config) and
|
||||
innercc = getCallContextCall(call, p.getEnclosingCallable(), outercc) and
|
||||
if allowsFieldFlow = false then ap instanceof ApNil else any()
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate fwdFlowRetFromArg(
|
||||
RetNodeEx ret, FlowState state, CcCall ccc, ParameterPosition summaryCtx, ParamNodeEx p,
|
||||
Ap argAp, ApApprox argApa, Ap ap, ApApprox apa, Configuration config
|
||||
) {
|
||||
exists(DataFlowCallable c, ReturnKindExt kind |
|
||||
fwdFlow(pragma[only_bind_into](ret), state, ccc,
|
||||
TParameterPositionSome(pragma[only_bind_into](summaryCtx)), apSome(argAp), ap, apa, config) and
|
||||
getApprox(argAp) = argApa and
|
||||
c = ret.getEnclosingCallable() and
|
||||
kind = ret.getKind() and
|
||||
p.isParameterOf(c, pragma[only_bind_into](summaryCtx)) and
|
||||
parameterFlowThroughAllowed(p, kind)
|
||||
)
|
||||
}
|
||||
|
||||
pragma[inline]
|
||||
private predicate fwdFlowInMayFlowThrough(
|
||||
DataFlowCall call, Cc cc, CcCall innerCc, ParameterPositionOption summaryCtx, ApOption argAp,
|
||||
ParamNodeEx param, Ap ap, ApApprox apa, Configuration config
|
||||
) {
|
||||
fwdFlowIn(call, pragma[only_bind_into](param), _, cc, innerCc, summaryCtx, argAp, ap,
|
||||
pragma[only_bind_into](apa), pragma[only_bind_into](config)) and
|
||||
PrevStage::parameterMayFlowThrough(param, apa, config)
|
||||
}
|
||||
|
||||
// dedup before joining with `flowThroughOutOfCall`
|
||||
pragma[nomagic]
|
||||
private predicate fwdFlowInMayFlowThroughProj(
|
||||
DataFlowCall call, CcCall innerCc, ApApprox apa, Configuration config
|
||||
) {
|
||||
fwdFlowInMayFlowThrough(call, _, innerCc, _, _, _, _, apa, config)
|
||||
}
|
||||
|
||||
/**
|
||||
* Same as `flowThroughOutOfCall`, but restricted to calls that are reached
|
||||
* in the flow covered by `fwdFlow`, where data might flow through the target
|
||||
* callable and back out at `call`.
|
||||
*/
|
||||
pragma[nomagic]
|
||||
private predicate fwdFlowThroughOutOfCall(
|
||||
DataFlowCall call, CcCall ccc, RetNodeEx ret, NodeEx out, boolean allowsFieldFlow,
|
||||
ApApprox argApa, ApApprox apa, Configuration config
|
||||
) {
|
||||
fwdFlowInMayFlowThroughProj(call, ccc, argApa, config) and
|
||||
flowThroughOutOfCall(call, ccc, ret, out, allowsFieldFlow, argApa, apa, config)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate fwdFlowOutFromArg(
|
||||
DataFlowCall call, NodeEx out, FlowState state, ParameterPosition summaryCtx, Ap argAp, Ap ap,
|
||||
ApApprox apa, Configuration config
|
||||
Configuration config
|
||||
) {
|
||||
exists(RetNodeEx ret, boolean allowsFieldFlow, CcCall ccc, ApApprox argApa |
|
||||
fwdFlowRetFromArg(pragma[only_bind_into](ret), state, pragma[only_bind_into](ccc),
|
||||
summaryCtx, _, argAp, pragma[only_bind_into](argApa), ap, pragma[only_bind_into](apa),
|
||||
exists(
|
||||
DataFlowCallable c, RetNodeEx ret, ReturnKindExt kind, boolean allowsFieldFlow, CcCall ccc
|
||||
|
|
||||
fwdFlow(pragma[only_bind_into](ret), state, pragma[only_bind_into](ccc),
|
||||
TParameterPositionSome(pragma[only_bind_into](summaryCtx)), apSome(argAp), ap, config) and
|
||||
flowThroughOutOfCall(call, pragma[only_bind_into](c), ccc, ret, kind, out, allowsFieldFlow,
|
||||
config) and
|
||||
fwdFlowThroughOutOfCall(call, ccc, ret, out, allowsFieldFlow, argApa, apa, config) and
|
||||
(if allowsFieldFlow = false then ap instanceof ApNil else any())
|
||||
(if allowsFieldFlow = false then ap instanceof ApNil else any()) and
|
||||
parameterFlowThroughAllowed(c, pragma[only_bind_into](summaryCtx), kind)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1575,7 +1483,8 @@ private module MkStage<StageSig PrevStage> {
|
||||
ParameterPosition pos, Ap ap, Configuration config
|
||||
) {
|
||||
exists(ParamNodeEx param |
|
||||
fwdFlowInMayFlowThrough(call, cc, _, summaryCtx, argAp, param, ap, _, config) and
|
||||
fwdFlowIn(call, param, _, cc, _, summaryCtx, argAp, ap, config) and
|
||||
PrevStage::parameterMayFlowThrough(param, unbindApa(getApprox(ap)), config) and
|
||||
pos = param.getPosition()
|
||||
)
|
||||
}
|
||||
@@ -1596,55 +1505,41 @@ private module MkStage<StageSig PrevStage> {
|
||||
fwdFlowConsCand(ap1, c, ap2, config)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate returnFlowsThrough0(
|
||||
RetNodeEx ret, ReturnKindExt kind, FlowState state, CcCall ccc, DataFlowCallable c,
|
||||
ParameterPosition ppos, Ap argAp, Ap ap, Configuration config
|
||||
) {
|
||||
exists(boolean allowsFieldFlow |
|
||||
fwdFlow(ret, state, ccc, TParameterPositionSome(ppos), apSome(argAp), ap, config) and
|
||||
flowThroughOutOfCall(_, c, _, pragma[only_bind_into](ret), kind, _, allowsFieldFlow,
|
||||
pragma[only_bind_into](config)) and
|
||||
if allowsFieldFlow = false then ap instanceof ApNil else any()
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate returnFlowsThrough(
|
||||
RetNodeEx ret, ReturnKindExt kind, FlowState state, CcCall ccc, ParamNodeEx p, Ap argAp,
|
||||
Ap ap, Configuration config
|
||||
) {
|
||||
exists(boolean allowsFieldFlow, ApApprox argApa, ApApprox apa |
|
||||
fwdFlowRetFromArg(pragma[only_bind_into](ret), state, pragma[only_bind_into](ccc), _, p,
|
||||
argAp, pragma[only_bind_into](argApa), ap, pragma[only_bind_into](apa), config) and
|
||||
kind = ret.getKind() and
|
||||
fwdFlowThroughOutOfCall(_, ccc, ret, _, allowsFieldFlow, argApa, apa, config) and
|
||||
(if allowsFieldFlow = false then ap instanceof ApNil else any())
|
||||
exists(DataFlowCallable c, ParameterPosition ppos |
|
||||
returnFlowsThrough0(ret, kind, state, ccc, c, ppos, argAp, ap, config) and
|
||||
p.isParameterOf(c, ppos) and
|
||||
parameterFlowThroughAllowed(p, kind)
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate flowThroughIntoCall(
|
||||
DataFlowCall call, ArgNodeEx arg, ParamNodeEx p, boolean allowsFieldFlow, Ap argAp,
|
||||
Configuration config
|
||||
DataFlowCall call, ArgNodeEx arg, ParamNodeEx p, boolean allowsFieldFlow, Configuration config
|
||||
) {
|
||||
exists(ApApprox argApa |
|
||||
flowIntoCallApa(call, pragma[only_bind_into](arg), pragma[only_bind_into](p),
|
||||
allowsFieldFlow, argApa, pragma[only_bind_into](config)) and
|
||||
fwdFlow(arg, _, _, _, _, pragma[only_bind_into](argAp), argApa,
|
||||
exists(Ap argAp |
|
||||
flowIntoCall(call, pragma[only_bind_into](arg), pragma[only_bind_into](p), allowsFieldFlow,
|
||||
pragma[only_bind_into](config)) and
|
||||
fwdFlow(arg, _, _, _, _, pragma[only_bind_into](argAp), pragma[only_bind_into](config)) and
|
||||
returnFlowsThrough(_, _, _, _, p, pragma[only_bind_into](argAp), _,
|
||||
pragma[only_bind_into](config)) and
|
||||
if allowsFieldFlow = false then argAp instanceof ApNil else any()
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate flowIntoCallAp(
|
||||
DataFlowCall call, ArgNodeEx arg, ParamNodeEx p, boolean allowsFieldFlow, Ap ap,
|
||||
Configuration config
|
||||
) {
|
||||
exists(ApApprox apa |
|
||||
flowIntoCallApa(call, arg, p, allowsFieldFlow, apa, config) and
|
||||
fwdFlow(arg, _, _, _, _, ap, apa, config)
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate flowOutOfCallAp(
|
||||
DataFlowCall call, RetNodeEx ret, ReturnKindExt kind, NodeEx out, boolean allowsFieldFlow,
|
||||
Ap ap, Configuration config
|
||||
) {
|
||||
exists(ApApprox apa |
|
||||
flowOutOfCallApa(call, ret, kind, out, allowsFieldFlow, apa, config) and
|
||||
fwdFlow(ret, _, _, _, _, ap, apa, config)
|
||||
pragma[only_bind_into](config))
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1733,7 +1628,7 @@ private module MkStage<StageSig PrevStage> {
|
||||
// flow into a callable
|
||||
exists(ParamNodeEx p, boolean allowsFieldFlow |
|
||||
revFlow(p, state, TReturnCtxNone(), returnAp, ap, config) and
|
||||
flowIntoCallAp(_, node, p, allowsFieldFlow, ap, config) and
|
||||
flowIntoCall(_, node, p, allowsFieldFlow, config) and
|
||||
(if allowsFieldFlow = false then ap instanceof ApNil else any()) and
|
||||
returnCtx = TReturnCtxNone()
|
||||
)
|
||||
@@ -1787,41 +1682,22 @@ private module MkStage<StageSig PrevStage> {
|
||||
) {
|
||||
exists(NodeEx out, boolean allowsFieldFlow |
|
||||
revFlow(out, state, returnCtx, returnAp, ap, config) and
|
||||
flowOutOfCallAp(call, ret, kind, out, allowsFieldFlow, ap, config) and
|
||||
flowOutOfCall(call, ret, kind, out, allowsFieldFlow, config) and
|
||||
if allowsFieldFlow = false then ap instanceof ApNil else any()
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Same as `flowThroughIntoCall`, but restricted to calls that are reached
|
||||
* in the flow covered by `revFlow`, where data might flow through the target
|
||||
* callable and back out at `call`.
|
||||
*/
|
||||
pragma[nomagic]
|
||||
private predicate revFlowThroughIntoCall(
|
||||
DataFlowCall call, ArgNodeEx arg, ParamNodeEx p, boolean allowsFieldFlow, Ap argAp,
|
||||
Configuration config
|
||||
) {
|
||||
flowThroughIntoCall(call, arg, p, allowsFieldFlow, argAp, config) and
|
||||
revFlowIsReturned(call, _, _, _, _, config)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate revFlowParamToReturn(
|
||||
ParamNodeEx p, FlowState state, ReturnKindExt kind, Ap returnAp, Ap ap, Configuration config
|
||||
) {
|
||||
revFlow(p, state, TReturnCtxMaybeFlowThrough(kind), apSome(returnAp), ap, config) and
|
||||
parameterFlowThroughAllowed(p, kind)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate revFlowInToReturn(
|
||||
DataFlowCall call, ArgNodeEx arg, FlowState state, ReturnKindExt kind, Ap returnAp, Ap ap,
|
||||
Configuration config
|
||||
) {
|
||||
exists(ParamNodeEx p, boolean allowsFieldFlow |
|
||||
revFlowParamToReturn(p, state, kind, returnAp, ap, config) and
|
||||
revFlowThroughIntoCall(call, arg, p, allowsFieldFlow, ap, config)
|
||||
revFlow(pragma[only_bind_into](p), state,
|
||||
TReturnCtxMaybeFlowThrough(pragma[only_bind_into](kind)), apSome(returnAp), ap, config) and
|
||||
flowThroughIntoCall(call, arg, p, allowsFieldFlow, config) and
|
||||
(if allowsFieldFlow = false then ap instanceof ApNil else any()) and
|
||||
parameterFlowThroughAllowed(p, kind)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1874,11 +1750,6 @@ private module MkStage<StageSig PrevStage> {
|
||||
pragma[nomagic]
|
||||
predicate revFlow(NodeEx node, Configuration config) { revFlow(node, _, _, _, _, config) }
|
||||
|
||||
pragma[nomagic]
|
||||
predicate revFlowAp(NodeEx node, Ap ap, Configuration config) {
|
||||
revFlow(node, _, _, _, ap, config)
|
||||
}
|
||||
|
||||
// use an alias as a workaround for bad functionality-induced joins
|
||||
pragma[nomagic]
|
||||
additional predicate revFlowAlias(NodeEx node, Configuration config) {
|
||||
@@ -1915,9 +1786,9 @@ private module MkStage<StageSig PrevStage> {
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate parameterFlowsThroughRev(
|
||||
ParamNodeEx p, Ap ap, ReturnKindExt kind, Ap returnAp, Configuration config
|
||||
ParamNodeEx p, Ap ap, ReturnKindExt kind, Configuration config
|
||||
) {
|
||||
revFlow(p, _, TReturnCtxMaybeFlowThrough(kind), apSome(returnAp), ap, config) and
|
||||
revFlow(p, _, TReturnCtxMaybeFlowThrough(kind), apSome(_), ap, config) and
|
||||
parameterFlowThroughAllowed(p, kind)
|
||||
}
|
||||
|
||||
@@ -1925,36 +1796,27 @@ private module MkStage<StageSig PrevStage> {
|
||||
predicate parameterMayFlowThrough(ParamNodeEx p, Ap ap, Configuration config) {
|
||||
exists(RetNodeEx ret, ReturnKindExt kind |
|
||||
returnFlowsThrough(ret, kind, _, _, p, ap, _, config) and
|
||||
parameterFlowsThroughRev(p, ap, kind, _, config)
|
||||
parameterFlowsThroughRev(p, ap, kind, config)
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
predicate returnMayFlowThrough(
|
||||
RetNodeEx ret, Ap argAp, Ap ap, ReturnKindExt kind, Configuration config
|
||||
) {
|
||||
exists(ParamNodeEx p |
|
||||
returnFlowsThrough(ret, kind, _, _, p, argAp, ap, config) and
|
||||
parameterFlowsThroughRev(p, argAp, kind, ap, config)
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
predicate revFlowInToReturnIsReturned(
|
||||
DataFlowCall call, ArgNodeEx arg, FlowState state, ReturnCtx returnCtx, ApOption returnAp,
|
||||
Ap ap, Configuration config
|
||||
) {
|
||||
exists(ReturnKindExt returnKind0, Ap returnAp0 |
|
||||
revFlowInToReturn(call, arg, state, returnKind0, returnAp0, ap, config) and
|
||||
revFlowIsReturned(call, returnCtx, returnAp, returnKind0, returnAp0, config)
|
||||
predicate returnMayFlowThrough(RetNodeEx ret, ReturnKindExt kind, Configuration config) {
|
||||
exists(ParamNodeEx p, Ap ap |
|
||||
returnFlowsThrough(ret, kind, _, _, p, ap, _, config) and
|
||||
parameterFlowsThroughRev(p, ap, kind, config)
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
predicate callMayFlowThroughRev(DataFlowCall call, Configuration config) {
|
||||
exists(ArgNodeEx arg, FlowState state, ReturnCtx returnCtx, ApOption returnAp, Ap ap |
|
||||
exists(
|
||||
ReturnKindExt returnKind0, Ap returnAp0, ArgNodeEx arg, FlowState state,
|
||||
ReturnCtx returnCtx, ApOption returnAp, Ap ap
|
||||
|
|
||||
revFlow(arg, state, returnCtx, returnAp, ap, config) and
|
||||
revFlowInToReturnIsReturned(call, arg, state, returnCtx, returnAp, ap, config)
|
||||
revFlowInToReturn(call, arg, state, returnKind0, returnAp0, ap, config) and
|
||||
revFlowIsReturned(call, returnCtx, returnAp, returnKind0, returnAp0, config)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -2317,16 +2179,16 @@ private module LocalFlowBigStep {
|
||||
pragma[nomagic]
|
||||
predicate localFlowBigStep(
|
||||
NodeEx node1, FlowState state1, NodeEx node2, FlowState state2, boolean preservesValue,
|
||||
DataFlowType t, Configuration config, LocalCallContext callContext
|
||||
AccessPathFrontNil apf, Configuration config, LocalCallContext callContext
|
||||
) {
|
||||
localFlowStepPlus(node1, state1, node2, preservesValue, t, config, callContext) and
|
||||
localFlowStepPlus(node1, state1, node2, preservesValue, apf.getType(), config, callContext) and
|
||||
localFlowExit(node2, state1, config) and
|
||||
state1 = state2
|
||||
or
|
||||
additionalLocalFlowStepNodeCand2(node1, state1, node2, state2, config) and
|
||||
state1 != state2 and
|
||||
preservesValue = false and
|
||||
t = node2.getDataFlowType() and
|
||||
apf = TFrontNil(node2.getDataFlowType()) and
|
||||
callContext.relevantFor(node1.getEnclosingCallable()) and
|
||||
not exists(DataFlowCall call | call = callContext.(LocalCallContextSpecificCall).getCall() |
|
||||
isUnreachableInCallCached(node1.asNode(), call) or
|
||||
@@ -2340,87 +2202,11 @@ private import LocalFlowBigStep
|
||||
private module Stage3Param implements MkStage<Stage2>::StageParam {
|
||||
private module PrevStage = Stage2;
|
||||
|
||||
class Ap = ApproxAccessPathFront;
|
||||
|
||||
class ApNil = ApproxAccessPathFrontNil;
|
||||
|
||||
PrevStage::Ap getApprox(Ap ap) { result = ap.toBoolNonEmpty() }
|
||||
|
||||
ApNil getApNil(NodeEx node) {
|
||||
PrevStage::revFlow(node, _) and result = TApproxFrontNil(node.getDataFlowType())
|
||||
}
|
||||
|
||||
bindingset[tc, tail]
|
||||
Ap apCons(TypedContent tc, Ap tail) { result.getAHead() = tc and exists(tail) }
|
||||
|
||||
pragma[noinline]
|
||||
Content getHeadContent(Ap ap) { result = ap.getAHead().getContent() }
|
||||
|
||||
class ApOption = ApproxAccessPathFrontOption;
|
||||
|
||||
ApOption apNone() { result = TApproxAccessPathFrontNone() }
|
||||
|
||||
ApOption apSome(Ap ap) { result = TApproxAccessPathFrontSome(ap) }
|
||||
|
||||
import BooleanCallContext
|
||||
|
||||
predicate localStep(
|
||||
NodeEx node1, FlowState state1, NodeEx node2, FlowState state2, boolean preservesValue,
|
||||
ApproxAccessPathFrontNil ap, Configuration config, LocalCc lcc
|
||||
) {
|
||||
localFlowBigStep(node1, state1, node2, state2, preservesValue, ap.getType(), config, _) and
|
||||
exists(lcc)
|
||||
}
|
||||
|
||||
predicate flowOutOfCall = flowOutOfCallNodeCand2/6;
|
||||
|
||||
predicate flowIntoCall = flowIntoCallNodeCand2/5;
|
||||
|
||||
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.getAHead().getContent()
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate castingNodeEx(NodeEx node) { node.asNode() instanceof CastingNode }
|
||||
|
||||
bindingset[node, state, ap, config]
|
||||
predicate filter(NodeEx node, FlowState state, Ap ap, Configuration config) {
|
||||
exists(state) and
|
||||
exists(config) and
|
||||
(if castingNodeEx(node) then compatibleTypes(node.getDataFlowType(), ap.getType()) else any()) and
|
||||
(
|
||||
notExpectsContent(node)
|
||||
or
|
||||
expectsContentCand(node, ap, config)
|
||||
)
|
||||
}
|
||||
|
||||
bindingset[ap, contentType]
|
||||
predicate typecheckStore(Ap ap, DataFlowType contentType) {
|
||||
// We need to typecheck stores here, since reverse flow through a getter
|
||||
// might have a different type here compared to inside the getter.
|
||||
compatibleTypes(ap.getType(), contentType)
|
||||
}
|
||||
}
|
||||
|
||||
private module Stage3 implements StageSig {
|
||||
import MkStage<Stage2>::Stage<Stage3Param>
|
||||
}
|
||||
|
||||
private module Stage4Param implements MkStage<Stage3>::StageParam {
|
||||
private module PrevStage = Stage3;
|
||||
|
||||
class Ap = AccessPathFront;
|
||||
|
||||
class ApNil = AccessPathFrontNil;
|
||||
|
||||
PrevStage::Ap getApprox(Ap ap) { result = ap.toApprox() }
|
||||
PrevStage::Ap getApprox(Ap ap) { result = ap.toBoolNonEmpty() }
|
||||
|
||||
ApNil getApNil(NodeEx node) {
|
||||
PrevStage::revFlow(node, _) and result = TFrontNil(node.getDataFlowType())
|
||||
@@ -2440,42 +2226,16 @@ private module Stage4Param implements MkStage<Stage3>::StageParam {
|
||||
|
||||
import BooleanCallContext
|
||||
|
||||
pragma[nomagic]
|
||||
predicate localStep(
|
||||
NodeEx node1, FlowState state1, NodeEx node2, FlowState state2, boolean preservesValue,
|
||||
ApNil ap, Configuration config, LocalCc lcc
|
||||
) {
|
||||
localFlowBigStep(node1, state1, node2, state2, preservesValue, ap.getType(), config, _) and
|
||||
PrevStage::revFlow(node1, pragma[only_bind_into](state1), _, pragma[only_bind_into](config)) and
|
||||
PrevStage::revFlowAlias(node2, pragma[only_bind_into](state2), _, pragma[only_bind_into](config)) and
|
||||
exists(lcc)
|
||||
localFlowBigStep(node1, state1, node2, state2, preservesValue, ap, config, _) and exists(lcc)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
predicate flowOutOfCall(
|
||||
DataFlowCall call, RetNodeEx node1, ReturnKindExt kind, NodeEx node2, boolean allowsFieldFlow,
|
||||
Configuration config
|
||||
) {
|
||||
exists(FlowState state |
|
||||
flowOutOfCallNodeCand2(call, node1, kind, node2, allowsFieldFlow, config) and
|
||||
PrevStage::revFlow(node2, pragma[only_bind_into](state), _, pragma[only_bind_into](config)) and
|
||||
PrevStage::revFlowAlias(node1, pragma[only_bind_into](state), _,
|
||||
pragma[only_bind_into](config))
|
||||
)
|
||||
}
|
||||
predicate flowOutOfCall = flowOutOfCallNodeCand2/6;
|
||||
|
||||
pragma[nomagic]
|
||||
predicate flowIntoCall(
|
||||
DataFlowCall call, ArgNodeEx node1, ParamNodeEx node2, boolean allowsFieldFlow,
|
||||
Configuration config
|
||||
) {
|
||||
exists(FlowState state |
|
||||
flowIntoCallNodeCand2(call, node1, node2, allowsFieldFlow, config) and
|
||||
PrevStage::revFlow(node2, pragma[only_bind_into](state), _, pragma[only_bind_into](config)) and
|
||||
PrevStage::revFlowAlias(node1, pragma[only_bind_into](state), _,
|
||||
pragma[only_bind_into](config))
|
||||
)
|
||||
}
|
||||
predicate flowIntoCall = flowIntoCallNodeCand2/5;
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate clearSet(NodeEx node, ContentSet c, Configuration config) {
|
||||
@@ -2531,8 +2291,8 @@ private module Stage4Param implements MkStage<Stage3>::StageParam {
|
||||
}
|
||||
}
|
||||
|
||||
private module Stage4 implements StageSig {
|
||||
import MkStage<Stage3>::Stage<Stage4Param>
|
||||
private module Stage3 implements StageSig {
|
||||
import MkStage<Stage2>::Stage<Stage3Param>
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -2543,8 +2303,8 @@ private predicate flowCandSummaryCtx(
|
||||
NodeEx node, FlowState state, AccessPathFront argApf, Configuration config
|
||||
) {
|
||||
exists(AccessPathFront apf |
|
||||
Stage4::revFlow(node, state, TReturnCtxMaybeFlowThrough(_), _, apf, config) and
|
||||
Stage4::fwdFlow(node, state, any(Stage4::CcCall ccc), _, TAccessPathFrontSome(argApf), apf,
|
||||
Stage3::revFlow(node, state, TReturnCtxMaybeFlowThrough(_), _, apf, config) and
|
||||
Stage3::fwdFlow(node, state, any(Stage3::CcCall ccc), _, TAccessPathFrontSome(argApf), apf,
|
||||
config)
|
||||
)
|
||||
}
|
||||
@@ -2555,10 +2315,10 @@ private predicate flowCandSummaryCtx(
|
||||
*/
|
||||
private predicate expensiveLen2unfolding(TypedContent tc, Configuration config) {
|
||||
exists(int tails, int nodes, int apLimit, int tupleLimit |
|
||||
tails = strictcount(AccessPathFront apf | Stage4::consCand(tc, apf, config)) and
|
||||
tails = strictcount(AccessPathFront apf | Stage3::consCand(tc, apf, config)) and
|
||||
nodes =
|
||||
strictcount(NodeEx n, FlowState state |
|
||||
Stage4::revFlow(n, state, any(AccessPathFrontHead apf | apf.getHead() = tc), config)
|
||||
Stage3::revFlow(n, state, any(AccessPathFrontHead apf | apf.getHead() = tc), config)
|
||||
or
|
||||
flowCandSummaryCtx(n, state, any(AccessPathFrontHead apf | apf.getHead() = tc), config)
|
||||
) and
|
||||
@@ -2572,11 +2332,11 @@ private predicate expensiveLen2unfolding(TypedContent tc, Configuration config)
|
||||
private newtype TAccessPathApprox =
|
||||
TNil(DataFlowType t) or
|
||||
TConsNil(TypedContent tc, DataFlowType t) {
|
||||
Stage4::consCand(tc, TFrontNil(t), _) and
|
||||
Stage3::consCand(tc, TFrontNil(t), _) and
|
||||
not expensiveLen2unfolding(tc, _)
|
||||
} or
|
||||
TConsCons(TypedContent tc1, TypedContent tc2, int len) {
|
||||
Stage4::consCand(tc1, TFrontHead(tc2), _) and
|
||||
Stage3::consCand(tc1, TFrontHead(tc2), _) and
|
||||
len in [2 .. accessPathLimit()] and
|
||||
not expensiveLen2unfolding(tc1, _)
|
||||
} or
|
||||
@@ -2706,7 +2466,7 @@ private class AccessPathApproxCons1 extends AccessPathApproxCons, TCons1 {
|
||||
override AccessPathApprox pop(TypedContent head) {
|
||||
head = tc and
|
||||
(
|
||||
exists(TypedContent tc2 | Stage4::consCand(tc, TFrontHead(tc2), _) |
|
||||
exists(TypedContent tc2 | Stage3::consCand(tc, TFrontHead(tc2), _) |
|
||||
result = TConsCons(tc2, _, len - 1)
|
||||
or
|
||||
len = 2 and
|
||||
@@ -2717,7 +2477,7 @@ private class AccessPathApproxCons1 extends AccessPathApproxCons, TCons1 {
|
||||
or
|
||||
exists(DataFlowType t |
|
||||
len = 1 and
|
||||
Stage4::consCand(tc, TFrontNil(t), _) and
|
||||
Stage3::consCand(tc, TFrontNil(t), _) and
|
||||
result = TNil(t)
|
||||
)
|
||||
)
|
||||
@@ -2742,14 +2502,13 @@ private class AccessPathApproxOption extends TAccessPathApproxOption {
|
||||
}
|
||||
}
|
||||
|
||||
private module Stage5Param implements MkStage<Stage4>::StageParam {
|
||||
private module PrevStage = Stage4;
|
||||
private module Stage4Param implements MkStage<Stage3>::StageParam {
|
||||
private module PrevStage = Stage3;
|
||||
|
||||
class Ap = AccessPathApprox;
|
||||
|
||||
class ApNil = AccessPathApproxNil;
|
||||
|
||||
pragma[nomagic]
|
||||
PrevStage::Ap getApprox(Ap ap) { result = ap.getFront() }
|
||||
|
||||
ApNil getApNil(NodeEx node) {
|
||||
@@ -2775,9 +2534,7 @@ private module Stage5Param implements MkStage<Stage4>::StageParam {
|
||||
NodeEx node1, FlowState state1, NodeEx node2, FlowState state2, boolean preservesValue,
|
||||
ApNil ap, Configuration config, LocalCc lcc
|
||||
) {
|
||||
localFlowBigStep(node1, state1, node2, state2, preservesValue, ap.getType(), config, lcc) and
|
||||
PrevStage::revFlow(node1, pragma[only_bind_into](state1), _, pragma[only_bind_into](config)) and
|
||||
PrevStage::revFlowAlias(node2, pragma[only_bind_into](state2), _, pragma[only_bind_into](config))
|
||||
localFlowBigStep(node1, state1, node2, state2, preservesValue, ap.getFront(), config, lcc)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
@@ -2814,7 +2571,7 @@ private module Stage5Param implements MkStage<Stage4>::StageParam {
|
||||
predicate typecheckStore(Ap ap, DataFlowType contentType) { any() }
|
||||
}
|
||||
|
||||
private module Stage5 = MkStage<Stage4>::Stage<Stage5Param>;
|
||||
private module Stage4 = MkStage<Stage3>::Stage<Stage4Param>;
|
||||
|
||||
bindingset[conf, result]
|
||||
private Configuration unbindConf(Configuration conf) {
|
||||
@@ -2828,8 +2585,8 @@ private predicate nodeMayUseSummary0(
|
||||
) {
|
||||
exists(AccessPathApprox apa0 |
|
||||
c = n.getEnclosingCallable() and
|
||||
Stage5::revFlow(n, state, TReturnCtxMaybeFlowThrough(_), _, apa0, config) and
|
||||
Stage5::fwdFlow(n, state, any(CallContextCall ccc), TParameterPositionSome(pos),
|
||||
Stage4::revFlow(n, state, TReturnCtxMaybeFlowThrough(_), _, apa0, config) and
|
||||
Stage4::fwdFlow(n, state, any(CallContextCall ccc), TParameterPositionSome(pos),
|
||||
TAccessPathApproxSome(apa), apa0, config)
|
||||
)
|
||||
}
|
||||
@@ -2839,7 +2596,7 @@ private predicate nodeMayUseSummary(
|
||||
NodeEx n, FlowState state, AccessPathApprox apa, Configuration config
|
||||
) {
|
||||
exists(DataFlowCallable c, ParameterPosition pos, ParamNodeEx p |
|
||||
Stage5::parameterMayFlowThrough(p, apa, config) and
|
||||
Stage4::parameterMayFlowThrough(p, apa, config) and
|
||||
nodeMayUseSummary0(n, c, pos, state, apa, config) and
|
||||
p.isParameterOf(c, pos)
|
||||
)
|
||||
@@ -2849,8 +2606,8 @@ private newtype TSummaryCtx =
|
||||
TSummaryCtxNone() or
|
||||
TSummaryCtxSome(ParamNodeEx p, FlowState state, AccessPath ap) {
|
||||
exists(Configuration config |
|
||||
Stage5::parameterMayFlowThrough(p, ap.getApprox(), config) and
|
||||
Stage5::revFlow(p, state, _, config)
|
||||
Stage4::parameterMayFlowThrough(p, ap.getApprox(), config) and
|
||||
Stage4::revFlow(p, state, _, config)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -2899,7 +2656,7 @@ private int count1to2unfold(AccessPathApproxCons1 apa, Configuration config) {
|
||||
len = apa.len() and
|
||||
result =
|
||||
strictcount(AccessPathFront apf |
|
||||
Stage5::consCand(tc, any(AccessPathApprox ap | ap.getFront() = apf and ap.len() = len - 1),
|
||||
Stage4::consCand(tc, any(AccessPathApprox ap | ap.getFront() = apf and ap.len() = len - 1),
|
||||
config)
|
||||
)
|
||||
)
|
||||
@@ -2908,7 +2665,7 @@ private int count1to2unfold(AccessPathApproxCons1 apa, Configuration config) {
|
||||
private int countNodesUsingAccessPath(AccessPathApprox apa, Configuration config) {
|
||||
result =
|
||||
strictcount(NodeEx n, FlowState state |
|
||||
Stage5::revFlow(n, state, apa, config) or nodeMayUseSummary(n, state, apa, config)
|
||||
Stage4::revFlow(n, state, apa, config) or nodeMayUseSummary(n, state, apa, config)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -2929,7 +2686,7 @@ private predicate expensiveLen1to2unfolding(AccessPathApproxCons1 apa, Configura
|
||||
private AccessPathApprox getATail(AccessPathApprox apa, Configuration config) {
|
||||
exists(TypedContent head |
|
||||
apa.pop(head) = result and
|
||||
Stage5::consCand(head, result, config)
|
||||
Stage4::consCand(head, result, config)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -3011,7 +2768,7 @@ private newtype TPathNode =
|
||||
NodeEx node, FlowState state, CallContext cc, SummaryCtx sc, AccessPath ap, Configuration config
|
||||
) {
|
||||
// A PathNode is introduced by a source ...
|
||||
Stage5::revFlow(node, state, config) and
|
||||
Stage4::revFlow(node, state, config) and
|
||||
sourceNode(node, state, config) and
|
||||
(
|
||||
if hasSourceCallCtx(config)
|
||||
@@ -3025,7 +2782,7 @@ private newtype TPathNode =
|
||||
exists(PathNodeMid mid |
|
||||
pathStep(mid, node, state, cc, sc, ap) and
|
||||
pragma[only_bind_into](config) = mid.getConfiguration() and
|
||||
Stage5::revFlow(node, state, ap.getApprox(), pragma[only_bind_into](config))
|
||||
Stage4::revFlow(node, state, ap.getApprox(), pragma[only_bind_into](config))
|
||||
)
|
||||
} or
|
||||
TPathNodeSink(NodeEx node, FlowState state, Configuration config) {
|
||||
@@ -3167,7 +2924,7 @@ private class AccessPathCons2 extends AccessPath, TAccessPathCons2 {
|
||||
override TypedContent getHead() { result = head1 }
|
||||
|
||||
override AccessPath getTail() {
|
||||
Stage5::consCand(head1, result.getApprox(), _) and
|
||||
Stage4::consCand(head1, result.getApprox(), _) and
|
||||
result.getHead() = head2 and
|
||||
result.length() = len - 1
|
||||
}
|
||||
@@ -3198,7 +2955,7 @@ private class AccessPathCons1 extends AccessPath, TAccessPathCons1 {
|
||||
override TypedContent getHead() { result = head }
|
||||
|
||||
override AccessPath getTail() {
|
||||
Stage5::consCand(head, result.getApprox(), _) and result.length() = len - 1
|
||||
Stage4::consCand(head, result.getApprox(), _) and result.length() = len - 1
|
||||
}
|
||||
|
||||
override AccessPathFrontHead getFront() { result = TFrontHead(head) }
|
||||
@@ -3590,8 +3347,7 @@ private predicate pathStep(
|
||||
AccessPath ap0, NodeEx midnode, FlowState state0, Configuration conf, LocalCallContext localCC
|
||||
|
|
||||
pathNode(mid, midnode, state0, cc, sc, ap0, conf, localCC) and
|
||||
localFlowBigStep(midnode, state0, node, state, false, ap.(AccessPathNil).getType(), conf,
|
||||
localCC) and
|
||||
localFlowBigStep(midnode, state0, node, state, false, ap.getFront(), conf, localCC) and
|
||||
ap0 instanceof AccessPathNil
|
||||
)
|
||||
or
|
||||
@@ -3633,7 +3389,7 @@ private predicate pathReadStep(
|
||||
) {
|
||||
ap0 = mid.getAp() and
|
||||
tc = ap0.getHead() and
|
||||
Stage5::readStepCand(mid.getNodeEx(), tc.getContent(), node, mid.getConfiguration()) and
|
||||
Stage4::readStepCand(mid.getNodeEx(), tc.getContent(), node, mid.getConfiguration()) and
|
||||
state = mid.getState() and
|
||||
cc = mid.getCallContext()
|
||||
}
|
||||
@@ -3643,7 +3399,7 @@ private predicate pathStoreStep(
|
||||
PathNodeMid mid, NodeEx node, FlowState state, AccessPath ap0, TypedContent tc, CallContext cc
|
||||
) {
|
||||
ap0 = mid.getAp() and
|
||||
Stage5::storeStepCand(mid.getNodeEx(), _, tc, node, _, mid.getConfiguration()) and
|
||||
Stage4::storeStepCand(mid.getNodeEx(), _, tc, node, _, mid.getConfiguration()) and
|
||||
state = mid.getState() and
|
||||
cc = mid.getCallContext()
|
||||
}
|
||||
@@ -3680,7 +3436,7 @@ private NodeEx getAnOutNodeFlow(
|
||||
ReturnKindExt kind, DataFlowCall call, AccessPathApprox apa, Configuration config
|
||||
) {
|
||||
result.asNode() = kind.getAnOutNode(call) and
|
||||
Stage5::revFlow(result, _, apa, config)
|
||||
Stage4::revFlow(result, _, apa, config)
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -3716,7 +3472,7 @@ private predicate parameterCand(
|
||||
DataFlowCallable callable, ParameterPosition pos, AccessPathApprox apa, Configuration config
|
||||
) {
|
||||
exists(ParamNodeEx p |
|
||||
Stage5::revFlow(p, _, apa, config) and
|
||||
Stage4::revFlow(p, _, apa, config) and
|
||||
p.isParameterOf(callable, pos)
|
||||
)
|
||||
}
|
||||
@@ -3995,17 +3751,9 @@ predicate stageStats(
|
||||
n = 45 and
|
||||
Stage4::stats(false, nodes, fields, conscand, states, tuples, config)
|
||||
or
|
||||
stage = "5 Fwd" and
|
||||
n = 50 and
|
||||
Stage5::stats(true, nodes, fields, conscand, states, tuples, config)
|
||||
stage = "5 Fwd" and n = 50 and finalStats(true, nodes, fields, conscand, states, tuples)
|
||||
or
|
||||
stage = "5 Rev" and
|
||||
n = 55 and
|
||||
Stage5::stats(false, nodes, fields, conscand, states, tuples, config)
|
||||
or
|
||||
stage = "6 Fwd" and n = 60 and finalStats(true, nodes, fields, conscand, states, tuples)
|
||||
or
|
||||
stage = "6 Rev" and n = 65 and finalStats(false, nodes, fields, conscand, states, tuples)
|
||||
stage = "5 Rev" and n = 55 and finalStats(false, nodes, fields, conscand, states, tuples)
|
||||
}
|
||||
|
||||
private module FlowExploration {
|
||||
|
||||
@@ -926,46 +926,18 @@ private module Cached {
|
||||
TReturnCtxNoFlowThrough() or
|
||||
TReturnCtxMaybeFlowThrough(ReturnKindExt kind)
|
||||
|
||||
cached
|
||||
newtype TTypedContentApprox =
|
||||
MkTypedContentApprox(ContentApprox c, DataFlowType t) {
|
||||
exists(Content cont |
|
||||
c = getContentApprox(cont) and
|
||||
store(_, cont, _, _, t)
|
||||
)
|
||||
}
|
||||
|
||||
cached
|
||||
newtype TTypedContent = MkTypedContent(Content c, DataFlowType t) { store(_, c, _, _, t) }
|
||||
|
||||
cached
|
||||
TypedContent getATypedContent(TypedContentApprox c) {
|
||||
exists(ContentApprox cls, DataFlowType t, Content cont |
|
||||
c = MkTypedContentApprox(cls, pragma[only_bind_into](t)) and
|
||||
result = MkTypedContent(cont, pragma[only_bind_into](t)) and
|
||||
cls = getContentApprox(cont)
|
||||
)
|
||||
}
|
||||
|
||||
cached
|
||||
newtype TAccessPathFront =
|
||||
TFrontNil(DataFlowType t) or
|
||||
TFrontHead(TypedContent tc)
|
||||
|
||||
cached
|
||||
newtype TApproxAccessPathFront =
|
||||
TApproxFrontNil(DataFlowType t) or
|
||||
TApproxFrontHead(TypedContentApprox tc)
|
||||
|
||||
cached
|
||||
newtype TAccessPathFrontOption =
|
||||
TAccessPathFrontNone() or
|
||||
TAccessPathFrontSome(AccessPathFront apf)
|
||||
|
||||
cached
|
||||
newtype TApproxAccessPathFrontOption =
|
||||
TApproxAccessPathFrontNone() or
|
||||
TApproxAccessPathFrontSome(ApproxAccessPathFront apf)
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1381,75 +1353,6 @@ class ReturnCtx extends TReturnCtx {
|
||||
}
|
||||
}
|
||||
|
||||
/** An approximated `Content` tagged with the type of a containing object. */
|
||||
class TypedContentApprox extends MkTypedContentApprox {
|
||||
private ContentApprox c;
|
||||
private DataFlowType t;
|
||||
|
||||
TypedContentApprox() { this = MkTypedContentApprox(c, t) }
|
||||
|
||||
/** Gets a typed content approximated by this value. */
|
||||
TypedContent getATypedContent() { result = getATypedContent(this) }
|
||||
|
||||
/** Gets the container type. */
|
||||
DataFlowType getContainerType() { result = t }
|
||||
|
||||
/** Gets a textual representation of this approximated content. */
|
||||
string toString() { result = c.toString() }
|
||||
}
|
||||
|
||||
/**
|
||||
* The front of an approximated access path. This is either a head or a nil.
|
||||
*/
|
||||
abstract class ApproxAccessPathFront extends TApproxAccessPathFront {
|
||||
abstract string toString();
|
||||
|
||||
abstract DataFlowType getType();
|
||||
|
||||
abstract boolean toBoolNonEmpty();
|
||||
|
||||
pragma[nomagic]
|
||||
TypedContent getAHead() {
|
||||
exists(TypedContentApprox cont |
|
||||
this = TApproxFrontHead(cont) and
|
||||
result = cont.getATypedContent()
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
class ApproxAccessPathFrontNil extends ApproxAccessPathFront, TApproxFrontNil {
|
||||
private DataFlowType t;
|
||||
|
||||
ApproxAccessPathFrontNil() { this = TApproxFrontNil(t) }
|
||||
|
||||
override string toString() { result = ppReprType(t) }
|
||||
|
||||
override DataFlowType getType() { result = t }
|
||||
|
||||
override boolean toBoolNonEmpty() { result = false }
|
||||
}
|
||||
|
||||
class ApproxAccessPathFrontHead extends ApproxAccessPathFront, TApproxFrontHead {
|
||||
private TypedContentApprox tc;
|
||||
|
||||
ApproxAccessPathFrontHead() { this = TApproxFrontHead(tc) }
|
||||
|
||||
override string toString() { result = tc.toString() }
|
||||
|
||||
override DataFlowType getType() { result = tc.getContainerType() }
|
||||
|
||||
override boolean toBoolNonEmpty() { result = true }
|
||||
}
|
||||
|
||||
/** An optional approximated access path front. */
|
||||
class ApproxAccessPathFrontOption extends TApproxAccessPathFrontOption {
|
||||
string toString() {
|
||||
this = TApproxAccessPathFrontNone() and result = "<none>"
|
||||
or
|
||||
this = TApproxAccessPathFrontSome(any(ApproxAccessPathFront apf | result = apf.toString()))
|
||||
}
|
||||
}
|
||||
|
||||
/** A `Content` tagged with the type of a containing object. */
|
||||
class TypedContent extends MkTypedContent {
|
||||
private Content c;
|
||||
@@ -1482,7 +1385,7 @@ abstract class AccessPathFront extends TAccessPathFront {
|
||||
|
||||
abstract DataFlowType getType();
|
||||
|
||||
abstract ApproxAccessPathFront toApprox();
|
||||
abstract boolean toBoolNonEmpty();
|
||||
|
||||
TypedContent getHead() { this = TFrontHead(result) }
|
||||
}
|
||||
@@ -1496,7 +1399,7 @@ class AccessPathFrontNil extends AccessPathFront, TFrontNil {
|
||||
|
||||
override DataFlowType getType() { result = t }
|
||||
|
||||
override ApproxAccessPathFront toApprox() { result = TApproxFrontNil(t) }
|
||||
override boolean toBoolNonEmpty() { result = false }
|
||||
}
|
||||
|
||||
class AccessPathFrontHead extends AccessPathFront, TFrontHead {
|
||||
@@ -1508,7 +1411,7 @@ class AccessPathFrontHead extends AccessPathFront, TFrontHead {
|
||||
|
||||
override DataFlowType getType() { result = tc.getContainerType() }
|
||||
|
||||
override ApproxAccessPathFront toApprox() { result.getAHead() = tc }
|
||||
override boolean toBoolNonEmpty() { result = true }
|
||||
}
|
||||
|
||||
/** An optional access path front. */
|
||||
|
||||
@@ -260,9 +260,4 @@ module Consistency {
|
||||
not exists(unique(ParameterPosition pos0 | isParameterNode(p, c, pos0))) and
|
||||
msg = "Parameter node with multiple positions."
|
||||
}
|
||||
|
||||
query predicate uniqueContentApprox(Content c, string msg) {
|
||||
not exists(unique(ContentApprox approx | approx = getContentApprox(c))) and
|
||||
msg = "Non-unique content approximation."
|
||||
}
|
||||
}
|
||||
|
||||
@@ -621,6 +621,23 @@ private predicate parameterFlowThroughAllowed(ParamNodeEx p, ReturnKindExt kind)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if flow from a parameter at position `pos` inside `c` to a return node of
|
||||
* kind `kind` is allowed.
|
||||
*
|
||||
* We don't expect a parameter to return stored in itself, unless
|
||||
* explicitly allowed
|
||||
*/
|
||||
bindingset[c, pos, kind]
|
||||
private predicate parameterFlowThroughAllowed(
|
||||
DataFlowCallable c, ParameterPosition pos, ReturnKindExt kind
|
||||
) {
|
||||
exists(ParamNodeEx p |
|
||||
p.isParameterOf(c, pos) and
|
||||
parameterFlowThroughAllowed(p, kind)
|
||||
)
|
||||
}
|
||||
|
||||
private module Stage1 implements StageSig {
|
||||
class Ap = Unit;
|
||||
|
||||
@@ -963,12 +980,6 @@ private module Stage1 implements StageSig {
|
||||
pragma[nomagic]
|
||||
predicate revFlow(NodeEx node, Configuration config) { revFlow(node, _, config) }
|
||||
|
||||
pragma[nomagic]
|
||||
predicate revFlowAp(NodeEx node, Ap ap, Configuration config) {
|
||||
revFlow(node, config) and
|
||||
exists(ap)
|
||||
}
|
||||
|
||||
bindingset[node, state, config]
|
||||
predicate revFlow(NodeEx node, FlowState state, Ap ap, Configuration config) {
|
||||
revFlow(node, _, pragma[only_bind_into](config)) and
|
||||
@@ -1011,13 +1022,9 @@ private module Stage1 implements StageSig {
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
predicate returnMayFlowThrough(
|
||||
RetNodeEx ret, Ap argAp, Ap ap, ReturnKindExt kind, Configuration config
|
||||
) {
|
||||
predicate returnMayFlowThrough(RetNodeEx ret, ReturnKindExt kind, Configuration config) {
|
||||
throughFlowNodeCand(ret, config) and
|
||||
kind = ret.getKind() and
|
||||
exists(argAp) and
|
||||
exists(ap)
|
||||
kind = ret.getKind()
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
@@ -1182,8 +1189,6 @@ private signature module StageSig {
|
||||
|
||||
predicate revFlow(NodeEx node, Configuration config);
|
||||
|
||||
predicate revFlowAp(NodeEx node, Ap ap, Configuration config);
|
||||
|
||||
bindingset[node, state, config]
|
||||
predicate revFlow(NodeEx node, FlowState state, Ap ap, Configuration config);
|
||||
|
||||
@@ -1191,9 +1196,7 @@ private signature module StageSig {
|
||||
|
||||
predicate parameterMayFlowThrough(ParamNodeEx p, Ap ap, Configuration config);
|
||||
|
||||
predicate returnMayFlowThrough(
|
||||
RetNodeEx ret, Ap argAp, Ap ap, ReturnKindExt kind, Configuration config
|
||||
);
|
||||
predicate returnMayFlowThrough(RetNodeEx ret, ReturnKindExt kind, Configuration config);
|
||||
|
||||
predicate storeStepCand(
|
||||
NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, DataFlowType contentType,
|
||||
@@ -1278,43 +1281,21 @@ private module MkStage<StageSig PrevStage> {
|
||||
import Param
|
||||
|
||||
/* Begin: Stage logic. */
|
||||
// use an alias as a workaround for bad functionality-induced joins
|
||||
pragma[nomagic]
|
||||
private predicate revFlowApAlias(NodeEx node, ApApprox apa, Configuration config) {
|
||||
PrevStage::revFlowAp(node, apa, config)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate flowIntoCallApa(
|
||||
DataFlowCall call, ArgNodeEx arg, ParamNodeEx p, boolean allowsFieldFlow, ApApprox apa,
|
||||
Configuration config
|
||||
) {
|
||||
flowIntoCall(call, arg, p, allowsFieldFlow, config) and
|
||||
PrevStage::revFlowAp(p, pragma[only_bind_into](apa), pragma[only_bind_into](config)) and
|
||||
revFlowApAlias(arg, pragma[only_bind_into](apa), pragma[only_bind_into](config))
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate flowOutOfCallApa(
|
||||
DataFlowCall call, RetNodeEx ret, ReturnKindExt kind, NodeEx out, boolean allowsFieldFlow,
|
||||
ApApprox apa, Configuration config
|
||||
) {
|
||||
flowOutOfCall(call, ret, kind, out, allowsFieldFlow, config) and
|
||||
PrevStage::revFlowAp(out, pragma[only_bind_into](apa), pragma[only_bind_into](config)) and
|
||||
revFlowApAlias(ret, pragma[only_bind_into](apa), pragma[only_bind_into](config))
|
||||
bindingset[result, apa]
|
||||
private ApApprox unbindApa(ApApprox apa) {
|
||||
pragma[only_bind_out](apa) = pragma[only_bind_out](result)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate flowThroughOutOfCall(
|
||||
DataFlowCall call, CcCall ccc, RetNodeEx ret, NodeEx out, boolean allowsFieldFlow,
|
||||
ApApprox argApa, ApApprox apa, Configuration config
|
||||
DataFlowCall call, DataFlowCallable c, CcCall ccc, RetNodeEx ret, ReturnKindExt kind,
|
||||
NodeEx out, boolean allowsFieldFlow, Configuration config
|
||||
) {
|
||||
exists(ReturnKindExt kind |
|
||||
flowOutOfCallApa(call, ret, kind, out, allowsFieldFlow, apa, pragma[only_bind_into](config)) and
|
||||
PrevStage::callMayFlowThroughRev(call, pragma[only_bind_into](config)) and
|
||||
PrevStage::returnMayFlowThrough(ret, argApa, apa, kind, pragma[only_bind_into](config)) and
|
||||
matchesCall(ccc, call)
|
||||
)
|
||||
flowOutOfCall(call, ret, kind, out, allowsFieldFlow, pragma[only_bind_into](config)) and
|
||||
PrevStage::callMayFlowThroughRev(call, pragma[only_bind_into](config)) and
|
||||
PrevStage::returnMayFlowThrough(ret, kind, pragma[only_bind_into](config)) and
|
||||
matchesCall(ccc, call) and
|
||||
c = ret.getEnclosingCallable()
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1326,50 +1307,39 @@ private module MkStage<StageSig PrevStage> {
|
||||
* corresponding parameter position and access path of that argument, respectively.
|
||||
*/
|
||||
pragma[nomagic]
|
||||
additional predicate fwdFlow(
|
||||
NodeEx node, FlowState state, Cc cc, ParameterPositionOption summaryCtx, ApOption argAp,
|
||||
Ap ap, ApApprox apa, Configuration config
|
||||
) {
|
||||
fwdFlow0(node, state, cc, summaryCtx, argAp, ap, apa, config) and
|
||||
PrevStage::revFlow(node, state, apa, config) and
|
||||
filter(node, state, ap, config)
|
||||
}
|
||||
|
||||
pragma[inline]
|
||||
additional predicate fwdFlow(
|
||||
NodeEx node, FlowState state, Cc cc, ParameterPositionOption summaryCtx, ApOption argAp,
|
||||
Ap ap, Configuration config
|
||||
) {
|
||||
fwdFlow(node, state, cc, summaryCtx, argAp, ap, _, config)
|
||||
fwdFlow0(node, state, cc, summaryCtx, argAp, ap, config) and
|
||||
PrevStage::revFlow(node, state, unbindApa(getApprox(ap)), config) and
|
||||
filter(node, state, ap, config)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate fwdFlow0(
|
||||
NodeEx node, FlowState state, Cc cc, ParameterPositionOption summaryCtx, ApOption argAp,
|
||||
Ap ap, ApApprox apa, Configuration config
|
||||
Ap ap, Configuration config
|
||||
) {
|
||||
sourceNode(node, state, config) and
|
||||
(if hasSourceCallCtx(config) then cc = ccSomeCall() else cc = ccNone()) and
|
||||
argAp = apNone() and
|
||||
summaryCtx = TParameterPositionNone() and
|
||||
ap = getApNil(node) and
|
||||
apa = getApprox(ap)
|
||||
ap = getApNil(node)
|
||||
or
|
||||
exists(NodeEx mid, FlowState state0, Ap ap0, ApApprox apa0, LocalCc localCc |
|
||||
fwdFlow(mid, state0, cc, summaryCtx, argAp, ap0, apa0, config) and
|
||||
exists(NodeEx mid, FlowState state0, Ap ap0, LocalCc localCc |
|
||||
fwdFlow(mid, state0, cc, summaryCtx, argAp, ap0, config) and
|
||||
localCc = getLocalCc(mid, cc)
|
||||
|
|
||||
localStep(mid, state0, node, state, true, _, config, localCc) and
|
||||
ap = ap0 and
|
||||
apa = apa0
|
||||
ap = ap0
|
||||
or
|
||||
localStep(mid, state0, node, state, false, ap, config, localCc) and
|
||||
ap0 instanceof ApNil and
|
||||
apa = getApprox(ap)
|
||||
ap0 instanceof ApNil
|
||||
)
|
||||
or
|
||||
exists(NodeEx mid |
|
||||
fwdFlow(mid, pragma[only_bind_into](state), _, _, _, ap, apa, pragma[only_bind_into](config)) and
|
||||
fwdFlow(mid, pragma[only_bind_into](state), _, _, _, ap, pragma[only_bind_into](config)) and
|
||||
jumpStep(mid, node, config) and
|
||||
cc = ccNone() and
|
||||
summaryCtx = TParameterPositionNone() and
|
||||
@@ -1382,8 +1352,7 @@ private module MkStage<StageSig PrevStage> {
|
||||
cc = ccNone() and
|
||||
summaryCtx = TParameterPositionNone() and
|
||||
argAp = apNone() and
|
||||
ap = getApNil(node) and
|
||||
apa = getApprox(ap)
|
||||
ap = getApNil(node)
|
||||
)
|
||||
or
|
||||
exists(NodeEx mid, FlowState state0, ApNil nil |
|
||||
@@ -1392,32 +1361,32 @@ private module MkStage<StageSig PrevStage> {
|
||||
cc = ccNone() and
|
||||
summaryCtx = TParameterPositionNone() and
|
||||
argAp = apNone() and
|
||||
ap = getApNil(node) and
|
||||
apa = getApprox(ap)
|
||||
ap = getApNil(node)
|
||||
)
|
||||
or
|
||||
// store
|
||||
exists(TypedContent tc, Ap ap0 |
|
||||
fwdFlowStore(_, ap0, tc, node, state, cc, summaryCtx, argAp, config) and
|
||||
ap = apCons(tc, ap0) and
|
||||
apa = getApprox(ap)
|
||||
ap = apCons(tc, ap0)
|
||||
)
|
||||
or
|
||||
// read
|
||||
exists(Ap ap0, Content c |
|
||||
fwdFlowRead(ap0, c, _, node, state, cc, summaryCtx, argAp, config) and
|
||||
fwdFlowConsCand(ap0, c, ap, config) and
|
||||
apa = getApprox(ap)
|
||||
fwdFlowConsCand(ap0, c, ap, config)
|
||||
)
|
||||
or
|
||||
// flow into a callable
|
||||
fwdFlowIn(_, node, state, _, cc, _, _, ap, apa, config) and
|
||||
if PrevStage::parameterMayFlowThrough(node, apa, config)
|
||||
then (
|
||||
summaryCtx = TParameterPositionSome(node.(ParamNodeEx).getPosition()) and
|
||||
argAp = apSome(ap)
|
||||
) else (
|
||||
summaryCtx = TParameterPositionNone() and argAp = apNone()
|
||||
exists(ApApprox apa |
|
||||
fwdFlowIn(_, node, state, _, cc, _, _, ap, config) and
|
||||
apa = getApprox(ap) and
|
||||
if PrevStage::parameterMayFlowThrough(node, apa, config)
|
||||
then (
|
||||
summaryCtx = TParameterPositionSome(node.(ParamNodeEx).getPosition()) and
|
||||
argAp = apSome(ap)
|
||||
) else (
|
||||
summaryCtx = TParameterPositionNone() and argAp = apNone()
|
||||
)
|
||||
)
|
||||
or
|
||||
// flow out of a callable
|
||||
@@ -1425,8 +1394,8 @@ private module MkStage<StageSig PrevStage> {
|
||||
DataFlowCall call, RetNodeEx ret, boolean allowsFieldFlow, CcNoCall innercc,
|
||||
DataFlowCallable inner
|
||||
|
|
||||
fwdFlow(ret, state, innercc, summaryCtx, argAp, ap, apa, config) and
|
||||
flowOutOfCallApa(call, ret, _, node, allowsFieldFlow, apa, config) and
|
||||
fwdFlow(ret, state, innercc, summaryCtx, argAp, ap, config) and
|
||||
flowOutOfCall(call, ret, _, node, allowsFieldFlow, config) and
|
||||
inner = ret.getEnclosingCallable() and
|
||||
cc = getCallContextReturn(inner, call, innercc) and
|
||||
if allowsFieldFlow = false then ap instanceof ApNil else any()
|
||||
@@ -1434,7 +1403,7 @@ private module MkStage<StageSig PrevStage> {
|
||||
or
|
||||
// flow through a callable
|
||||
exists(DataFlowCall call, ParameterPosition summaryCtx0, Ap argAp0 |
|
||||
fwdFlowOutFromArg(call, node, state, summaryCtx0, argAp0, ap, apa, config) and
|
||||
fwdFlowOutFromArg(call, node, state, summaryCtx0, argAp0, ap, config) and
|
||||
fwdFlowIsEntered(call, cc, summaryCtx, argAp, summaryCtx0, argAp0, config)
|
||||
)
|
||||
}
|
||||
@@ -1444,9 +1413,9 @@ private module MkStage<StageSig PrevStage> {
|
||||
NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, FlowState state, Cc cc,
|
||||
ParameterPositionOption summaryCtx, ApOption argAp, Configuration config
|
||||
) {
|
||||
exists(DataFlowType contentType, ApApprox apa1 |
|
||||
fwdFlow(node1, state, cc, summaryCtx, argAp, ap1, apa1, config) and
|
||||
PrevStage::storeStepCand(node1, apa1, tc, node2, contentType, config) and
|
||||
exists(DataFlowType contentType |
|
||||
fwdFlow(node1, state, cc, summaryCtx, argAp, ap1, config) and
|
||||
PrevStage::storeStepCand(node1, unbindApa(getApprox(ap1)), tc, node2, contentType, config) and
|
||||
typecheckStore(ap1, contentType)
|
||||
)
|
||||
}
|
||||
@@ -1464,104 +1433,43 @@ private module MkStage<StageSig PrevStage> {
|
||||
)
|
||||
}
|
||||
|
||||
private class ApNonNil instanceof Ap {
|
||||
pragma[nomagic]
|
||||
ApNonNil() { not this instanceof ApNil }
|
||||
|
||||
string toString() { result = "" }
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate fwdFlowRead0(
|
||||
NodeEx node1, FlowState state, Cc cc, ParameterPositionOption summaryCtx, ApOption argAp,
|
||||
ApNonNil ap, Configuration config
|
||||
) {
|
||||
fwdFlow(node1, state, cc, summaryCtx, argAp, ap, config) and
|
||||
PrevStage::readStepCand(node1, _, _, config)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate fwdFlowRead(
|
||||
Ap ap, Content c, NodeEx node1, NodeEx node2, FlowState state, Cc cc,
|
||||
ParameterPositionOption summaryCtx, ApOption argAp, Configuration config
|
||||
) {
|
||||
fwdFlowRead0(node1, state, cc, summaryCtx, argAp, ap, config) and
|
||||
fwdFlow(node1, state, cc, summaryCtx, argAp, ap, config) and
|
||||
PrevStage::readStepCand(node1, c, node2, config) and
|
||||
getHeadContent(ap) = c
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate fwdFlowIn(
|
||||
DataFlowCall call, ParamNodeEx p, FlowState state, Cc outercc, CcCall innercc,
|
||||
ParameterPositionOption summaryCtx, ApOption argAp, Ap ap, ApApprox apa, Configuration config
|
||||
DataFlowCall call, ParamNodeEx p, FlowState state, Cc outercc, Cc innercc,
|
||||
ParameterPositionOption summaryCtx, ApOption argAp, Ap ap, Configuration config
|
||||
) {
|
||||
exists(ArgNodeEx arg, boolean allowsFieldFlow |
|
||||
fwdFlow(arg, state, outercc, summaryCtx, argAp, ap, apa, config) and
|
||||
flowIntoCallApa(call, arg, p, allowsFieldFlow, apa, config) and
|
||||
fwdFlow(arg, state, outercc, summaryCtx, argAp, ap, config) and
|
||||
flowIntoCall(call, arg, p, allowsFieldFlow, config) and
|
||||
innercc = getCallContextCall(call, p.getEnclosingCallable(), outercc) and
|
||||
if allowsFieldFlow = false then ap instanceof ApNil else any()
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate fwdFlowRetFromArg(
|
||||
RetNodeEx ret, FlowState state, CcCall ccc, ParameterPosition summaryCtx, ParamNodeEx p,
|
||||
Ap argAp, ApApprox argApa, Ap ap, ApApprox apa, Configuration config
|
||||
) {
|
||||
exists(DataFlowCallable c, ReturnKindExt kind |
|
||||
fwdFlow(pragma[only_bind_into](ret), state, ccc,
|
||||
TParameterPositionSome(pragma[only_bind_into](summaryCtx)), apSome(argAp), ap, apa, config) and
|
||||
getApprox(argAp) = argApa and
|
||||
c = ret.getEnclosingCallable() and
|
||||
kind = ret.getKind() and
|
||||
p.isParameterOf(c, pragma[only_bind_into](summaryCtx)) and
|
||||
parameterFlowThroughAllowed(p, kind)
|
||||
)
|
||||
}
|
||||
|
||||
pragma[inline]
|
||||
private predicate fwdFlowInMayFlowThrough(
|
||||
DataFlowCall call, Cc cc, CcCall innerCc, ParameterPositionOption summaryCtx, ApOption argAp,
|
||||
ParamNodeEx param, Ap ap, ApApprox apa, Configuration config
|
||||
) {
|
||||
fwdFlowIn(call, pragma[only_bind_into](param), _, cc, innerCc, summaryCtx, argAp, ap,
|
||||
pragma[only_bind_into](apa), pragma[only_bind_into](config)) and
|
||||
PrevStage::parameterMayFlowThrough(param, apa, config)
|
||||
}
|
||||
|
||||
// dedup before joining with `flowThroughOutOfCall`
|
||||
pragma[nomagic]
|
||||
private predicate fwdFlowInMayFlowThroughProj(
|
||||
DataFlowCall call, CcCall innerCc, ApApprox apa, Configuration config
|
||||
) {
|
||||
fwdFlowInMayFlowThrough(call, _, innerCc, _, _, _, _, apa, config)
|
||||
}
|
||||
|
||||
/**
|
||||
* Same as `flowThroughOutOfCall`, but restricted to calls that are reached
|
||||
* in the flow covered by `fwdFlow`, where data might flow through the target
|
||||
* callable and back out at `call`.
|
||||
*/
|
||||
pragma[nomagic]
|
||||
private predicate fwdFlowThroughOutOfCall(
|
||||
DataFlowCall call, CcCall ccc, RetNodeEx ret, NodeEx out, boolean allowsFieldFlow,
|
||||
ApApprox argApa, ApApprox apa, Configuration config
|
||||
) {
|
||||
fwdFlowInMayFlowThroughProj(call, ccc, argApa, config) and
|
||||
flowThroughOutOfCall(call, ccc, ret, out, allowsFieldFlow, argApa, apa, config)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate fwdFlowOutFromArg(
|
||||
DataFlowCall call, NodeEx out, FlowState state, ParameterPosition summaryCtx, Ap argAp, Ap ap,
|
||||
ApApprox apa, Configuration config
|
||||
Configuration config
|
||||
) {
|
||||
exists(RetNodeEx ret, boolean allowsFieldFlow, CcCall ccc, ApApprox argApa |
|
||||
fwdFlowRetFromArg(pragma[only_bind_into](ret), state, pragma[only_bind_into](ccc),
|
||||
summaryCtx, _, argAp, pragma[only_bind_into](argApa), ap, pragma[only_bind_into](apa),
|
||||
exists(
|
||||
DataFlowCallable c, RetNodeEx ret, ReturnKindExt kind, boolean allowsFieldFlow, CcCall ccc
|
||||
|
|
||||
fwdFlow(pragma[only_bind_into](ret), state, pragma[only_bind_into](ccc),
|
||||
TParameterPositionSome(pragma[only_bind_into](summaryCtx)), apSome(argAp), ap, config) and
|
||||
flowThroughOutOfCall(call, pragma[only_bind_into](c), ccc, ret, kind, out, allowsFieldFlow,
|
||||
config) and
|
||||
fwdFlowThroughOutOfCall(call, ccc, ret, out, allowsFieldFlow, argApa, apa, config) and
|
||||
(if allowsFieldFlow = false then ap instanceof ApNil else any())
|
||||
(if allowsFieldFlow = false then ap instanceof ApNil else any()) and
|
||||
parameterFlowThroughAllowed(c, pragma[only_bind_into](summaryCtx), kind)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1575,7 +1483,8 @@ private module MkStage<StageSig PrevStage> {
|
||||
ParameterPosition pos, Ap ap, Configuration config
|
||||
) {
|
||||
exists(ParamNodeEx param |
|
||||
fwdFlowInMayFlowThrough(call, cc, _, summaryCtx, argAp, param, ap, _, config) and
|
||||
fwdFlowIn(call, param, _, cc, _, summaryCtx, argAp, ap, config) and
|
||||
PrevStage::parameterMayFlowThrough(param, unbindApa(getApprox(ap)), config) and
|
||||
pos = param.getPosition()
|
||||
)
|
||||
}
|
||||
@@ -1596,55 +1505,41 @@ private module MkStage<StageSig PrevStage> {
|
||||
fwdFlowConsCand(ap1, c, ap2, config)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate returnFlowsThrough0(
|
||||
RetNodeEx ret, ReturnKindExt kind, FlowState state, CcCall ccc, DataFlowCallable c,
|
||||
ParameterPosition ppos, Ap argAp, Ap ap, Configuration config
|
||||
) {
|
||||
exists(boolean allowsFieldFlow |
|
||||
fwdFlow(ret, state, ccc, TParameterPositionSome(ppos), apSome(argAp), ap, config) and
|
||||
flowThroughOutOfCall(_, c, _, pragma[only_bind_into](ret), kind, _, allowsFieldFlow,
|
||||
pragma[only_bind_into](config)) and
|
||||
if allowsFieldFlow = false then ap instanceof ApNil else any()
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate returnFlowsThrough(
|
||||
RetNodeEx ret, ReturnKindExt kind, FlowState state, CcCall ccc, ParamNodeEx p, Ap argAp,
|
||||
Ap ap, Configuration config
|
||||
) {
|
||||
exists(boolean allowsFieldFlow, ApApprox argApa, ApApprox apa |
|
||||
fwdFlowRetFromArg(pragma[only_bind_into](ret), state, pragma[only_bind_into](ccc), _, p,
|
||||
argAp, pragma[only_bind_into](argApa), ap, pragma[only_bind_into](apa), config) and
|
||||
kind = ret.getKind() and
|
||||
fwdFlowThroughOutOfCall(_, ccc, ret, _, allowsFieldFlow, argApa, apa, config) and
|
||||
(if allowsFieldFlow = false then ap instanceof ApNil else any())
|
||||
exists(DataFlowCallable c, ParameterPosition ppos |
|
||||
returnFlowsThrough0(ret, kind, state, ccc, c, ppos, argAp, ap, config) and
|
||||
p.isParameterOf(c, ppos) and
|
||||
parameterFlowThroughAllowed(p, kind)
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate flowThroughIntoCall(
|
||||
DataFlowCall call, ArgNodeEx arg, ParamNodeEx p, boolean allowsFieldFlow, Ap argAp,
|
||||
Configuration config
|
||||
DataFlowCall call, ArgNodeEx arg, ParamNodeEx p, boolean allowsFieldFlow, Configuration config
|
||||
) {
|
||||
exists(ApApprox argApa |
|
||||
flowIntoCallApa(call, pragma[only_bind_into](arg), pragma[only_bind_into](p),
|
||||
allowsFieldFlow, argApa, pragma[only_bind_into](config)) and
|
||||
fwdFlow(arg, _, _, _, _, pragma[only_bind_into](argAp), argApa,
|
||||
exists(Ap argAp |
|
||||
flowIntoCall(call, pragma[only_bind_into](arg), pragma[only_bind_into](p), allowsFieldFlow,
|
||||
pragma[only_bind_into](config)) and
|
||||
fwdFlow(arg, _, _, _, _, pragma[only_bind_into](argAp), pragma[only_bind_into](config)) and
|
||||
returnFlowsThrough(_, _, _, _, p, pragma[only_bind_into](argAp), _,
|
||||
pragma[only_bind_into](config)) and
|
||||
if allowsFieldFlow = false then argAp instanceof ApNil else any()
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate flowIntoCallAp(
|
||||
DataFlowCall call, ArgNodeEx arg, ParamNodeEx p, boolean allowsFieldFlow, Ap ap,
|
||||
Configuration config
|
||||
) {
|
||||
exists(ApApprox apa |
|
||||
flowIntoCallApa(call, arg, p, allowsFieldFlow, apa, config) and
|
||||
fwdFlow(arg, _, _, _, _, ap, apa, config)
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate flowOutOfCallAp(
|
||||
DataFlowCall call, RetNodeEx ret, ReturnKindExt kind, NodeEx out, boolean allowsFieldFlow,
|
||||
Ap ap, Configuration config
|
||||
) {
|
||||
exists(ApApprox apa |
|
||||
flowOutOfCallApa(call, ret, kind, out, allowsFieldFlow, apa, config) and
|
||||
fwdFlow(ret, _, _, _, _, ap, apa, config)
|
||||
pragma[only_bind_into](config))
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1733,7 +1628,7 @@ private module MkStage<StageSig PrevStage> {
|
||||
// flow into a callable
|
||||
exists(ParamNodeEx p, boolean allowsFieldFlow |
|
||||
revFlow(p, state, TReturnCtxNone(), returnAp, ap, config) and
|
||||
flowIntoCallAp(_, node, p, allowsFieldFlow, ap, config) and
|
||||
flowIntoCall(_, node, p, allowsFieldFlow, config) and
|
||||
(if allowsFieldFlow = false then ap instanceof ApNil else any()) and
|
||||
returnCtx = TReturnCtxNone()
|
||||
)
|
||||
@@ -1787,41 +1682,22 @@ private module MkStage<StageSig PrevStage> {
|
||||
) {
|
||||
exists(NodeEx out, boolean allowsFieldFlow |
|
||||
revFlow(out, state, returnCtx, returnAp, ap, config) and
|
||||
flowOutOfCallAp(call, ret, kind, out, allowsFieldFlow, ap, config) and
|
||||
flowOutOfCall(call, ret, kind, out, allowsFieldFlow, config) and
|
||||
if allowsFieldFlow = false then ap instanceof ApNil else any()
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Same as `flowThroughIntoCall`, but restricted to calls that are reached
|
||||
* in the flow covered by `revFlow`, where data might flow through the target
|
||||
* callable and back out at `call`.
|
||||
*/
|
||||
pragma[nomagic]
|
||||
private predicate revFlowThroughIntoCall(
|
||||
DataFlowCall call, ArgNodeEx arg, ParamNodeEx p, boolean allowsFieldFlow, Ap argAp,
|
||||
Configuration config
|
||||
) {
|
||||
flowThroughIntoCall(call, arg, p, allowsFieldFlow, argAp, config) and
|
||||
revFlowIsReturned(call, _, _, _, _, config)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate revFlowParamToReturn(
|
||||
ParamNodeEx p, FlowState state, ReturnKindExt kind, Ap returnAp, Ap ap, Configuration config
|
||||
) {
|
||||
revFlow(p, state, TReturnCtxMaybeFlowThrough(kind), apSome(returnAp), ap, config) and
|
||||
parameterFlowThroughAllowed(p, kind)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate revFlowInToReturn(
|
||||
DataFlowCall call, ArgNodeEx arg, FlowState state, ReturnKindExt kind, Ap returnAp, Ap ap,
|
||||
Configuration config
|
||||
) {
|
||||
exists(ParamNodeEx p, boolean allowsFieldFlow |
|
||||
revFlowParamToReturn(p, state, kind, returnAp, ap, config) and
|
||||
revFlowThroughIntoCall(call, arg, p, allowsFieldFlow, ap, config)
|
||||
revFlow(pragma[only_bind_into](p), state,
|
||||
TReturnCtxMaybeFlowThrough(pragma[only_bind_into](kind)), apSome(returnAp), ap, config) and
|
||||
flowThroughIntoCall(call, arg, p, allowsFieldFlow, config) and
|
||||
(if allowsFieldFlow = false then ap instanceof ApNil else any()) and
|
||||
parameterFlowThroughAllowed(p, kind)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1874,11 +1750,6 @@ private module MkStage<StageSig PrevStage> {
|
||||
pragma[nomagic]
|
||||
predicate revFlow(NodeEx node, Configuration config) { revFlow(node, _, _, _, _, config) }
|
||||
|
||||
pragma[nomagic]
|
||||
predicate revFlowAp(NodeEx node, Ap ap, Configuration config) {
|
||||
revFlow(node, _, _, _, ap, config)
|
||||
}
|
||||
|
||||
// use an alias as a workaround for bad functionality-induced joins
|
||||
pragma[nomagic]
|
||||
additional predicate revFlowAlias(NodeEx node, Configuration config) {
|
||||
@@ -1915,9 +1786,9 @@ private module MkStage<StageSig PrevStage> {
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate parameterFlowsThroughRev(
|
||||
ParamNodeEx p, Ap ap, ReturnKindExt kind, Ap returnAp, Configuration config
|
||||
ParamNodeEx p, Ap ap, ReturnKindExt kind, Configuration config
|
||||
) {
|
||||
revFlow(p, _, TReturnCtxMaybeFlowThrough(kind), apSome(returnAp), ap, config) and
|
||||
revFlow(p, _, TReturnCtxMaybeFlowThrough(kind), apSome(_), ap, config) and
|
||||
parameterFlowThroughAllowed(p, kind)
|
||||
}
|
||||
|
||||
@@ -1925,36 +1796,27 @@ private module MkStage<StageSig PrevStage> {
|
||||
predicate parameterMayFlowThrough(ParamNodeEx p, Ap ap, Configuration config) {
|
||||
exists(RetNodeEx ret, ReturnKindExt kind |
|
||||
returnFlowsThrough(ret, kind, _, _, p, ap, _, config) and
|
||||
parameterFlowsThroughRev(p, ap, kind, _, config)
|
||||
parameterFlowsThroughRev(p, ap, kind, config)
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
predicate returnMayFlowThrough(
|
||||
RetNodeEx ret, Ap argAp, Ap ap, ReturnKindExt kind, Configuration config
|
||||
) {
|
||||
exists(ParamNodeEx p |
|
||||
returnFlowsThrough(ret, kind, _, _, p, argAp, ap, config) and
|
||||
parameterFlowsThroughRev(p, argAp, kind, ap, config)
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
predicate revFlowInToReturnIsReturned(
|
||||
DataFlowCall call, ArgNodeEx arg, FlowState state, ReturnCtx returnCtx, ApOption returnAp,
|
||||
Ap ap, Configuration config
|
||||
) {
|
||||
exists(ReturnKindExt returnKind0, Ap returnAp0 |
|
||||
revFlowInToReturn(call, arg, state, returnKind0, returnAp0, ap, config) and
|
||||
revFlowIsReturned(call, returnCtx, returnAp, returnKind0, returnAp0, config)
|
||||
predicate returnMayFlowThrough(RetNodeEx ret, ReturnKindExt kind, Configuration config) {
|
||||
exists(ParamNodeEx p, Ap ap |
|
||||
returnFlowsThrough(ret, kind, _, _, p, ap, _, config) and
|
||||
parameterFlowsThroughRev(p, ap, kind, config)
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
predicate callMayFlowThroughRev(DataFlowCall call, Configuration config) {
|
||||
exists(ArgNodeEx arg, FlowState state, ReturnCtx returnCtx, ApOption returnAp, Ap ap |
|
||||
exists(
|
||||
ReturnKindExt returnKind0, Ap returnAp0, ArgNodeEx arg, FlowState state,
|
||||
ReturnCtx returnCtx, ApOption returnAp, Ap ap
|
||||
|
|
||||
revFlow(arg, state, returnCtx, returnAp, ap, config) and
|
||||
revFlowInToReturnIsReturned(call, arg, state, returnCtx, returnAp, ap, config)
|
||||
revFlowInToReturn(call, arg, state, returnKind0, returnAp0, ap, config) and
|
||||
revFlowIsReturned(call, returnCtx, returnAp, returnKind0, returnAp0, config)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -2317,16 +2179,16 @@ private module LocalFlowBigStep {
|
||||
pragma[nomagic]
|
||||
predicate localFlowBigStep(
|
||||
NodeEx node1, FlowState state1, NodeEx node2, FlowState state2, boolean preservesValue,
|
||||
DataFlowType t, Configuration config, LocalCallContext callContext
|
||||
AccessPathFrontNil apf, Configuration config, LocalCallContext callContext
|
||||
) {
|
||||
localFlowStepPlus(node1, state1, node2, preservesValue, t, config, callContext) and
|
||||
localFlowStepPlus(node1, state1, node2, preservesValue, apf.getType(), config, callContext) and
|
||||
localFlowExit(node2, state1, config) and
|
||||
state1 = state2
|
||||
or
|
||||
additionalLocalFlowStepNodeCand2(node1, state1, node2, state2, config) and
|
||||
state1 != state2 and
|
||||
preservesValue = false and
|
||||
t = node2.getDataFlowType() and
|
||||
apf = TFrontNil(node2.getDataFlowType()) and
|
||||
callContext.relevantFor(node1.getEnclosingCallable()) and
|
||||
not exists(DataFlowCall call | call = callContext.(LocalCallContextSpecificCall).getCall() |
|
||||
isUnreachableInCallCached(node1.asNode(), call) or
|
||||
@@ -2340,87 +2202,11 @@ private import LocalFlowBigStep
|
||||
private module Stage3Param implements MkStage<Stage2>::StageParam {
|
||||
private module PrevStage = Stage2;
|
||||
|
||||
class Ap = ApproxAccessPathFront;
|
||||
|
||||
class ApNil = ApproxAccessPathFrontNil;
|
||||
|
||||
PrevStage::Ap getApprox(Ap ap) { result = ap.toBoolNonEmpty() }
|
||||
|
||||
ApNil getApNil(NodeEx node) {
|
||||
PrevStage::revFlow(node, _) and result = TApproxFrontNil(node.getDataFlowType())
|
||||
}
|
||||
|
||||
bindingset[tc, tail]
|
||||
Ap apCons(TypedContent tc, Ap tail) { result.getAHead() = tc and exists(tail) }
|
||||
|
||||
pragma[noinline]
|
||||
Content getHeadContent(Ap ap) { result = ap.getAHead().getContent() }
|
||||
|
||||
class ApOption = ApproxAccessPathFrontOption;
|
||||
|
||||
ApOption apNone() { result = TApproxAccessPathFrontNone() }
|
||||
|
||||
ApOption apSome(Ap ap) { result = TApproxAccessPathFrontSome(ap) }
|
||||
|
||||
import BooleanCallContext
|
||||
|
||||
predicate localStep(
|
||||
NodeEx node1, FlowState state1, NodeEx node2, FlowState state2, boolean preservesValue,
|
||||
ApproxAccessPathFrontNil ap, Configuration config, LocalCc lcc
|
||||
) {
|
||||
localFlowBigStep(node1, state1, node2, state2, preservesValue, ap.getType(), config, _) and
|
||||
exists(lcc)
|
||||
}
|
||||
|
||||
predicate flowOutOfCall = flowOutOfCallNodeCand2/6;
|
||||
|
||||
predicate flowIntoCall = flowIntoCallNodeCand2/5;
|
||||
|
||||
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.getAHead().getContent()
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate castingNodeEx(NodeEx node) { node.asNode() instanceof CastingNode }
|
||||
|
||||
bindingset[node, state, ap, config]
|
||||
predicate filter(NodeEx node, FlowState state, Ap ap, Configuration config) {
|
||||
exists(state) and
|
||||
exists(config) and
|
||||
(if castingNodeEx(node) then compatibleTypes(node.getDataFlowType(), ap.getType()) else any()) and
|
||||
(
|
||||
notExpectsContent(node)
|
||||
or
|
||||
expectsContentCand(node, ap, config)
|
||||
)
|
||||
}
|
||||
|
||||
bindingset[ap, contentType]
|
||||
predicate typecheckStore(Ap ap, DataFlowType contentType) {
|
||||
// We need to typecheck stores here, since reverse flow through a getter
|
||||
// might have a different type here compared to inside the getter.
|
||||
compatibleTypes(ap.getType(), contentType)
|
||||
}
|
||||
}
|
||||
|
||||
private module Stage3 implements StageSig {
|
||||
import MkStage<Stage2>::Stage<Stage3Param>
|
||||
}
|
||||
|
||||
private module Stage4Param implements MkStage<Stage3>::StageParam {
|
||||
private module PrevStage = Stage3;
|
||||
|
||||
class Ap = AccessPathFront;
|
||||
|
||||
class ApNil = AccessPathFrontNil;
|
||||
|
||||
PrevStage::Ap getApprox(Ap ap) { result = ap.toApprox() }
|
||||
PrevStage::Ap getApprox(Ap ap) { result = ap.toBoolNonEmpty() }
|
||||
|
||||
ApNil getApNil(NodeEx node) {
|
||||
PrevStage::revFlow(node, _) and result = TFrontNil(node.getDataFlowType())
|
||||
@@ -2440,42 +2226,16 @@ private module Stage4Param implements MkStage<Stage3>::StageParam {
|
||||
|
||||
import BooleanCallContext
|
||||
|
||||
pragma[nomagic]
|
||||
predicate localStep(
|
||||
NodeEx node1, FlowState state1, NodeEx node2, FlowState state2, boolean preservesValue,
|
||||
ApNil ap, Configuration config, LocalCc lcc
|
||||
) {
|
||||
localFlowBigStep(node1, state1, node2, state2, preservesValue, ap.getType(), config, _) and
|
||||
PrevStage::revFlow(node1, pragma[only_bind_into](state1), _, pragma[only_bind_into](config)) and
|
||||
PrevStage::revFlowAlias(node2, pragma[only_bind_into](state2), _, pragma[only_bind_into](config)) and
|
||||
exists(lcc)
|
||||
localFlowBigStep(node1, state1, node2, state2, preservesValue, ap, config, _) and exists(lcc)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
predicate flowOutOfCall(
|
||||
DataFlowCall call, RetNodeEx node1, ReturnKindExt kind, NodeEx node2, boolean allowsFieldFlow,
|
||||
Configuration config
|
||||
) {
|
||||
exists(FlowState state |
|
||||
flowOutOfCallNodeCand2(call, node1, kind, node2, allowsFieldFlow, config) and
|
||||
PrevStage::revFlow(node2, pragma[only_bind_into](state), _, pragma[only_bind_into](config)) and
|
||||
PrevStage::revFlowAlias(node1, pragma[only_bind_into](state), _,
|
||||
pragma[only_bind_into](config))
|
||||
)
|
||||
}
|
||||
predicate flowOutOfCall = flowOutOfCallNodeCand2/6;
|
||||
|
||||
pragma[nomagic]
|
||||
predicate flowIntoCall(
|
||||
DataFlowCall call, ArgNodeEx node1, ParamNodeEx node2, boolean allowsFieldFlow,
|
||||
Configuration config
|
||||
) {
|
||||
exists(FlowState state |
|
||||
flowIntoCallNodeCand2(call, node1, node2, allowsFieldFlow, config) and
|
||||
PrevStage::revFlow(node2, pragma[only_bind_into](state), _, pragma[only_bind_into](config)) and
|
||||
PrevStage::revFlowAlias(node1, pragma[only_bind_into](state), _,
|
||||
pragma[only_bind_into](config))
|
||||
)
|
||||
}
|
||||
predicate flowIntoCall = flowIntoCallNodeCand2/5;
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate clearSet(NodeEx node, ContentSet c, Configuration config) {
|
||||
@@ -2531,8 +2291,8 @@ private module Stage4Param implements MkStage<Stage3>::StageParam {
|
||||
}
|
||||
}
|
||||
|
||||
private module Stage4 implements StageSig {
|
||||
import MkStage<Stage3>::Stage<Stage4Param>
|
||||
private module Stage3 implements StageSig {
|
||||
import MkStage<Stage2>::Stage<Stage3Param>
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -2543,8 +2303,8 @@ private predicate flowCandSummaryCtx(
|
||||
NodeEx node, FlowState state, AccessPathFront argApf, Configuration config
|
||||
) {
|
||||
exists(AccessPathFront apf |
|
||||
Stage4::revFlow(node, state, TReturnCtxMaybeFlowThrough(_), _, apf, config) and
|
||||
Stage4::fwdFlow(node, state, any(Stage4::CcCall ccc), _, TAccessPathFrontSome(argApf), apf,
|
||||
Stage3::revFlow(node, state, TReturnCtxMaybeFlowThrough(_), _, apf, config) and
|
||||
Stage3::fwdFlow(node, state, any(Stage3::CcCall ccc), _, TAccessPathFrontSome(argApf), apf,
|
||||
config)
|
||||
)
|
||||
}
|
||||
@@ -2555,10 +2315,10 @@ private predicate flowCandSummaryCtx(
|
||||
*/
|
||||
private predicate expensiveLen2unfolding(TypedContent tc, Configuration config) {
|
||||
exists(int tails, int nodes, int apLimit, int tupleLimit |
|
||||
tails = strictcount(AccessPathFront apf | Stage4::consCand(tc, apf, config)) and
|
||||
tails = strictcount(AccessPathFront apf | Stage3::consCand(tc, apf, config)) and
|
||||
nodes =
|
||||
strictcount(NodeEx n, FlowState state |
|
||||
Stage4::revFlow(n, state, any(AccessPathFrontHead apf | apf.getHead() = tc), config)
|
||||
Stage3::revFlow(n, state, any(AccessPathFrontHead apf | apf.getHead() = tc), config)
|
||||
or
|
||||
flowCandSummaryCtx(n, state, any(AccessPathFrontHead apf | apf.getHead() = tc), config)
|
||||
) and
|
||||
@@ -2572,11 +2332,11 @@ private predicate expensiveLen2unfolding(TypedContent tc, Configuration config)
|
||||
private newtype TAccessPathApprox =
|
||||
TNil(DataFlowType t) or
|
||||
TConsNil(TypedContent tc, DataFlowType t) {
|
||||
Stage4::consCand(tc, TFrontNil(t), _) and
|
||||
Stage3::consCand(tc, TFrontNil(t), _) and
|
||||
not expensiveLen2unfolding(tc, _)
|
||||
} or
|
||||
TConsCons(TypedContent tc1, TypedContent tc2, int len) {
|
||||
Stage4::consCand(tc1, TFrontHead(tc2), _) and
|
||||
Stage3::consCand(tc1, TFrontHead(tc2), _) and
|
||||
len in [2 .. accessPathLimit()] and
|
||||
not expensiveLen2unfolding(tc1, _)
|
||||
} or
|
||||
@@ -2706,7 +2466,7 @@ private class AccessPathApproxCons1 extends AccessPathApproxCons, TCons1 {
|
||||
override AccessPathApprox pop(TypedContent head) {
|
||||
head = tc and
|
||||
(
|
||||
exists(TypedContent tc2 | Stage4::consCand(tc, TFrontHead(tc2), _) |
|
||||
exists(TypedContent tc2 | Stage3::consCand(tc, TFrontHead(tc2), _) |
|
||||
result = TConsCons(tc2, _, len - 1)
|
||||
or
|
||||
len = 2 and
|
||||
@@ -2717,7 +2477,7 @@ private class AccessPathApproxCons1 extends AccessPathApproxCons, TCons1 {
|
||||
or
|
||||
exists(DataFlowType t |
|
||||
len = 1 and
|
||||
Stage4::consCand(tc, TFrontNil(t), _) and
|
||||
Stage3::consCand(tc, TFrontNil(t), _) and
|
||||
result = TNil(t)
|
||||
)
|
||||
)
|
||||
@@ -2742,14 +2502,13 @@ private class AccessPathApproxOption extends TAccessPathApproxOption {
|
||||
}
|
||||
}
|
||||
|
||||
private module Stage5Param implements MkStage<Stage4>::StageParam {
|
||||
private module PrevStage = Stage4;
|
||||
private module Stage4Param implements MkStage<Stage3>::StageParam {
|
||||
private module PrevStage = Stage3;
|
||||
|
||||
class Ap = AccessPathApprox;
|
||||
|
||||
class ApNil = AccessPathApproxNil;
|
||||
|
||||
pragma[nomagic]
|
||||
PrevStage::Ap getApprox(Ap ap) { result = ap.getFront() }
|
||||
|
||||
ApNil getApNil(NodeEx node) {
|
||||
@@ -2775,9 +2534,7 @@ private module Stage5Param implements MkStage<Stage4>::StageParam {
|
||||
NodeEx node1, FlowState state1, NodeEx node2, FlowState state2, boolean preservesValue,
|
||||
ApNil ap, Configuration config, LocalCc lcc
|
||||
) {
|
||||
localFlowBigStep(node1, state1, node2, state2, preservesValue, ap.getType(), config, lcc) and
|
||||
PrevStage::revFlow(node1, pragma[only_bind_into](state1), _, pragma[only_bind_into](config)) and
|
||||
PrevStage::revFlowAlias(node2, pragma[only_bind_into](state2), _, pragma[only_bind_into](config))
|
||||
localFlowBigStep(node1, state1, node2, state2, preservesValue, ap.getFront(), config, lcc)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
@@ -2814,7 +2571,7 @@ private module Stage5Param implements MkStage<Stage4>::StageParam {
|
||||
predicate typecheckStore(Ap ap, DataFlowType contentType) { any() }
|
||||
}
|
||||
|
||||
private module Stage5 = MkStage<Stage4>::Stage<Stage5Param>;
|
||||
private module Stage4 = MkStage<Stage3>::Stage<Stage4Param>;
|
||||
|
||||
bindingset[conf, result]
|
||||
private Configuration unbindConf(Configuration conf) {
|
||||
@@ -2828,8 +2585,8 @@ private predicate nodeMayUseSummary0(
|
||||
) {
|
||||
exists(AccessPathApprox apa0 |
|
||||
c = n.getEnclosingCallable() and
|
||||
Stage5::revFlow(n, state, TReturnCtxMaybeFlowThrough(_), _, apa0, config) and
|
||||
Stage5::fwdFlow(n, state, any(CallContextCall ccc), TParameterPositionSome(pos),
|
||||
Stage4::revFlow(n, state, TReturnCtxMaybeFlowThrough(_), _, apa0, config) and
|
||||
Stage4::fwdFlow(n, state, any(CallContextCall ccc), TParameterPositionSome(pos),
|
||||
TAccessPathApproxSome(apa), apa0, config)
|
||||
)
|
||||
}
|
||||
@@ -2839,7 +2596,7 @@ private predicate nodeMayUseSummary(
|
||||
NodeEx n, FlowState state, AccessPathApprox apa, Configuration config
|
||||
) {
|
||||
exists(DataFlowCallable c, ParameterPosition pos, ParamNodeEx p |
|
||||
Stage5::parameterMayFlowThrough(p, apa, config) and
|
||||
Stage4::parameterMayFlowThrough(p, apa, config) and
|
||||
nodeMayUseSummary0(n, c, pos, state, apa, config) and
|
||||
p.isParameterOf(c, pos)
|
||||
)
|
||||
@@ -2849,8 +2606,8 @@ private newtype TSummaryCtx =
|
||||
TSummaryCtxNone() or
|
||||
TSummaryCtxSome(ParamNodeEx p, FlowState state, AccessPath ap) {
|
||||
exists(Configuration config |
|
||||
Stage5::parameterMayFlowThrough(p, ap.getApprox(), config) and
|
||||
Stage5::revFlow(p, state, _, config)
|
||||
Stage4::parameterMayFlowThrough(p, ap.getApprox(), config) and
|
||||
Stage4::revFlow(p, state, _, config)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -2899,7 +2656,7 @@ private int count1to2unfold(AccessPathApproxCons1 apa, Configuration config) {
|
||||
len = apa.len() and
|
||||
result =
|
||||
strictcount(AccessPathFront apf |
|
||||
Stage5::consCand(tc, any(AccessPathApprox ap | ap.getFront() = apf and ap.len() = len - 1),
|
||||
Stage4::consCand(tc, any(AccessPathApprox ap | ap.getFront() = apf and ap.len() = len - 1),
|
||||
config)
|
||||
)
|
||||
)
|
||||
@@ -2908,7 +2665,7 @@ private int count1to2unfold(AccessPathApproxCons1 apa, Configuration config) {
|
||||
private int countNodesUsingAccessPath(AccessPathApprox apa, Configuration config) {
|
||||
result =
|
||||
strictcount(NodeEx n, FlowState state |
|
||||
Stage5::revFlow(n, state, apa, config) or nodeMayUseSummary(n, state, apa, config)
|
||||
Stage4::revFlow(n, state, apa, config) or nodeMayUseSummary(n, state, apa, config)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -2929,7 +2686,7 @@ private predicate expensiveLen1to2unfolding(AccessPathApproxCons1 apa, Configura
|
||||
private AccessPathApprox getATail(AccessPathApprox apa, Configuration config) {
|
||||
exists(TypedContent head |
|
||||
apa.pop(head) = result and
|
||||
Stage5::consCand(head, result, config)
|
||||
Stage4::consCand(head, result, config)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -3011,7 +2768,7 @@ private newtype TPathNode =
|
||||
NodeEx node, FlowState state, CallContext cc, SummaryCtx sc, AccessPath ap, Configuration config
|
||||
) {
|
||||
// A PathNode is introduced by a source ...
|
||||
Stage5::revFlow(node, state, config) and
|
||||
Stage4::revFlow(node, state, config) and
|
||||
sourceNode(node, state, config) and
|
||||
(
|
||||
if hasSourceCallCtx(config)
|
||||
@@ -3025,7 +2782,7 @@ private newtype TPathNode =
|
||||
exists(PathNodeMid mid |
|
||||
pathStep(mid, node, state, cc, sc, ap) and
|
||||
pragma[only_bind_into](config) = mid.getConfiguration() and
|
||||
Stage5::revFlow(node, state, ap.getApprox(), pragma[only_bind_into](config))
|
||||
Stage4::revFlow(node, state, ap.getApprox(), pragma[only_bind_into](config))
|
||||
)
|
||||
} or
|
||||
TPathNodeSink(NodeEx node, FlowState state, Configuration config) {
|
||||
@@ -3167,7 +2924,7 @@ private class AccessPathCons2 extends AccessPath, TAccessPathCons2 {
|
||||
override TypedContent getHead() { result = head1 }
|
||||
|
||||
override AccessPath getTail() {
|
||||
Stage5::consCand(head1, result.getApprox(), _) and
|
||||
Stage4::consCand(head1, result.getApprox(), _) and
|
||||
result.getHead() = head2 and
|
||||
result.length() = len - 1
|
||||
}
|
||||
@@ -3198,7 +2955,7 @@ private class AccessPathCons1 extends AccessPath, TAccessPathCons1 {
|
||||
override TypedContent getHead() { result = head }
|
||||
|
||||
override AccessPath getTail() {
|
||||
Stage5::consCand(head, result.getApprox(), _) and result.length() = len - 1
|
||||
Stage4::consCand(head, result.getApprox(), _) and result.length() = len - 1
|
||||
}
|
||||
|
||||
override AccessPathFrontHead getFront() { result = TFrontHead(head) }
|
||||
@@ -3590,8 +3347,7 @@ private predicate pathStep(
|
||||
AccessPath ap0, NodeEx midnode, FlowState state0, Configuration conf, LocalCallContext localCC
|
||||
|
|
||||
pathNode(mid, midnode, state0, cc, sc, ap0, conf, localCC) and
|
||||
localFlowBigStep(midnode, state0, node, state, false, ap.(AccessPathNil).getType(), conf,
|
||||
localCC) and
|
||||
localFlowBigStep(midnode, state0, node, state, false, ap.getFront(), conf, localCC) and
|
||||
ap0 instanceof AccessPathNil
|
||||
)
|
||||
or
|
||||
@@ -3633,7 +3389,7 @@ private predicate pathReadStep(
|
||||
) {
|
||||
ap0 = mid.getAp() and
|
||||
tc = ap0.getHead() and
|
||||
Stage5::readStepCand(mid.getNodeEx(), tc.getContent(), node, mid.getConfiguration()) and
|
||||
Stage4::readStepCand(mid.getNodeEx(), tc.getContent(), node, mid.getConfiguration()) and
|
||||
state = mid.getState() and
|
||||
cc = mid.getCallContext()
|
||||
}
|
||||
@@ -3643,7 +3399,7 @@ private predicate pathStoreStep(
|
||||
PathNodeMid mid, NodeEx node, FlowState state, AccessPath ap0, TypedContent tc, CallContext cc
|
||||
) {
|
||||
ap0 = mid.getAp() and
|
||||
Stage5::storeStepCand(mid.getNodeEx(), _, tc, node, _, mid.getConfiguration()) and
|
||||
Stage4::storeStepCand(mid.getNodeEx(), _, tc, node, _, mid.getConfiguration()) and
|
||||
state = mid.getState() and
|
||||
cc = mid.getCallContext()
|
||||
}
|
||||
@@ -3680,7 +3436,7 @@ private NodeEx getAnOutNodeFlow(
|
||||
ReturnKindExt kind, DataFlowCall call, AccessPathApprox apa, Configuration config
|
||||
) {
|
||||
result.asNode() = kind.getAnOutNode(call) and
|
||||
Stage5::revFlow(result, _, apa, config)
|
||||
Stage4::revFlow(result, _, apa, config)
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -3716,7 +3472,7 @@ private predicate parameterCand(
|
||||
DataFlowCallable callable, ParameterPosition pos, AccessPathApprox apa, Configuration config
|
||||
) {
|
||||
exists(ParamNodeEx p |
|
||||
Stage5::revFlow(p, _, apa, config) and
|
||||
Stage4::revFlow(p, _, apa, config) and
|
||||
p.isParameterOf(callable, pos)
|
||||
)
|
||||
}
|
||||
@@ -3995,17 +3751,9 @@ predicate stageStats(
|
||||
n = 45 and
|
||||
Stage4::stats(false, nodes, fields, conscand, states, tuples, config)
|
||||
or
|
||||
stage = "5 Fwd" and
|
||||
n = 50 and
|
||||
Stage5::stats(true, nodes, fields, conscand, states, tuples, config)
|
||||
stage = "5 Fwd" and n = 50 and finalStats(true, nodes, fields, conscand, states, tuples)
|
||||
or
|
||||
stage = "5 Rev" and
|
||||
n = 55 and
|
||||
Stage5::stats(false, nodes, fields, conscand, states, tuples, config)
|
||||
or
|
||||
stage = "6 Fwd" and n = 60 and finalStats(true, nodes, fields, conscand, states, tuples)
|
||||
or
|
||||
stage = "6 Rev" and n = 65 and finalStats(false, nodes, fields, conscand, states, tuples)
|
||||
stage = "5 Rev" and n = 55 and finalStats(false, nodes, fields, conscand, states, tuples)
|
||||
}
|
||||
|
||||
private module FlowExploration {
|
||||
|
||||
@@ -296,13 +296,6 @@ predicate additionalLambdaFlowStep(Node nodeFrom, Node nodeTo, boolean preserves
|
||||
*/
|
||||
predicate allowParameterReturnInSelf(ParameterNode p) { none() }
|
||||
|
||||
/** An approximated `Content`. */
|
||||
class ContentApprox = Unit;
|
||||
|
||||
/** Gets an approximated value for content `c`. */
|
||||
pragma[inline]
|
||||
ContentApprox getContentApprox(Content c) { any() }
|
||||
|
||||
private class MyConsistencyConfiguration extends Consistency::ConsistencyConfiguration {
|
||||
override predicate argHasPostUpdateExclude(ArgumentNode n) {
|
||||
// Is the null pointer (or something that's not really a pointer)
|
||||
|
||||
@@ -1,21 +1,643 @@
|
||||
/**
|
||||
* DEPRECATED: Use `semmle.code.cpp.ir.dataflow.TaintTracking` as a replacement.
|
||||
*
|
||||
* An IR taint tracking library that uses an IR DataFlow configuration to track
|
||||
* taint from user inputs as defined by `semmle.code.cpp.security.Security`.
|
||||
*/
|
||||
|
||||
import cpp
|
||||
import semmle.code.cpp.security.Security
|
||||
private import semmle.code.cpp.ir.dataflow.internal.DefaultTaintTrackingImpl as DefaultTaintTrackingImpl
|
||||
private import semmle.code.cpp.ir.dataflow.DataFlow
|
||||
private import semmle.code.cpp.ir.dataflow.internal.DataFlowUtil
|
||||
private import semmle.code.cpp.ir.dataflow.DataFlow3
|
||||
private import semmle.code.cpp.ir.IR
|
||||
private import semmle.code.cpp.ir.dataflow.ResolveCall
|
||||
private import semmle.code.cpp.controlflow.IRGuards
|
||||
private import semmle.code.cpp.models.interfaces.Taint
|
||||
private import semmle.code.cpp.models.interfaces.DataFlow
|
||||
private import semmle.code.cpp.ir.dataflow.TaintTracking
|
||||
private import semmle.code.cpp.ir.dataflow.TaintTracking2
|
||||
private import semmle.code.cpp.ir.dataflow.TaintTracking3
|
||||
private import semmle.code.cpp.ir.dataflow.internal.ModelUtil
|
||||
|
||||
deprecated predicate predictableOnlyFlow = DefaultTaintTrackingImpl::predictableOnlyFlow/1;
|
||||
/**
|
||||
* A predictable instruction is one where an external user can predict
|
||||
* the value. For example, a literal in the source code is considered
|
||||
* predictable.
|
||||
*/
|
||||
private predicate predictableInstruction(Instruction instr) {
|
||||
instr instanceof ConstantInstruction
|
||||
or
|
||||
instr instanceof StringConstantInstruction
|
||||
or
|
||||
// This could be a conversion on a string literal
|
||||
predictableInstruction(instr.(UnaryInstruction).getUnary())
|
||||
}
|
||||
|
||||
deprecated predicate tainted = DefaultTaintTrackingImpl::tainted/2;
|
||||
/**
|
||||
* Functions that we should only allow taint to flow through (to the return
|
||||
* value) if all but the source argument are 'predictable'. This is done to
|
||||
* emulate the old security library's implementation rather than due to any
|
||||
* strong belief that this is the right approach.
|
||||
*
|
||||
* Note that the list itself is not very principled; it consists of all the
|
||||
* functions listed in the old security library's [default] `isPureFunction`
|
||||
* that have more than one argument, but are not in the old taint tracking
|
||||
* library's `returnArgument` predicate.
|
||||
*/
|
||||
predicate predictableOnlyFlow(string name) {
|
||||
name =
|
||||
[
|
||||
"strcasestr", "strchnul", "strchr", "strchrnul", "strcmp", "strcspn", "strncmp", "strndup",
|
||||
"strnlen", "strrchr", "strspn", "strstr", "strtod", "strtof", "strtol", "strtoll", "strtoq",
|
||||
"strtoul"
|
||||
]
|
||||
}
|
||||
|
||||
deprecated predicate taintedIncludingGlobalVars =
|
||||
DefaultTaintTrackingImpl::taintedIncludingGlobalVars/3;
|
||||
private DataFlow::Node getNodeForSource(Expr source) {
|
||||
isUserInput(source, _) and
|
||||
result = getNodeForExpr(source)
|
||||
}
|
||||
|
||||
deprecated predicate globalVarFromId = DefaultTaintTrackingImpl::globalVarFromId/1;
|
||||
private DataFlow::Node getNodeForExpr(Expr node) {
|
||||
result = DataFlow::exprNode(node)
|
||||
or
|
||||
// Some of the sources in `isUserInput` are intended to match the value of
|
||||
// an expression, while others (those modeled below) are intended to match
|
||||
// the taint that propagates out of an argument, like the `char *` argument
|
||||
// to `gets`. It's impossible here to tell which is which, but the "access
|
||||
// to argv" source is definitely not intended to match an output argument,
|
||||
// and it causes false positives if we let it.
|
||||
//
|
||||
// This case goes together with the similar (but not identical) rule in
|
||||
// `nodeIsBarrierIn`.
|
||||
result = DataFlow::definitionByReferenceNodeFromArgument(node) and
|
||||
not argv(node.(VariableAccess).getTarget())
|
||||
}
|
||||
|
||||
deprecated module TaintedWithPath = DefaultTaintTrackingImpl::TaintedWithPath;
|
||||
private class DefaultTaintTrackingCfg extends TaintTracking::Configuration {
|
||||
DefaultTaintTrackingCfg() { this = "DefaultTaintTrackingCfg" }
|
||||
|
||||
override predicate isSource(DataFlow::Node source) { source = getNodeForSource(_) }
|
||||
|
||||
override predicate isSink(DataFlow::Node sink) { exists(adjustedSink(sink)) }
|
||||
|
||||
override predicate isSanitizer(DataFlow::Node node) { nodeIsBarrier(node) }
|
||||
|
||||
override predicate isSanitizerIn(DataFlow::Node node) { nodeIsBarrierIn(node) }
|
||||
}
|
||||
|
||||
private class ToGlobalVarTaintTrackingCfg extends TaintTracking::Configuration {
|
||||
ToGlobalVarTaintTrackingCfg() { this = "GlobalVarTaintTrackingCfg" }
|
||||
|
||||
override predicate isSource(DataFlow::Node source) { source = getNodeForSource(_) }
|
||||
|
||||
override predicate isSink(DataFlow::Node sink) {
|
||||
sink.asVariable() instanceof GlobalOrNamespaceVariable
|
||||
}
|
||||
|
||||
override predicate isAdditionalTaintStep(DataFlow::Node n1, DataFlow::Node n2) {
|
||||
writesVariable(n1.asInstruction(), n2.asVariable().(GlobalOrNamespaceVariable))
|
||||
or
|
||||
readsVariable(n2.asInstruction(), n1.asVariable().(GlobalOrNamespaceVariable))
|
||||
}
|
||||
|
||||
override predicate isSanitizer(DataFlow::Node node) { nodeIsBarrier(node) }
|
||||
|
||||
override predicate isSanitizerIn(DataFlow::Node node) { nodeIsBarrierIn(node) }
|
||||
}
|
||||
|
||||
private class FromGlobalVarTaintTrackingCfg extends TaintTracking2::Configuration {
|
||||
FromGlobalVarTaintTrackingCfg() { this = "FromGlobalVarTaintTrackingCfg" }
|
||||
|
||||
override predicate isSource(DataFlow::Node source) {
|
||||
// This set of sources should be reasonably small, which is good for
|
||||
// performance since the set of sinks is very large.
|
||||
exists(ToGlobalVarTaintTrackingCfg otherCfg | otherCfg.hasFlowTo(source))
|
||||
}
|
||||
|
||||
override predicate isSink(DataFlow::Node sink) { exists(adjustedSink(sink)) }
|
||||
|
||||
override predicate isAdditionalTaintStep(DataFlow::Node n1, DataFlow::Node n2) {
|
||||
// Additional step for flow out of variables. There is no flow _into_
|
||||
// variables in this configuration, so this step only serves to take flow
|
||||
// out of a variable that's a source.
|
||||
readsVariable(n2.asInstruction(), n1.asVariable())
|
||||
}
|
||||
|
||||
override predicate isSanitizer(DataFlow::Node node) { nodeIsBarrier(node) }
|
||||
|
||||
override predicate isSanitizerIn(DataFlow::Node node) { nodeIsBarrierIn(node) }
|
||||
}
|
||||
|
||||
private predicate readsVariable(LoadInstruction load, Variable var) {
|
||||
load.getSourceAddress().(VariableAddressInstruction).getAstVariable() = var
|
||||
}
|
||||
|
||||
private predicate writesVariable(StoreInstruction store, Variable var) {
|
||||
store.getDestinationAddress().(VariableAddressInstruction).getAstVariable() = var
|
||||
}
|
||||
|
||||
/**
|
||||
* A variable that has any kind of upper-bound check anywhere in the program. This is
|
||||
* biased towards being inclusive because there are a lot of valid ways of doing an
|
||||
* upper bounds checks if we don't consider where it occurs, for example:
|
||||
* ```
|
||||
* if (x < 10) { sink(x); }
|
||||
*
|
||||
* if (10 > y) { sink(y); }
|
||||
*
|
||||
* if (z > 10) { z = 10; }
|
||||
* sink(z);
|
||||
* ```
|
||||
*/
|
||||
// TODO: This coarse overapproximation, ported from the old taint tracking
|
||||
// library, could be replaced with an actual semantic check that a particular
|
||||
// variable _access_ is guarded by an upper-bound check. We probably don't want
|
||||
// to do this right away since it could expose a lot of FPs that were
|
||||
// previously suppressed by this predicate by coincidence.
|
||||
private predicate hasUpperBoundsCheck(Variable var) {
|
||||
exists(RelationalOperation oper, VariableAccess access |
|
||||
oper.getAnOperand() = access and
|
||||
access.getTarget() = var and
|
||||
// Comparing to 0 is not an upper bound check
|
||||
not oper.getAnOperand().getValue() = "0"
|
||||
)
|
||||
}
|
||||
|
||||
private predicate nodeIsBarrierEqualityCandidate(
|
||||
DataFlow::Node node, Operand access, Variable checkedVar
|
||||
) {
|
||||
readsVariable(node.asInstruction(), checkedVar) and
|
||||
any(IRGuardCondition guard).ensuresEq(access, _, _, node.asInstruction().getBlock(), true)
|
||||
}
|
||||
|
||||
cached
|
||||
private module Cached {
|
||||
cached
|
||||
predicate nodeIsBarrier(DataFlow::Node node) {
|
||||
exists(Variable checkedVar |
|
||||
readsVariable(node.asInstruction(), checkedVar) and
|
||||
hasUpperBoundsCheck(checkedVar)
|
||||
)
|
||||
or
|
||||
exists(Variable checkedVar, Operand access |
|
||||
/*
|
||||
* This node is guarded by a condition that forces the accessed variable
|
||||
* to equal something else. For example:
|
||||
* ```
|
||||
* x = taintsource()
|
||||
* if (x == 10) {
|
||||
* taintsink(x); // not considered tainted
|
||||
* }
|
||||
* ```
|
||||
*/
|
||||
|
||||
nodeIsBarrierEqualityCandidate(node, access, checkedVar) and
|
||||
readsVariable(access.getDef(), checkedVar)
|
||||
)
|
||||
}
|
||||
|
||||
cached
|
||||
predicate nodeIsBarrierIn(DataFlow::Node node) {
|
||||
// don't use dataflow into taint sources, as this leads to duplicate results.
|
||||
exists(Expr source | isUserInput(source, _) |
|
||||
node = DataFlow::exprNode(source)
|
||||
or
|
||||
// This case goes together with the similar (but not identical) rule in
|
||||
// `getNodeForSource`.
|
||||
node = DataFlow::definitionByReferenceNodeFromArgument(source)
|
||||
)
|
||||
or
|
||||
// don't use dataflow into binary instructions if both operands are unpredictable
|
||||
exists(BinaryInstruction iTo |
|
||||
iTo = node.asInstruction() and
|
||||
not predictableInstruction(iTo.getLeft()) and
|
||||
not predictableInstruction(iTo.getRight()) and
|
||||
// propagate taint from either the pointer or the offset, regardless of predictability
|
||||
not iTo instanceof PointerArithmeticInstruction
|
||||
)
|
||||
or
|
||||
// don't use dataflow through calls to pure functions if two or more operands
|
||||
// are unpredictable
|
||||
exists(Instruction iFrom1, Instruction iFrom2, CallInstruction iTo |
|
||||
iTo = node.asInstruction() and
|
||||
isPureFunction(iTo.getStaticCallTarget().getName()) and
|
||||
iFrom1 = iTo.getAnArgument() and
|
||||
iFrom2 = iTo.getAnArgument() and
|
||||
not predictableInstruction(iFrom1) and
|
||||
not predictableInstruction(iFrom2) and
|
||||
iFrom1 != iFrom2
|
||||
)
|
||||
}
|
||||
|
||||
cached
|
||||
Element adjustedSink(DataFlow::Node sink) {
|
||||
// TODO: is it more appropriate to use asConvertedExpr here and avoid
|
||||
// `getConversion*`? Or will that cause us to miss some cases where there's
|
||||
// flow to a conversion (like a `ReferenceDereferenceExpr`) and we want to
|
||||
// pretend there was flow to the converted `Expr` for the sake of
|
||||
// compatibility.
|
||||
sink.asExpr().getConversion*() = result
|
||||
or
|
||||
// For compatibility, send flow from arguments to parameters, even for
|
||||
// functions with no body.
|
||||
exists(FunctionCall call, int i |
|
||||
sink.asExpr() = call.getArgument(pragma[only_bind_into](i)) and
|
||||
result = resolveCall(call).getParameter(pragma[only_bind_into](i))
|
||||
)
|
||||
or
|
||||
// For compatibility, send flow into a `Variable` if there is flow to any
|
||||
// Load or Store of that variable.
|
||||
exists(CopyInstruction copy |
|
||||
copy.getSourceValue() = sink.asInstruction() and
|
||||
(
|
||||
readsVariable(copy, result) or
|
||||
writesVariable(copy, result)
|
||||
) and
|
||||
not hasUpperBoundsCheck(result)
|
||||
)
|
||||
or
|
||||
// For compatibility, send flow into a `NotExpr` even if it's part of a
|
||||
// short-circuiting condition and thus might get skipped.
|
||||
result.(NotExpr).getOperand() = sink.asExpr()
|
||||
or
|
||||
// Taint postfix and prefix crement operations when their operand is tainted.
|
||||
result.(CrementOperation).getAnOperand() = sink.asExpr()
|
||||
or
|
||||
// Taint `e1 += e2`, `e &= e2` and friends when `e1` or `e2` is tainted.
|
||||
result.(AssignOperation).getAnOperand() = sink.asExpr()
|
||||
or
|
||||
result =
|
||||
sink.asOperand()
|
||||
.(SideEffectOperand)
|
||||
.getUse()
|
||||
.(ReadSideEffectInstruction)
|
||||
.getArgumentDef()
|
||||
.getUnconvertedResultExpression()
|
||||
}
|
||||
|
||||
/**
|
||||
* Step to return value of a modeled function when an input taints the
|
||||
* dereference of the return value.
|
||||
*/
|
||||
cached
|
||||
predicate additionalTaintStep(DataFlow::Node n1, DataFlow::Node n2) {
|
||||
exists(CallInstruction call, Function func, FunctionInput modelIn, FunctionOutput modelOut |
|
||||
n1.asOperand() = callInput(call, modelIn) and
|
||||
(
|
||||
func.(TaintFunction).hasTaintFlow(modelIn, modelOut)
|
||||
or
|
||||
func.(DataFlowFunction).hasDataFlow(modelIn, modelOut)
|
||||
) and
|
||||
call.getStaticCallTarget() = func and
|
||||
modelOut.isReturnValueDeref() and
|
||||
call = n2.asInstruction()
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private import Cached
|
||||
|
||||
/**
|
||||
* Holds if `tainted` may contain taint from `source`.
|
||||
*
|
||||
* A tainted expression is either directly user input, or is
|
||||
* computed from user input in a way that users can probably
|
||||
* control the exact output of the computation.
|
||||
*
|
||||
* This doesn't include data flow through global variables.
|
||||
* If you need that you must call `taintedIncludingGlobalVars`.
|
||||
*/
|
||||
cached
|
||||
predicate tainted(Expr source, Element tainted) {
|
||||
exists(DefaultTaintTrackingCfg cfg, DataFlow::Node sink |
|
||||
cfg.hasFlow(getNodeForSource(source), sink) and
|
||||
tainted = adjustedSink(sink)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `tainted` may contain taint from `source`, where the taint passed
|
||||
* through a global variable named `globalVar`.
|
||||
*
|
||||
* A tainted expression is either directly user input, or is
|
||||
* computed from user input in a way that users can probably
|
||||
* control the exact output of the computation.
|
||||
*
|
||||
* This version gives the same results as tainted but also includes
|
||||
* data flow through global variables.
|
||||
*
|
||||
* The parameter `globalVar` is the qualified name of the last global variable
|
||||
* used to move the value from source to tainted. If the taint did not pass
|
||||
* through a global variable, then `globalVar = ""`.
|
||||
*/
|
||||
cached
|
||||
predicate taintedIncludingGlobalVars(Expr source, Element tainted, string globalVar) {
|
||||
tainted(source, tainted) and
|
||||
globalVar = ""
|
||||
or
|
||||
exists(
|
||||
ToGlobalVarTaintTrackingCfg toCfg, FromGlobalVarTaintTrackingCfg fromCfg,
|
||||
DataFlow::VariableNode variableNode, GlobalOrNamespaceVariable global, DataFlow::Node sink
|
||||
|
|
||||
global = variableNode.getVariable() and
|
||||
toCfg.hasFlow(getNodeForSource(source), variableNode) and
|
||||
fromCfg.hasFlow(variableNode, sink) and
|
||||
tainted = adjustedSink(sink) and
|
||||
global = globalVarFromId(globalVar)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the global variable whose qualified name is `id`. Use this predicate
|
||||
* together with `taintedIncludingGlobalVars`. Example:
|
||||
*
|
||||
* ```
|
||||
* exists(string varName |
|
||||
* taintedIncludingGlobalVars(source, tainted, varName) and
|
||||
* var = globalVarFromId(varName)
|
||||
* )
|
||||
* ```
|
||||
*/
|
||||
GlobalOrNamespaceVariable globalVarFromId(string id) { id = result.getQualifiedName() }
|
||||
|
||||
/**
|
||||
* Provides definitions for augmenting source/sink pairs with data-flow paths
|
||||
* between them. From a `@kind path-problem` query, import this module in the
|
||||
* global scope, extend `TaintTrackingConfiguration`, and use `taintedWithPath`
|
||||
* in place of `tainted`.
|
||||
*
|
||||
* Importing this module will also import the query predicates that contain the
|
||||
* taint paths.
|
||||
*/
|
||||
module TaintedWithPath {
|
||||
private newtype TSingleton = MkSingleton()
|
||||
|
||||
/**
|
||||
* A taint-tracking configuration that matches sources and sinks in the same
|
||||
* way as the `tainted` predicate.
|
||||
*
|
||||
* Override `isSink` and `taintThroughGlobals` as needed, but do not provide
|
||||
* a characteristic predicate.
|
||||
*/
|
||||
class TaintTrackingConfiguration extends TSingleton {
|
||||
/** Override this to specify which elements are sources in this configuration. */
|
||||
predicate isSource(Expr source) { exists(getNodeForSource(source)) }
|
||||
|
||||
/** Override this to specify which elements are sinks in this configuration. */
|
||||
abstract predicate isSink(Element e);
|
||||
|
||||
/** Override this to specify which expressions are barriers in this configuration. */
|
||||
predicate isBarrier(Expr e) { nodeIsBarrier(getNodeForExpr(e)) }
|
||||
|
||||
/**
|
||||
* Override this predicate to `any()` to allow taint to flow through global
|
||||
* variables.
|
||||
*/
|
||||
predicate taintThroughGlobals() { none() }
|
||||
|
||||
/** Gets a textual representation of this element. */
|
||||
string toString() { result = "TaintTrackingConfiguration" }
|
||||
}
|
||||
|
||||
private class AdjustedConfiguration extends TaintTracking3::Configuration {
|
||||
AdjustedConfiguration() { this = "AdjustedConfiguration" }
|
||||
|
||||
override predicate isSource(DataFlow::Node source) {
|
||||
exists(TaintTrackingConfiguration cfg, Expr e |
|
||||
cfg.isSource(e) and source = getNodeForExpr(e)
|
||||
)
|
||||
}
|
||||
|
||||
override predicate isSink(DataFlow::Node sink) {
|
||||
exists(TaintTrackingConfiguration cfg | cfg.isSink(adjustedSink(sink)))
|
||||
}
|
||||
|
||||
override predicate isAdditionalTaintStep(DataFlow::Node n1, DataFlow::Node n2) {
|
||||
// Steps into and out of global variables
|
||||
exists(TaintTrackingConfiguration cfg | cfg.taintThroughGlobals() |
|
||||
writesVariable(n1.asInstruction(), n2.asVariable().(GlobalOrNamespaceVariable))
|
||||
or
|
||||
readsVariable(n2.asInstruction(), n1.asVariable().(GlobalOrNamespaceVariable))
|
||||
)
|
||||
or
|
||||
additionalTaintStep(n1, n2)
|
||||
}
|
||||
|
||||
override predicate isSanitizer(DataFlow::Node node) {
|
||||
exists(TaintTrackingConfiguration cfg, Expr e | cfg.isBarrier(e) and node = getNodeForExpr(e))
|
||||
}
|
||||
|
||||
override predicate isSanitizerIn(DataFlow::Node node) { nodeIsBarrierIn(node) }
|
||||
}
|
||||
|
||||
/*
|
||||
* A sink `Element` may map to multiple `DataFlowX::PathNode`s via (the
|
||||
* inverse of) `adjustedSink`. For example, an `Expr` maps to all its
|
||||
* conversions, and a `Variable` maps to all loads and stores from it. Because
|
||||
* the path node is part of the tuple that constitutes the alert, this leads
|
||||
* to duplicate alerts.
|
||||
*
|
||||
* To avoid showing duplicates, we edit the graph to replace the final node
|
||||
* coming from the data-flow library with a node that matches exactly the
|
||||
* `Element` sink that's requested.
|
||||
*
|
||||
* The same is done for sources.
|
||||
*/
|
||||
|
||||
private newtype TPathNode =
|
||||
TWrapPathNode(DataFlow3::PathNode n) or
|
||||
// There's a single newtype constructor for both sources and sinks since
|
||||
// that makes it easiest to deal with the case where source = sink.
|
||||
TEndpointPathNode(Element e) {
|
||||
exists(AdjustedConfiguration cfg, DataFlow3::Node sourceNode, DataFlow3::Node sinkNode |
|
||||
cfg.hasFlow(sourceNode, sinkNode)
|
||||
|
|
||||
sourceNode = getNodeForExpr(e) and
|
||||
exists(TaintTrackingConfiguration ttCfg | ttCfg.isSource(e))
|
||||
or
|
||||
e = adjustedSink(sinkNode) and
|
||||
exists(TaintTrackingConfiguration ttCfg | ttCfg.isSink(e))
|
||||
)
|
||||
}
|
||||
|
||||
/** An opaque type used for the nodes of a data-flow path. */
|
||||
class PathNode extends TPathNode {
|
||||
/** Gets a textual representation of this element. */
|
||||
string toString() { none() }
|
||||
|
||||
/**
|
||||
* Holds if this element is at the specified location.
|
||||
* The location spans column `startcolumn` of line `startline` to
|
||||
* column `endcolumn` of line `endline` in file `filepath`.
|
||||
* For more information, see
|
||||
* [Locations](https://codeql.github.com/docs/writing-codeql-queries/providing-locations-in-codeql-queries/).
|
||||
*/
|
||||
pragma[inline]
|
||||
predicate hasLocationInfo(
|
||||
string filepath, int startline, int startcolumn, int endline, int endcolumn
|
||||
) {
|
||||
none()
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* INTERNAL: Do not use.
|
||||
*/
|
||||
module Private {
|
||||
/** Gets a predecessor `PathNode` of `pathNode`, if any. */
|
||||
PathNode getAPredecessor(PathNode pathNode) { edges(result, pathNode) }
|
||||
|
||||
/** Gets the element that `pathNode` wraps, if any. */
|
||||
Element getElementFromPathNode(PathNode pathNode) {
|
||||
exists(DataFlow::Node node | node = pathNode.(WrapPathNode).inner().getNode() |
|
||||
result = node.asInstruction().getAst()
|
||||
or
|
||||
result = node.asOperand().getDef().getAst()
|
||||
)
|
||||
or
|
||||
result = pathNode.(EndpointPathNode).inner()
|
||||
}
|
||||
}
|
||||
|
||||
private class WrapPathNode extends PathNode, TWrapPathNode {
|
||||
DataFlow3::PathNode inner() { this = TWrapPathNode(result) }
|
||||
|
||||
override string toString() { result = this.inner().toString() }
|
||||
|
||||
override predicate hasLocationInfo(
|
||||
string filepath, int startline, int startcolumn, int endline, int endcolumn
|
||||
) {
|
||||
this.inner().hasLocationInfo(filepath, startline, startcolumn, endline, endcolumn)
|
||||
}
|
||||
}
|
||||
|
||||
private class EndpointPathNode extends PathNode, TEndpointPathNode {
|
||||
Expr inner() { this = TEndpointPathNode(result) }
|
||||
|
||||
override string toString() { result = this.inner().toString() }
|
||||
|
||||
override predicate hasLocationInfo(
|
||||
string filepath, int startline, int startcolumn, int endline, int endcolumn
|
||||
) {
|
||||
this.inner()
|
||||
.getLocation()
|
||||
.hasLocationInfo(filepath, startline, startcolumn, endline, endcolumn)
|
||||
}
|
||||
}
|
||||
|
||||
/** A PathNode whose `Element` is a source. It may also be a sink. */
|
||||
private class InitialPathNode extends EndpointPathNode {
|
||||
InitialPathNode() { exists(TaintTrackingConfiguration cfg | cfg.isSource(this.inner())) }
|
||||
}
|
||||
|
||||
/** A PathNode whose `Element` is a sink. It may also be a source. */
|
||||
private class FinalPathNode extends EndpointPathNode {
|
||||
FinalPathNode() { exists(TaintTrackingConfiguration cfg | cfg.isSink(this.inner())) }
|
||||
}
|
||||
|
||||
/** Holds if `(a,b)` is an edge in the graph of data flow path explanations. */
|
||||
query predicate edges(PathNode a, PathNode b) {
|
||||
DataFlow3::PathGraph::edges(a.(WrapPathNode).inner(), b.(WrapPathNode).inner())
|
||||
or
|
||||
// To avoid showing trivial-looking steps, we _replace_ the last node instead
|
||||
// of adding an edge out of it.
|
||||
exists(WrapPathNode sinkNode |
|
||||
DataFlow3::PathGraph::edges(a.(WrapPathNode).inner(), sinkNode.inner()) and
|
||||
b.(FinalPathNode).inner() = adjustedSink(sinkNode.inner().getNode())
|
||||
)
|
||||
or
|
||||
// Same for the first node
|
||||
exists(WrapPathNode sourceNode |
|
||||
DataFlow3::PathGraph::edges(sourceNode.inner(), b.(WrapPathNode).inner()) and
|
||||
sourceNode.inner().getNode() = getNodeForExpr(a.(InitialPathNode).inner())
|
||||
)
|
||||
or
|
||||
// Finally, handle the case where the path goes directly from a source to a
|
||||
// sink, meaning that they both need to be translated.
|
||||
exists(WrapPathNode sinkNode, WrapPathNode sourceNode |
|
||||
DataFlow3::PathGraph::edges(sourceNode.inner(), sinkNode.inner()) and
|
||||
sourceNode.inner().getNode() = getNodeForExpr(a.(InitialPathNode).inner()) and
|
||||
b.(FinalPathNode).inner() = adjustedSink(sinkNode.inner().getNode())
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if there is flow from `arg` to `out` across a call that can by summarized by the flow
|
||||
* from `par` to `ret` within it, in the graph of data flow path explanations.
|
||||
*/
|
||||
query predicate subpaths(PathNode arg, PathNode par, PathNode ret, PathNode out) {
|
||||
DataFlow3::PathGraph::subpaths(arg.(WrapPathNode).inner(), par.(WrapPathNode).inner(),
|
||||
ret.(WrapPathNode).inner(), out.(WrapPathNode).inner())
|
||||
or
|
||||
// To avoid showing trivial-looking steps, we _replace_ the last node instead
|
||||
// of adding an edge out of it.
|
||||
exists(WrapPathNode sinkNode |
|
||||
DataFlow3::PathGraph::subpaths(arg.(WrapPathNode).inner(), par.(WrapPathNode).inner(),
|
||||
ret.(WrapPathNode).inner(), sinkNode.inner()) and
|
||||
out.(FinalPathNode).inner() = adjustedSink(sinkNode.inner().getNode())
|
||||
)
|
||||
or
|
||||
// Same for the first node
|
||||
exists(WrapPathNode sourceNode |
|
||||
DataFlow3::PathGraph::subpaths(sourceNode.inner(), par.(WrapPathNode).inner(),
|
||||
ret.(WrapPathNode).inner(), out.(WrapPathNode).inner()) and
|
||||
sourceNode.inner().getNode() = getNodeForExpr(arg.(InitialPathNode).inner())
|
||||
)
|
||||
or
|
||||
// Finally, handle the case where the path goes directly from a source to a
|
||||
// sink, meaning that they both need to be translated.
|
||||
exists(WrapPathNode sinkNode, WrapPathNode sourceNode |
|
||||
DataFlow3::PathGraph::subpaths(sourceNode.inner(), par.(WrapPathNode).inner(),
|
||||
ret.(WrapPathNode).inner(), sinkNode.inner()) and
|
||||
sourceNode.inner().getNode() = getNodeForExpr(arg.(InitialPathNode).inner()) and
|
||||
out.(FinalPathNode).inner() = adjustedSink(sinkNode.inner().getNode())
|
||||
)
|
||||
}
|
||||
|
||||
/** Holds if `n` is a node in the graph of data flow path explanations. */
|
||||
query predicate nodes(PathNode n, string key, string val) {
|
||||
key = "semmle.label" and val = n.toString()
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `tainted` may contain taint from `source`, where `sourceNode` and
|
||||
* `sinkNode` are the corresponding `PathNode`s that can be used in a query
|
||||
* to provide path explanations. Extend `TaintTrackingConfiguration` to use
|
||||
* this predicate.
|
||||
*
|
||||
* A tainted expression is either directly user input, or is computed from
|
||||
* user input in a way that users can probably control the exact output of
|
||||
* the computation.
|
||||
*/
|
||||
predicate taintedWithPath(Expr source, Element tainted, PathNode sourceNode, PathNode sinkNode) {
|
||||
exists(AdjustedConfiguration cfg, DataFlow3::Node flowSource, DataFlow3::Node flowSink |
|
||||
source = sourceNode.(InitialPathNode).inner() and
|
||||
flowSource = getNodeForExpr(source) and
|
||||
cfg.hasFlow(flowSource, flowSink) and
|
||||
tainted = adjustedSink(flowSink) and
|
||||
tainted = sinkNode.(FinalPathNode).inner()
|
||||
)
|
||||
}
|
||||
|
||||
private predicate isGlobalVariablePathNode(WrapPathNode n) {
|
||||
n.inner().getNode().asVariable() instanceof GlobalOrNamespaceVariable
|
||||
}
|
||||
|
||||
private predicate edgesWithoutGlobals(PathNode a, PathNode b) {
|
||||
edges(a, b) and
|
||||
not isGlobalVariablePathNode(a) and
|
||||
not isGlobalVariablePathNode(b)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `tainted` can be reached from a taint source without passing
|
||||
* through a global variable.
|
||||
*/
|
||||
predicate taintedWithoutGlobals(Element tainted) {
|
||||
exists(AdjustedConfiguration cfg, PathNode sourceNode, FinalPathNode sinkNode |
|
||||
cfg.isSource(sourceNode.(WrapPathNode).inner().getNode()) and
|
||||
edgesWithoutGlobals+(sourceNode, sinkNode) and
|
||||
tainted = sinkNode.inner()
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -621,6 +621,23 @@ private predicate parameterFlowThroughAllowed(ParamNodeEx p, ReturnKindExt kind)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if flow from a parameter at position `pos` inside `c` to a return node of
|
||||
* kind `kind` is allowed.
|
||||
*
|
||||
* We don't expect a parameter to return stored in itself, unless
|
||||
* explicitly allowed
|
||||
*/
|
||||
bindingset[c, pos, kind]
|
||||
private predicate parameterFlowThroughAllowed(
|
||||
DataFlowCallable c, ParameterPosition pos, ReturnKindExt kind
|
||||
) {
|
||||
exists(ParamNodeEx p |
|
||||
p.isParameterOf(c, pos) and
|
||||
parameterFlowThroughAllowed(p, kind)
|
||||
)
|
||||
}
|
||||
|
||||
private module Stage1 implements StageSig {
|
||||
class Ap = Unit;
|
||||
|
||||
@@ -963,12 +980,6 @@ private module Stage1 implements StageSig {
|
||||
pragma[nomagic]
|
||||
predicate revFlow(NodeEx node, Configuration config) { revFlow(node, _, config) }
|
||||
|
||||
pragma[nomagic]
|
||||
predicate revFlowAp(NodeEx node, Ap ap, Configuration config) {
|
||||
revFlow(node, config) and
|
||||
exists(ap)
|
||||
}
|
||||
|
||||
bindingset[node, state, config]
|
||||
predicate revFlow(NodeEx node, FlowState state, Ap ap, Configuration config) {
|
||||
revFlow(node, _, pragma[only_bind_into](config)) and
|
||||
@@ -1011,13 +1022,9 @@ private module Stage1 implements StageSig {
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
predicate returnMayFlowThrough(
|
||||
RetNodeEx ret, Ap argAp, Ap ap, ReturnKindExt kind, Configuration config
|
||||
) {
|
||||
predicate returnMayFlowThrough(RetNodeEx ret, ReturnKindExt kind, Configuration config) {
|
||||
throughFlowNodeCand(ret, config) and
|
||||
kind = ret.getKind() and
|
||||
exists(argAp) and
|
||||
exists(ap)
|
||||
kind = ret.getKind()
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
@@ -1182,8 +1189,6 @@ private signature module StageSig {
|
||||
|
||||
predicate revFlow(NodeEx node, Configuration config);
|
||||
|
||||
predicate revFlowAp(NodeEx node, Ap ap, Configuration config);
|
||||
|
||||
bindingset[node, state, config]
|
||||
predicate revFlow(NodeEx node, FlowState state, Ap ap, Configuration config);
|
||||
|
||||
@@ -1191,9 +1196,7 @@ private signature module StageSig {
|
||||
|
||||
predicate parameterMayFlowThrough(ParamNodeEx p, Ap ap, Configuration config);
|
||||
|
||||
predicate returnMayFlowThrough(
|
||||
RetNodeEx ret, Ap argAp, Ap ap, ReturnKindExt kind, Configuration config
|
||||
);
|
||||
predicate returnMayFlowThrough(RetNodeEx ret, ReturnKindExt kind, Configuration config);
|
||||
|
||||
predicate storeStepCand(
|
||||
NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, DataFlowType contentType,
|
||||
@@ -1278,43 +1281,21 @@ private module MkStage<StageSig PrevStage> {
|
||||
import Param
|
||||
|
||||
/* Begin: Stage logic. */
|
||||
// use an alias as a workaround for bad functionality-induced joins
|
||||
pragma[nomagic]
|
||||
private predicate revFlowApAlias(NodeEx node, ApApprox apa, Configuration config) {
|
||||
PrevStage::revFlowAp(node, apa, config)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate flowIntoCallApa(
|
||||
DataFlowCall call, ArgNodeEx arg, ParamNodeEx p, boolean allowsFieldFlow, ApApprox apa,
|
||||
Configuration config
|
||||
) {
|
||||
flowIntoCall(call, arg, p, allowsFieldFlow, config) and
|
||||
PrevStage::revFlowAp(p, pragma[only_bind_into](apa), pragma[only_bind_into](config)) and
|
||||
revFlowApAlias(arg, pragma[only_bind_into](apa), pragma[only_bind_into](config))
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate flowOutOfCallApa(
|
||||
DataFlowCall call, RetNodeEx ret, ReturnKindExt kind, NodeEx out, boolean allowsFieldFlow,
|
||||
ApApprox apa, Configuration config
|
||||
) {
|
||||
flowOutOfCall(call, ret, kind, out, allowsFieldFlow, config) and
|
||||
PrevStage::revFlowAp(out, pragma[only_bind_into](apa), pragma[only_bind_into](config)) and
|
||||
revFlowApAlias(ret, pragma[only_bind_into](apa), pragma[only_bind_into](config))
|
||||
bindingset[result, apa]
|
||||
private ApApprox unbindApa(ApApprox apa) {
|
||||
pragma[only_bind_out](apa) = pragma[only_bind_out](result)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate flowThroughOutOfCall(
|
||||
DataFlowCall call, CcCall ccc, RetNodeEx ret, NodeEx out, boolean allowsFieldFlow,
|
||||
ApApprox argApa, ApApprox apa, Configuration config
|
||||
DataFlowCall call, DataFlowCallable c, CcCall ccc, RetNodeEx ret, ReturnKindExt kind,
|
||||
NodeEx out, boolean allowsFieldFlow, Configuration config
|
||||
) {
|
||||
exists(ReturnKindExt kind |
|
||||
flowOutOfCallApa(call, ret, kind, out, allowsFieldFlow, apa, pragma[only_bind_into](config)) and
|
||||
PrevStage::callMayFlowThroughRev(call, pragma[only_bind_into](config)) and
|
||||
PrevStage::returnMayFlowThrough(ret, argApa, apa, kind, pragma[only_bind_into](config)) and
|
||||
matchesCall(ccc, call)
|
||||
)
|
||||
flowOutOfCall(call, ret, kind, out, allowsFieldFlow, pragma[only_bind_into](config)) and
|
||||
PrevStage::callMayFlowThroughRev(call, pragma[only_bind_into](config)) and
|
||||
PrevStage::returnMayFlowThrough(ret, kind, pragma[only_bind_into](config)) and
|
||||
matchesCall(ccc, call) and
|
||||
c = ret.getEnclosingCallable()
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1326,50 +1307,39 @@ private module MkStage<StageSig PrevStage> {
|
||||
* corresponding parameter position and access path of that argument, respectively.
|
||||
*/
|
||||
pragma[nomagic]
|
||||
additional predicate fwdFlow(
|
||||
NodeEx node, FlowState state, Cc cc, ParameterPositionOption summaryCtx, ApOption argAp,
|
||||
Ap ap, ApApprox apa, Configuration config
|
||||
) {
|
||||
fwdFlow0(node, state, cc, summaryCtx, argAp, ap, apa, config) and
|
||||
PrevStage::revFlow(node, state, apa, config) and
|
||||
filter(node, state, ap, config)
|
||||
}
|
||||
|
||||
pragma[inline]
|
||||
additional predicate fwdFlow(
|
||||
NodeEx node, FlowState state, Cc cc, ParameterPositionOption summaryCtx, ApOption argAp,
|
||||
Ap ap, Configuration config
|
||||
) {
|
||||
fwdFlow(node, state, cc, summaryCtx, argAp, ap, _, config)
|
||||
fwdFlow0(node, state, cc, summaryCtx, argAp, ap, config) and
|
||||
PrevStage::revFlow(node, state, unbindApa(getApprox(ap)), config) and
|
||||
filter(node, state, ap, config)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate fwdFlow0(
|
||||
NodeEx node, FlowState state, Cc cc, ParameterPositionOption summaryCtx, ApOption argAp,
|
||||
Ap ap, ApApprox apa, Configuration config
|
||||
Ap ap, Configuration config
|
||||
) {
|
||||
sourceNode(node, state, config) and
|
||||
(if hasSourceCallCtx(config) then cc = ccSomeCall() else cc = ccNone()) and
|
||||
argAp = apNone() and
|
||||
summaryCtx = TParameterPositionNone() and
|
||||
ap = getApNil(node) and
|
||||
apa = getApprox(ap)
|
||||
ap = getApNil(node)
|
||||
or
|
||||
exists(NodeEx mid, FlowState state0, Ap ap0, ApApprox apa0, LocalCc localCc |
|
||||
fwdFlow(mid, state0, cc, summaryCtx, argAp, ap0, apa0, config) and
|
||||
exists(NodeEx mid, FlowState state0, Ap ap0, LocalCc localCc |
|
||||
fwdFlow(mid, state0, cc, summaryCtx, argAp, ap0, config) and
|
||||
localCc = getLocalCc(mid, cc)
|
||||
|
|
||||
localStep(mid, state0, node, state, true, _, config, localCc) and
|
||||
ap = ap0 and
|
||||
apa = apa0
|
||||
ap = ap0
|
||||
or
|
||||
localStep(mid, state0, node, state, false, ap, config, localCc) and
|
||||
ap0 instanceof ApNil and
|
||||
apa = getApprox(ap)
|
||||
ap0 instanceof ApNil
|
||||
)
|
||||
or
|
||||
exists(NodeEx mid |
|
||||
fwdFlow(mid, pragma[only_bind_into](state), _, _, _, ap, apa, pragma[only_bind_into](config)) and
|
||||
fwdFlow(mid, pragma[only_bind_into](state), _, _, _, ap, pragma[only_bind_into](config)) and
|
||||
jumpStep(mid, node, config) and
|
||||
cc = ccNone() and
|
||||
summaryCtx = TParameterPositionNone() and
|
||||
@@ -1382,8 +1352,7 @@ private module MkStage<StageSig PrevStage> {
|
||||
cc = ccNone() and
|
||||
summaryCtx = TParameterPositionNone() and
|
||||
argAp = apNone() and
|
||||
ap = getApNil(node) and
|
||||
apa = getApprox(ap)
|
||||
ap = getApNil(node)
|
||||
)
|
||||
or
|
||||
exists(NodeEx mid, FlowState state0, ApNil nil |
|
||||
@@ -1392,32 +1361,32 @@ private module MkStage<StageSig PrevStage> {
|
||||
cc = ccNone() and
|
||||
summaryCtx = TParameterPositionNone() and
|
||||
argAp = apNone() and
|
||||
ap = getApNil(node) and
|
||||
apa = getApprox(ap)
|
||||
ap = getApNil(node)
|
||||
)
|
||||
or
|
||||
// store
|
||||
exists(TypedContent tc, Ap ap0 |
|
||||
fwdFlowStore(_, ap0, tc, node, state, cc, summaryCtx, argAp, config) and
|
||||
ap = apCons(tc, ap0) and
|
||||
apa = getApprox(ap)
|
||||
ap = apCons(tc, ap0)
|
||||
)
|
||||
or
|
||||
// read
|
||||
exists(Ap ap0, Content c |
|
||||
fwdFlowRead(ap0, c, _, node, state, cc, summaryCtx, argAp, config) and
|
||||
fwdFlowConsCand(ap0, c, ap, config) and
|
||||
apa = getApprox(ap)
|
||||
fwdFlowConsCand(ap0, c, ap, config)
|
||||
)
|
||||
or
|
||||
// flow into a callable
|
||||
fwdFlowIn(_, node, state, _, cc, _, _, ap, apa, config) and
|
||||
if PrevStage::parameterMayFlowThrough(node, apa, config)
|
||||
then (
|
||||
summaryCtx = TParameterPositionSome(node.(ParamNodeEx).getPosition()) and
|
||||
argAp = apSome(ap)
|
||||
) else (
|
||||
summaryCtx = TParameterPositionNone() and argAp = apNone()
|
||||
exists(ApApprox apa |
|
||||
fwdFlowIn(_, node, state, _, cc, _, _, ap, config) and
|
||||
apa = getApprox(ap) and
|
||||
if PrevStage::parameterMayFlowThrough(node, apa, config)
|
||||
then (
|
||||
summaryCtx = TParameterPositionSome(node.(ParamNodeEx).getPosition()) and
|
||||
argAp = apSome(ap)
|
||||
) else (
|
||||
summaryCtx = TParameterPositionNone() and argAp = apNone()
|
||||
)
|
||||
)
|
||||
or
|
||||
// flow out of a callable
|
||||
@@ -1425,8 +1394,8 @@ private module MkStage<StageSig PrevStage> {
|
||||
DataFlowCall call, RetNodeEx ret, boolean allowsFieldFlow, CcNoCall innercc,
|
||||
DataFlowCallable inner
|
||||
|
|
||||
fwdFlow(ret, state, innercc, summaryCtx, argAp, ap, apa, config) and
|
||||
flowOutOfCallApa(call, ret, _, node, allowsFieldFlow, apa, config) and
|
||||
fwdFlow(ret, state, innercc, summaryCtx, argAp, ap, config) and
|
||||
flowOutOfCall(call, ret, _, node, allowsFieldFlow, config) and
|
||||
inner = ret.getEnclosingCallable() and
|
||||
cc = getCallContextReturn(inner, call, innercc) and
|
||||
if allowsFieldFlow = false then ap instanceof ApNil else any()
|
||||
@@ -1434,7 +1403,7 @@ private module MkStage<StageSig PrevStage> {
|
||||
or
|
||||
// flow through a callable
|
||||
exists(DataFlowCall call, ParameterPosition summaryCtx0, Ap argAp0 |
|
||||
fwdFlowOutFromArg(call, node, state, summaryCtx0, argAp0, ap, apa, config) and
|
||||
fwdFlowOutFromArg(call, node, state, summaryCtx0, argAp0, ap, config) and
|
||||
fwdFlowIsEntered(call, cc, summaryCtx, argAp, summaryCtx0, argAp0, config)
|
||||
)
|
||||
}
|
||||
@@ -1444,9 +1413,9 @@ private module MkStage<StageSig PrevStage> {
|
||||
NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, FlowState state, Cc cc,
|
||||
ParameterPositionOption summaryCtx, ApOption argAp, Configuration config
|
||||
) {
|
||||
exists(DataFlowType contentType, ApApprox apa1 |
|
||||
fwdFlow(node1, state, cc, summaryCtx, argAp, ap1, apa1, config) and
|
||||
PrevStage::storeStepCand(node1, apa1, tc, node2, contentType, config) and
|
||||
exists(DataFlowType contentType |
|
||||
fwdFlow(node1, state, cc, summaryCtx, argAp, ap1, config) and
|
||||
PrevStage::storeStepCand(node1, unbindApa(getApprox(ap1)), tc, node2, contentType, config) and
|
||||
typecheckStore(ap1, contentType)
|
||||
)
|
||||
}
|
||||
@@ -1464,104 +1433,43 @@ private module MkStage<StageSig PrevStage> {
|
||||
)
|
||||
}
|
||||
|
||||
private class ApNonNil instanceof Ap {
|
||||
pragma[nomagic]
|
||||
ApNonNil() { not this instanceof ApNil }
|
||||
|
||||
string toString() { result = "" }
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate fwdFlowRead0(
|
||||
NodeEx node1, FlowState state, Cc cc, ParameterPositionOption summaryCtx, ApOption argAp,
|
||||
ApNonNil ap, Configuration config
|
||||
) {
|
||||
fwdFlow(node1, state, cc, summaryCtx, argAp, ap, config) and
|
||||
PrevStage::readStepCand(node1, _, _, config)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate fwdFlowRead(
|
||||
Ap ap, Content c, NodeEx node1, NodeEx node2, FlowState state, Cc cc,
|
||||
ParameterPositionOption summaryCtx, ApOption argAp, Configuration config
|
||||
) {
|
||||
fwdFlowRead0(node1, state, cc, summaryCtx, argAp, ap, config) and
|
||||
fwdFlow(node1, state, cc, summaryCtx, argAp, ap, config) and
|
||||
PrevStage::readStepCand(node1, c, node2, config) and
|
||||
getHeadContent(ap) = c
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate fwdFlowIn(
|
||||
DataFlowCall call, ParamNodeEx p, FlowState state, Cc outercc, CcCall innercc,
|
||||
ParameterPositionOption summaryCtx, ApOption argAp, Ap ap, ApApprox apa, Configuration config
|
||||
DataFlowCall call, ParamNodeEx p, FlowState state, Cc outercc, Cc innercc,
|
||||
ParameterPositionOption summaryCtx, ApOption argAp, Ap ap, Configuration config
|
||||
) {
|
||||
exists(ArgNodeEx arg, boolean allowsFieldFlow |
|
||||
fwdFlow(arg, state, outercc, summaryCtx, argAp, ap, apa, config) and
|
||||
flowIntoCallApa(call, arg, p, allowsFieldFlow, apa, config) and
|
||||
fwdFlow(arg, state, outercc, summaryCtx, argAp, ap, config) and
|
||||
flowIntoCall(call, arg, p, allowsFieldFlow, config) and
|
||||
innercc = getCallContextCall(call, p.getEnclosingCallable(), outercc) and
|
||||
if allowsFieldFlow = false then ap instanceof ApNil else any()
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate fwdFlowRetFromArg(
|
||||
RetNodeEx ret, FlowState state, CcCall ccc, ParameterPosition summaryCtx, ParamNodeEx p,
|
||||
Ap argAp, ApApprox argApa, Ap ap, ApApprox apa, Configuration config
|
||||
) {
|
||||
exists(DataFlowCallable c, ReturnKindExt kind |
|
||||
fwdFlow(pragma[only_bind_into](ret), state, ccc,
|
||||
TParameterPositionSome(pragma[only_bind_into](summaryCtx)), apSome(argAp), ap, apa, config) and
|
||||
getApprox(argAp) = argApa and
|
||||
c = ret.getEnclosingCallable() and
|
||||
kind = ret.getKind() and
|
||||
p.isParameterOf(c, pragma[only_bind_into](summaryCtx)) and
|
||||
parameterFlowThroughAllowed(p, kind)
|
||||
)
|
||||
}
|
||||
|
||||
pragma[inline]
|
||||
private predicate fwdFlowInMayFlowThrough(
|
||||
DataFlowCall call, Cc cc, CcCall innerCc, ParameterPositionOption summaryCtx, ApOption argAp,
|
||||
ParamNodeEx param, Ap ap, ApApprox apa, Configuration config
|
||||
) {
|
||||
fwdFlowIn(call, pragma[only_bind_into](param), _, cc, innerCc, summaryCtx, argAp, ap,
|
||||
pragma[only_bind_into](apa), pragma[only_bind_into](config)) and
|
||||
PrevStage::parameterMayFlowThrough(param, apa, config)
|
||||
}
|
||||
|
||||
// dedup before joining with `flowThroughOutOfCall`
|
||||
pragma[nomagic]
|
||||
private predicate fwdFlowInMayFlowThroughProj(
|
||||
DataFlowCall call, CcCall innerCc, ApApprox apa, Configuration config
|
||||
) {
|
||||
fwdFlowInMayFlowThrough(call, _, innerCc, _, _, _, _, apa, config)
|
||||
}
|
||||
|
||||
/**
|
||||
* Same as `flowThroughOutOfCall`, but restricted to calls that are reached
|
||||
* in the flow covered by `fwdFlow`, where data might flow through the target
|
||||
* callable and back out at `call`.
|
||||
*/
|
||||
pragma[nomagic]
|
||||
private predicate fwdFlowThroughOutOfCall(
|
||||
DataFlowCall call, CcCall ccc, RetNodeEx ret, NodeEx out, boolean allowsFieldFlow,
|
||||
ApApprox argApa, ApApprox apa, Configuration config
|
||||
) {
|
||||
fwdFlowInMayFlowThroughProj(call, ccc, argApa, config) and
|
||||
flowThroughOutOfCall(call, ccc, ret, out, allowsFieldFlow, argApa, apa, config)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate fwdFlowOutFromArg(
|
||||
DataFlowCall call, NodeEx out, FlowState state, ParameterPosition summaryCtx, Ap argAp, Ap ap,
|
||||
ApApprox apa, Configuration config
|
||||
Configuration config
|
||||
) {
|
||||
exists(RetNodeEx ret, boolean allowsFieldFlow, CcCall ccc, ApApprox argApa |
|
||||
fwdFlowRetFromArg(pragma[only_bind_into](ret), state, pragma[only_bind_into](ccc),
|
||||
summaryCtx, _, argAp, pragma[only_bind_into](argApa), ap, pragma[only_bind_into](apa),
|
||||
exists(
|
||||
DataFlowCallable c, RetNodeEx ret, ReturnKindExt kind, boolean allowsFieldFlow, CcCall ccc
|
||||
|
|
||||
fwdFlow(pragma[only_bind_into](ret), state, pragma[only_bind_into](ccc),
|
||||
TParameterPositionSome(pragma[only_bind_into](summaryCtx)), apSome(argAp), ap, config) and
|
||||
flowThroughOutOfCall(call, pragma[only_bind_into](c), ccc, ret, kind, out, allowsFieldFlow,
|
||||
config) and
|
||||
fwdFlowThroughOutOfCall(call, ccc, ret, out, allowsFieldFlow, argApa, apa, config) and
|
||||
(if allowsFieldFlow = false then ap instanceof ApNil else any())
|
||||
(if allowsFieldFlow = false then ap instanceof ApNil else any()) and
|
||||
parameterFlowThroughAllowed(c, pragma[only_bind_into](summaryCtx), kind)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1575,7 +1483,8 @@ private module MkStage<StageSig PrevStage> {
|
||||
ParameterPosition pos, Ap ap, Configuration config
|
||||
) {
|
||||
exists(ParamNodeEx param |
|
||||
fwdFlowInMayFlowThrough(call, cc, _, summaryCtx, argAp, param, ap, _, config) and
|
||||
fwdFlowIn(call, param, _, cc, _, summaryCtx, argAp, ap, config) and
|
||||
PrevStage::parameterMayFlowThrough(param, unbindApa(getApprox(ap)), config) and
|
||||
pos = param.getPosition()
|
||||
)
|
||||
}
|
||||
@@ -1596,55 +1505,41 @@ private module MkStage<StageSig PrevStage> {
|
||||
fwdFlowConsCand(ap1, c, ap2, config)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate returnFlowsThrough0(
|
||||
RetNodeEx ret, ReturnKindExt kind, FlowState state, CcCall ccc, DataFlowCallable c,
|
||||
ParameterPosition ppos, Ap argAp, Ap ap, Configuration config
|
||||
) {
|
||||
exists(boolean allowsFieldFlow |
|
||||
fwdFlow(ret, state, ccc, TParameterPositionSome(ppos), apSome(argAp), ap, config) and
|
||||
flowThroughOutOfCall(_, c, _, pragma[only_bind_into](ret), kind, _, allowsFieldFlow,
|
||||
pragma[only_bind_into](config)) and
|
||||
if allowsFieldFlow = false then ap instanceof ApNil else any()
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate returnFlowsThrough(
|
||||
RetNodeEx ret, ReturnKindExt kind, FlowState state, CcCall ccc, ParamNodeEx p, Ap argAp,
|
||||
Ap ap, Configuration config
|
||||
) {
|
||||
exists(boolean allowsFieldFlow, ApApprox argApa, ApApprox apa |
|
||||
fwdFlowRetFromArg(pragma[only_bind_into](ret), state, pragma[only_bind_into](ccc), _, p,
|
||||
argAp, pragma[only_bind_into](argApa), ap, pragma[only_bind_into](apa), config) and
|
||||
kind = ret.getKind() and
|
||||
fwdFlowThroughOutOfCall(_, ccc, ret, _, allowsFieldFlow, argApa, apa, config) and
|
||||
(if allowsFieldFlow = false then ap instanceof ApNil else any())
|
||||
exists(DataFlowCallable c, ParameterPosition ppos |
|
||||
returnFlowsThrough0(ret, kind, state, ccc, c, ppos, argAp, ap, config) and
|
||||
p.isParameterOf(c, ppos) and
|
||||
parameterFlowThroughAllowed(p, kind)
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate flowThroughIntoCall(
|
||||
DataFlowCall call, ArgNodeEx arg, ParamNodeEx p, boolean allowsFieldFlow, Ap argAp,
|
||||
Configuration config
|
||||
DataFlowCall call, ArgNodeEx arg, ParamNodeEx p, boolean allowsFieldFlow, Configuration config
|
||||
) {
|
||||
exists(ApApprox argApa |
|
||||
flowIntoCallApa(call, pragma[only_bind_into](arg), pragma[only_bind_into](p),
|
||||
allowsFieldFlow, argApa, pragma[only_bind_into](config)) and
|
||||
fwdFlow(arg, _, _, _, _, pragma[only_bind_into](argAp), argApa,
|
||||
exists(Ap argAp |
|
||||
flowIntoCall(call, pragma[only_bind_into](arg), pragma[only_bind_into](p), allowsFieldFlow,
|
||||
pragma[only_bind_into](config)) and
|
||||
fwdFlow(arg, _, _, _, _, pragma[only_bind_into](argAp), pragma[only_bind_into](config)) and
|
||||
returnFlowsThrough(_, _, _, _, p, pragma[only_bind_into](argAp), _,
|
||||
pragma[only_bind_into](config)) and
|
||||
if allowsFieldFlow = false then argAp instanceof ApNil else any()
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate flowIntoCallAp(
|
||||
DataFlowCall call, ArgNodeEx arg, ParamNodeEx p, boolean allowsFieldFlow, Ap ap,
|
||||
Configuration config
|
||||
) {
|
||||
exists(ApApprox apa |
|
||||
flowIntoCallApa(call, arg, p, allowsFieldFlow, apa, config) and
|
||||
fwdFlow(arg, _, _, _, _, ap, apa, config)
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate flowOutOfCallAp(
|
||||
DataFlowCall call, RetNodeEx ret, ReturnKindExt kind, NodeEx out, boolean allowsFieldFlow,
|
||||
Ap ap, Configuration config
|
||||
) {
|
||||
exists(ApApprox apa |
|
||||
flowOutOfCallApa(call, ret, kind, out, allowsFieldFlow, apa, config) and
|
||||
fwdFlow(ret, _, _, _, _, ap, apa, config)
|
||||
pragma[only_bind_into](config))
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1733,7 +1628,7 @@ private module MkStage<StageSig PrevStage> {
|
||||
// flow into a callable
|
||||
exists(ParamNodeEx p, boolean allowsFieldFlow |
|
||||
revFlow(p, state, TReturnCtxNone(), returnAp, ap, config) and
|
||||
flowIntoCallAp(_, node, p, allowsFieldFlow, ap, config) and
|
||||
flowIntoCall(_, node, p, allowsFieldFlow, config) and
|
||||
(if allowsFieldFlow = false then ap instanceof ApNil else any()) and
|
||||
returnCtx = TReturnCtxNone()
|
||||
)
|
||||
@@ -1787,41 +1682,22 @@ private module MkStage<StageSig PrevStage> {
|
||||
) {
|
||||
exists(NodeEx out, boolean allowsFieldFlow |
|
||||
revFlow(out, state, returnCtx, returnAp, ap, config) and
|
||||
flowOutOfCallAp(call, ret, kind, out, allowsFieldFlow, ap, config) and
|
||||
flowOutOfCall(call, ret, kind, out, allowsFieldFlow, config) and
|
||||
if allowsFieldFlow = false then ap instanceof ApNil else any()
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Same as `flowThroughIntoCall`, but restricted to calls that are reached
|
||||
* in the flow covered by `revFlow`, where data might flow through the target
|
||||
* callable and back out at `call`.
|
||||
*/
|
||||
pragma[nomagic]
|
||||
private predicate revFlowThroughIntoCall(
|
||||
DataFlowCall call, ArgNodeEx arg, ParamNodeEx p, boolean allowsFieldFlow, Ap argAp,
|
||||
Configuration config
|
||||
) {
|
||||
flowThroughIntoCall(call, arg, p, allowsFieldFlow, argAp, config) and
|
||||
revFlowIsReturned(call, _, _, _, _, config)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate revFlowParamToReturn(
|
||||
ParamNodeEx p, FlowState state, ReturnKindExt kind, Ap returnAp, Ap ap, Configuration config
|
||||
) {
|
||||
revFlow(p, state, TReturnCtxMaybeFlowThrough(kind), apSome(returnAp), ap, config) and
|
||||
parameterFlowThroughAllowed(p, kind)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate revFlowInToReturn(
|
||||
DataFlowCall call, ArgNodeEx arg, FlowState state, ReturnKindExt kind, Ap returnAp, Ap ap,
|
||||
Configuration config
|
||||
) {
|
||||
exists(ParamNodeEx p, boolean allowsFieldFlow |
|
||||
revFlowParamToReturn(p, state, kind, returnAp, ap, config) and
|
||||
revFlowThroughIntoCall(call, arg, p, allowsFieldFlow, ap, config)
|
||||
revFlow(pragma[only_bind_into](p), state,
|
||||
TReturnCtxMaybeFlowThrough(pragma[only_bind_into](kind)), apSome(returnAp), ap, config) and
|
||||
flowThroughIntoCall(call, arg, p, allowsFieldFlow, config) and
|
||||
(if allowsFieldFlow = false then ap instanceof ApNil else any()) and
|
||||
parameterFlowThroughAllowed(p, kind)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1874,11 +1750,6 @@ private module MkStage<StageSig PrevStage> {
|
||||
pragma[nomagic]
|
||||
predicate revFlow(NodeEx node, Configuration config) { revFlow(node, _, _, _, _, config) }
|
||||
|
||||
pragma[nomagic]
|
||||
predicate revFlowAp(NodeEx node, Ap ap, Configuration config) {
|
||||
revFlow(node, _, _, _, ap, config)
|
||||
}
|
||||
|
||||
// use an alias as a workaround for bad functionality-induced joins
|
||||
pragma[nomagic]
|
||||
additional predicate revFlowAlias(NodeEx node, Configuration config) {
|
||||
@@ -1915,9 +1786,9 @@ private module MkStage<StageSig PrevStage> {
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate parameterFlowsThroughRev(
|
||||
ParamNodeEx p, Ap ap, ReturnKindExt kind, Ap returnAp, Configuration config
|
||||
ParamNodeEx p, Ap ap, ReturnKindExt kind, Configuration config
|
||||
) {
|
||||
revFlow(p, _, TReturnCtxMaybeFlowThrough(kind), apSome(returnAp), ap, config) and
|
||||
revFlow(p, _, TReturnCtxMaybeFlowThrough(kind), apSome(_), ap, config) and
|
||||
parameterFlowThroughAllowed(p, kind)
|
||||
}
|
||||
|
||||
@@ -1925,36 +1796,27 @@ private module MkStage<StageSig PrevStage> {
|
||||
predicate parameterMayFlowThrough(ParamNodeEx p, Ap ap, Configuration config) {
|
||||
exists(RetNodeEx ret, ReturnKindExt kind |
|
||||
returnFlowsThrough(ret, kind, _, _, p, ap, _, config) and
|
||||
parameterFlowsThroughRev(p, ap, kind, _, config)
|
||||
parameterFlowsThroughRev(p, ap, kind, config)
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
predicate returnMayFlowThrough(
|
||||
RetNodeEx ret, Ap argAp, Ap ap, ReturnKindExt kind, Configuration config
|
||||
) {
|
||||
exists(ParamNodeEx p |
|
||||
returnFlowsThrough(ret, kind, _, _, p, argAp, ap, config) and
|
||||
parameterFlowsThroughRev(p, argAp, kind, ap, config)
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
predicate revFlowInToReturnIsReturned(
|
||||
DataFlowCall call, ArgNodeEx arg, FlowState state, ReturnCtx returnCtx, ApOption returnAp,
|
||||
Ap ap, Configuration config
|
||||
) {
|
||||
exists(ReturnKindExt returnKind0, Ap returnAp0 |
|
||||
revFlowInToReturn(call, arg, state, returnKind0, returnAp0, ap, config) and
|
||||
revFlowIsReturned(call, returnCtx, returnAp, returnKind0, returnAp0, config)
|
||||
predicate returnMayFlowThrough(RetNodeEx ret, ReturnKindExt kind, Configuration config) {
|
||||
exists(ParamNodeEx p, Ap ap |
|
||||
returnFlowsThrough(ret, kind, _, _, p, ap, _, config) and
|
||||
parameterFlowsThroughRev(p, ap, kind, config)
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
predicate callMayFlowThroughRev(DataFlowCall call, Configuration config) {
|
||||
exists(ArgNodeEx arg, FlowState state, ReturnCtx returnCtx, ApOption returnAp, Ap ap |
|
||||
exists(
|
||||
ReturnKindExt returnKind0, Ap returnAp0, ArgNodeEx arg, FlowState state,
|
||||
ReturnCtx returnCtx, ApOption returnAp, Ap ap
|
||||
|
|
||||
revFlow(arg, state, returnCtx, returnAp, ap, config) and
|
||||
revFlowInToReturnIsReturned(call, arg, state, returnCtx, returnAp, ap, config)
|
||||
revFlowInToReturn(call, arg, state, returnKind0, returnAp0, ap, config) and
|
||||
revFlowIsReturned(call, returnCtx, returnAp, returnKind0, returnAp0, config)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -2317,16 +2179,16 @@ private module LocalFlowBigStep {
|
||||
pragma[nomagic]
|
||||
predicate localFlowBigStep(
|
||||
NodeEx node1, FlowState state1, NodeEx node2, FlowState state2, boolean preservesValue,
|
||||
DataFlowType t, Configuration config, LocalCallContext callContext
|
||||
AccessPathFrontNil apf, Configuration config, LocalCallContext callContext
|
||||
) {
|
||||
localFlowStepPlus(node1, state1, node2, preservesValue, t, config, callContext) and
|
||||
localFlowStepPlus(node1, state1, node2, preservesValue, apf.getType(), config, callContext) and
|
||||
localFlowExit(node2, state1, config) and
|
||||
state1 = state2
|
||||
or
|
||||
additionalLocalFlowStepNodeCand2(node1, state1, node2, state2, config) and
|
||||
state1 != state2 and
|
||||
preservesValue = false and
|
||||
t = node2.getDataFlowType() and
|
||||
apf = TFrontNil(node2.getDataFlowType()) and
|
||||
callContext.relevantFor(node1.getEnclosingCallable()) and
|
||||
not exists(DataFlowCall call | call = callContext.(LocalCallContextSpecificCall).getCall() |
|
||||
isUnreachableInCallCached(node1.asNode(), call) or
|
||||
@@ -2340,87 +2202,11 @@ private import LocalFlowBigStep
|
||||
private module Stage3Param implements MkStage<Stage2>::StageParam {
|
||||
private module PrevStage = Stage2;
|
||||
|
||||
class Ap = ApproxAccessPathFront;
|
||||
|
||||
class ApNil = ApproxAccessPathFrontNil;
|
||||
|
||||
PrevStage::Ap getApprox(Ap ap) { result = ap.toBoolNonEmpty() }
|
||||
|
||||
ApNil getApNil(NodeEx node) {
|
||||
PrevStage::revFlow(node, _) and result = TApproxFrontNil(node.getDataFlowType())
|
||||
}
|
||||
|
||||
bindingset[tc, tail]
|
||||
Ap apCons(TypedContent tc, Ap tail) { result.getAHead() = tc and exists(tail) }
|
||||
|
||||
pragma[noinline]
|
||||
Content getHeadContent(Ap ap) { result = ap.getAHead().getContent() }
|
||||
|
||||
class ApOption = ApproxAccessPathFrontOption;
|
||||
|
||||
ApOption apNone() { result = TApproxAccessPathFrontNone() }
|
||||
|
||||
ApOption apSome(Ap ap) { result = TApproxAccessPathFrontSome(ap) }
|
||||
|
||||
import BooleanCallContext
|
||||
|
||||
predicate localStep(
|
||||
NodeEx node1, FlowState state1, NodeEx node2, FlowState state2, boolean preservesValue,
|
||||
ApproxAccessPathFrontNil ap, Configuration config, LocalCc lcc
|
||||
) {
|
||||
localFlowBigStep(node1, state1, node2, state2, preservesValue, ap.getType(), config, _) and
|
||||
exists(lcc)
|
||||
}
|
||||
|
||||
predicate flowOutOfCall = flowOutOfCallNodeCand2/6;
|
||||
|
||||
predicate flowIntoCall = flowIntoCallNodeCand2/5;
|
||||
|
||||
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.getAHead().getContent()
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate castingNodeEx(NodeEx node) { node.asNode() instanceof CastingNode }
|
||||
|
||||
bindingset[node, state, ap, config]
|
||||
predicate filter(NodeEx node, FlowState state, Ap ap, Configuration config) {
|
||||
exists(state) and
|
||||
exists(config) and
|
||||
(if castingNodeEx(node) then compatibleTypes(node.getDataFlowType(), ap.getType()) else any()) and
|
||||
(
|
||||
notExpectsContent(node)
|
||||
or
|
||||
expectsContentCand(node, ap, config)
|
||||
)
|
||||
}
|
||||
|
||||
bindingset[ap, contentType]
|
||||
predicate typecheckStore(Ap ap, DataFlowType contentType) {
|
||||
// We need to typecheck stores here, since reverse flow through a getter
|
||||
// might have a different type here compared to inside the getter.
|
||||
compatibleTypes(ap.getType(), contentType)
|
||||
}
|
||||
}
|
||||
|
||||
private module Stage3 implements StageSig {
|
||||
import MkStage<Stage2>::Stage<Stage3Param>
|
||||
}
|
||||
|
||||
private module Stage4Param implements MkStage<Stage3>::StageParam {
|
||||
private module PrevStage = Stage3;
|
||||
|
||||
class Ap = AccessPathFront;
|
||||
|
||||
class ApNil = AccessPathFrontNil;
|
||||
|
||||
PrevStage::Ap getApprox(Ap ap) { result = ap.toApprox() }
|
||||
PrevStage::Ap getApprox(Ap ap) { result = ap.toBoolNonEmpty() }
|
||||
|
||||
ApNil getApNil(NodeEx node) {
|
||||
PrevStage::revFlow(node, _) and result = TFrontNil(node.getDataFlowType())
|
||||
@@ -2440,42 +2226,16 @@ private module Stage4Param implements MkStage<Stage3>::StageParam {
|
||||
|
||||
import BooleanCallContext
|
||||
|
||||
pragma[nomagic]
|
||||
predicate localStep(
|
||||
NodeEx node1, FlowState state1, NodeEx node2, FlowState state2, boolean preservesValue,
|
||||
ApNil ap, Configuration config, LocalCc lcc
|
||||
) {
|
||||
localFlowBigStep(node1, state1, node2, state2, preservesValue, ap.getType(), config, _) and
|
||||
PrevStage::revFlow(node1, pragma[only_bind_into](state1), _, pragma[only_bind_into](config)) and
|
||||
PrevStage::revFlowAlias(node2, pragma[only_bind_into](state2), _, pragma[only_bind_into](config)) and
|
||||
exists(lcc)
|
||||
localFlowBigStep(node1, state1, node2, state2, preservesValue, ap, config, _) and exists(lcc)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
predicate flowOutOfCall(
|
||||
DataFlowCall call, RetNodeEx node1, ReturnKindExt kind, NodeEx node2, boolean allowsFieldFlow,
|
||||
Configuration config
|
||||
) {
|
||||
exists(FlowState state |
|
||||
flowOutOfCallNodeCand2(call, node1, kind, node2, allowsFieldFlow, config) and
|
||||
PrevStage::revFlow(node2, pragma[only_bind_into](state), _, pragma[only_bind_into](config)) and
|
||||
PrevStage::revFlowAlias(node1, pragma[only_bind_into](state), _,
|
||||
pragma[only_bind_into](config))
|
||||
)
|
||||
}
|
||||
predicate flowOutOfCall = flowOutOfCallNodeCand2/6;
|
||||
|
||||
pragma[nomagic]
|
||||
predicate flowIntoCall(
|
||||
DataFlowCall call, ArgNodeEx node1, ParamNodeEx node2, boolean allowsFieldFlow,
|
||||
Configuration config
|
||||
) {
|
||||
exists(FlowState state |
|
||||
flowIntoCallNodeCand2(call, node1, node2, allowsFieldFlow, config) and
|
||||
PrevStage::revFlow(node2, pragma[only_bind_into](state), _, pragma[only_bind_into](config)) and
|
||||
PrevStage::revFlowAlias(node1, pragma[only_bind_into](state), _,
|
||||
pragma[only_bind_into](config))
|
||||
)
|
||||
}
|
||||
predicate flowIntoCall = flowIntoCallNodeCand2/5;
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate clearSet(NodeEx node, ContentSet c, Configuration config) {
|
||||
@@ -2531,8 +2291,8 @@ private module Stage4Param implements MkStage<Stage3>::StageParam {
|
||||
}
|
||||
}
|
||||
|
||||
private module Stage4 implements StageSig {
|
||||
import MkStage<Stage3>::Stage<Stage4Param>
|
||||
private module Stage3 implements StageSig {
|
||||
import MkStage<Stage2>::Stage<Stage3Param>
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -2543,8 +2303,8 @@ private predicate flowCandSummaryCtx(
|
||||
NodeEx node, FlowState state, AccessPathFront argApf, Configuration config
|
||||
) {
|
||||
exists(AccessPathFront apf |
|
||||
Stage4::revFlow(node, state, TReturnCtxMaybeFlowThrough(_), _, apf, config) and
|
||||
Stage4::fwdFlow(node, state, any(Stage4::CcCall ccc), _, TAccessPathFrontSome(argApf), apf,
|
||||
Stage3::revFlow(node, state, TReturnCtxMaybeFlowThrough(_), _, apf, config) and
|
||||
Stage3::fwdFlow(node, state, any(Stage3::CcCall ccc), _, TAccessPathFrontSome(argApf), apf,
|
||||
config)
|
||||
)
|
||||
}
|
||||
@@ -2555,10 +2315,10 @@ private predicate flowCandSummaryCtx(
|
||||
*/
|
||||
private predicate expensiveLen2unfolding(TypedContent tc, Configuration config) {
|
||||
exists(int tails, int nodes, int apLimit, int tupleLimit |
|
||||
tails = strictcount(AccessPathFront apf | Stage4::consCand(tc, apf, config)) and
|
||||
tails = strictcount(AccessPathFront apf | Stage3::consCand(tc, apf, config)) and
|
||||
nodes =
|
||||
strictcount(NodeEx n, FlowState state |
|
||||
Stage4::revFlow(n, state, any(AccessPathFrontHead apf | apf.getHead() = tc), config)
|
||||
Stage3::revFlow(n, state, any(AccessPathFrontHead apf | apf.getHead() = tc), config)
|
||||
or
|
||||
flowCandSummaryCtx(n, state, any(AccessPathFrontHead apf | apf.getHead() = tc), config)
|
||||
) and
|
||||
@@ -2572,11 +2332,11 @@ private predicate expensiveLen2unfolding(TypedContent tc, Configuration config)
|
||||
private newtype TAccessPathApprox =
|
||||
TNil(DataFlowType t) or
|
||||
TConsNil(TypedContent tc, DataFlowType t) {
|
||||
Stage4::consCand(tc, TFrontNil(t), _) and
|
||||
Stage3::consCand(tc, TFrontNil(t), _) and
|
||||
not expensiveLen2unfolding(tc, _)
|
||||
} or
|
||||
TConsCons(TypedContent tc1, TypedContent tc2, int len) {
|
||||
Stage4::consCand(tc1, TFrontHead(tc2), _) and
|
||||
Stage3::consCand(tc1, TFrontHead(tc2), _) and
|
||||
len in [2 .. accessPathLimit()] and
|
||||
not expensiveLen2unfolding(tc1, _)
|
||||
} or
|
||||
@@ -2706,7 +2466,7 @@ private class AccessPathApproxCons1 extends AccessPathApproxCons, TCons1 {
|
||||
override AccessPathApprox pop(TypedContent head) {
|
||||
head = tc and
|
||||
(
|
||||
exists(TypedContent tc2 | Stage4::consCand(tc, TFrontHead(tc2), _) |
|
||||
exists(TypedContent tc2 | Stage3::consCand(tc, TFrontHead(tc2), _) |
|
||||
result = TConsCons(tc2, _, len - 1)
|
||||
or
|
||||
len = 2 and
|
||||
@@ -2717,7 +2477,7 @@ private class AccessPathApproxCons1 extends AccessPathApproxCons, TCons1 {
|
||||
or
|
||||
exists(DataFlowType t |
|
||||
len = 1 and
|
||||
Stage4::consCand(tc, TFrontNil(t), _) and
|
||||
Stage3::consCand(tc, TFrontNil(t), _) and
|
||||
result = TNil(t)
|
||||
)
|
||||
)
|
||||
@@ -2742,14 +2502,13 @@ private class AccessPathApproxOption extends TAccessPathApproxOption {
|
||||
}
|
||||
}
|
||||
|
||||
private module Stage5Param implements MkStage<Stage4>::StageParam {
|
||||
private module PrevStage = Stage4;
|
||||
private module Stage4Param implements MkStage<Stage3>::StageParam {
|
||||
private module PrevStage = Stage3;
|
||||
|
||||
class Ap = AccessPathApprox;
|
||||
|
||||
class ApNil = AccessPathApproxNil;
|
||||
|
||||
pragma[nomagic]
|
||||
PrevStage::Ap getApprox(Ap ap) { result = ap.getFront() }
|
||||
|
||||
ApNil getApNil(NodeEx node) {
|
||||
@@ -2775,9 +2534,7 @@ private module Stage5Param implements MkStage<Stage4>::StageParam {
|
||||
NodeEx node1, FlowState state1, NodeEx node2, FlowState state2, boolean preservesValue,
|
||||
ApNil ap, Configuration config, LocalCc lcc
|
||||
) {
|
||||
localFlowBigStep(node1, state1, node2, state2, preservesValue, ap.getType(), config, lcc) and
|
||||
PrevStage::revFlow(node1, pragma[only_bind_into](state1), _, pragma[only_bind_into](config)) and
|
||||
PrevStage::revFlowAlias(node2, pragma[only_bind_into](state2), _, pragma[only_bind_into](config))
|
||||
localFlowBigStep(node1, state1, node2, state2, preservesValue, ap.getFront(), config, lcc)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
@@ -2814,7 +2571,7 @@ private module Stage5Param implements MkStage<Stage4>::StageParam {
|
||||
predicate typecheckStore(Ap ap, DataFlowType contentType) { any() }
|
||||
}
|
||||
|
||||
private module Stage5 = MkStage<Stage4>::Stage<Stage5Param>;
|
||||
private module Stage4 = MkStage<Stage3>::Stage<Stage4Param>;
|
||||
|
||||
bindingset[conf, result]
|
||||
private Configuration unbindConf(Configuration conf) {
|
||||
@@ -2828,8 +2585,8 @@ private predicate nodeMayUseSummary0(
|
||||
) {
|
||||
exists(AccessPathApprox apa0 |
|
||||
c = n.getEnclosingCallable() and
|
||||
Stage5::revFlow(n, state, TReturnCtxMaybeFlowThrough(_), _, apa0, config) and
|
||||
Stage5::fwdFlow(n, state, any(CallContextCall ccc), TParameterPositionSome(pos),
|
||||
Stage4::revFlow(n, state, TReturnCtxMaybeFlowThrough(_), _, apa0, config) and
|
||||
Stage4::fwdFlow(n, state, any(CallContextCall ccc), TParameterPositionSome(pos),
|
||||
TAccessPathApproxSome(apa), apa0, config)
|
||||
)
|
||||
}
|
||||
@@ -2839,7 +2596,7 @@ private predicate nodeMayUseSummary(
|
||||
NodeEx n, FlowState state, AccessPathApprox apa, Configuration config
|
||||
) {
|
||||
exists(DataFlowCallable c, ParameterPosition pos, ParamNodeEx p |
|
||||
Stage5::parameterMayFlowThrough(p, apa, config) and
|
||||
Stage4::parameterMayFlowThrough(p, apa, config) and
|
||||
nodeMayUseSummary0(n, c, pos, state, apa, config) and
|
||||
p.isParameterOf(c, pos)
|
||||
)
|
||||
@@ -2849,8 +2606,8 @@ private newtype TSummaryCtx =
|
||||
TSummaryCtxNone() or
|
||||
TSummaryCtxSome(ParamNodeEx p, FlowState state, AccessPath ap) {
|
||||
exists(Configuration config |
|
||||
Stage5::parameterMayFlowThrough(p, ap.getApprox(), config) and
|
||||
Stage5::revFlow(p, state, _, config)
|
||||
Stage4::parameterMayFlowThrough(p, ap.getApprox(), config) and
|
||||
Stage4::revFlow(p, state, _, config)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -2899,7 +2656,7 @@ private int count1to2unfold(AccessPathApproxCons1 apa, Configuration config) {
|
||||
len = apa.len() and
|
||||
result =
|
||||
strictcount(AccessPathFront apf |
|
||||
Stage5::consCand(tc, any(AccessPathApprox ap | ap.getFront() = apf and ap.len() = len - 1),
|
||||
Stage4::consCand(tc, any(AccessPathApprox ap | ap.getFront() = apf and ap.len() = len - 1),
|
||||
config)
|
||||
)
|
||||
)
|
||||
@@ -2908,7 +2665,7 @@ private int count1to2unfold(AccessPathApproxCons1 apa, Configuration config) {
|
||||
private int countNodesUsingAccessPath(AccessPathApprox apa, Configuration config) {
|
||||
result =
|
||||
strictcount(NodeEx n, FlowState state |
|
||||
Stage5::revFlow(n, state, apa, config) or nodeMayUseSummary(n, state, apa, config)
|
||||
Stage4::revFlow(n, state, apa, config) or nodeMayUseSummary(n, state, apa, config)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -2929,7 +2686,7 @@ private predicate expensiveLen1to2unfolding(AccessPathApproxCons1 apa, Configura
|
||||
private AccessPathApprox getATail(AccessPathApprox apa, Configuration config) {
|
||||
exists(TypedContent head |
|
||||
apa.pop(head) = result and
|
||||
Stage5::consCand(head, result, config)
|
||||
Stage4::consCand(head, result, config)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -3011,7 +2768,7 @@ private newtype TPathNode =
|
||||
NodeEx node, FlowState state, CallContext cc, SummaryCtx sc, AccessPath ap, Configuration config
|
||||
) {
|
||||
// A PathNode is introduced by a source ...
|
||||
Stage5::revFlow(node, state, config) and
|
||||
Stage4::revFlow(node, state, config) and
|
||||
sourceNode(node, state, config) and
|
||||
(
|
||||
if hasSourceCallCtx(config)
|
||||
@@ -3025,7 +2782,7 @@ private newtype TPathNode =
|
||||
exists(PathNodeMid mid |
|
||||
pathStep(mid, node, state, cc, sc, ap) and
|
||||
pragma[only_bind_into](config) = mid.getConfiguration() and
|
||||
Stage5::revFlow(node, state, ap.getApprox(), pragma[only_bind_into](config))
|
||||
Stage4::revFlow(node, state, ap.getApprox(), pragma[only_bind_into](config))
|
||||
)
|
||||
} or
|
||||
TPathNodeSink(NodeEx node, FlowState state, Configuration config) {
|
||||
@@ -3167,7 +2924,7 @@ private class AccessPathCons2 extends AccessPath, TAccessPathCons2 {
|
||||
override TypedContent getHead() { result = head1 }
|
||||
|
||||
override AccessPath getTail() {
|
||||
Stage5::consCand(head1, result.getApprox(), _) and
|
||||
Stage4::consCand(head1, result.getApprox(), _) and
|
||||
result.getHead() = head2 and
|
||||
result.length() = len - 1
|
||||
}
|
||||
@@ -3198,7 +2955,7 @@ private class AccessPathCons1 extends AccessPath, TAccessPathCons1 {
|
||||
override TypedContent getHead() { result = head }
|
||||
|
||||
override AccessPath getTail() {
|
||||
Stage5::consCand(head, result.getApprox(), _) and result.length() = len - 1
|
||||
Stage4::consCand(head, result.getApprox(), _) and result.length() = len - 1
|
||||
}
|
||||
|
||||
override AccessPathFrontHead getFront() { result = TFrontHead(head) }
|
||||
@@ -3590,8 +3347,7 @@ private predicate pathStep(
|
||||
AccessPath ap0, NodeEx midnode, FlowState state0, Configuration conf, LocalCallContext localCC
|
||||
|
|
||||
pathNode(mid, midnode, state0, cc, sc, ap0, conf, localCC) and
|
||||
localFlowBigStep(midnode, state0, node, state, false, ap.(AccessPathNil).getType(), conf,
|
||||
localCC) and
|
||||
localFlowBigStep(midnode, state0, node, state, false, ap.getFront(), conf, localCC) and
|
||||
ap0 instanceof AccessPathNil
|
||||
)
|
||||
or
|
||||
@@ -3633,7 +3389,7 @@ private predicate pathReadStep(
|
||||
) {
|
||||
ap0 = mid.getAp() and
|
||||
tc = ap0.getHead() and
|
||||
Stage5::readStepCand(mid.getNodeEx(), tc.getContent(), node, mid.getConfiguration()) and
|
||||
Stage4::readStepCand(mid.getNodeEx(), tc.getContent(), node, mid.getConfiguration()) and
|
||||
state = mid.getState() and
|
||||
cc = mid.getCallContext()
|
||||
}
|
||||
@@ -3643,7 +3399,7 @@ private predicate pathStoreStep(
|
||||
PathNodeMid mid, NodeEx node, FlowState state, AccessPath ap0, TypedContent tc, CallContext cc
|
||||
) {
|
||||
ap0 = mid.getAp() and
|
||||
Stage5::storeStepCand(mid.getNodeEx(), _, tc, node, _, mid.getConfiguration()) and
|
||||
Stage4::storeStepCand(mid.getNodeEx(), _, tc, node, _, mid.getConfiguration()) and
|
||||
state = mid.getState() and
|
||||
cc = mid.getCallContext()
|
||||
}
|
||||
@@ -3680,7 +3436,7 @@ private NodeEx getAnOutNodeFlow(
|
||||
ReturnKindExt kind, DataFlowCall call, AccessPathApprox apa, Configuration config
|
||||
) {
|
||||
result.asNode() = kind.getAnOutNode(call) and
|
||||
Stage5::revFlow(result, _, apa, config)
|
||||
Stage4::revFlow(result, _, apa, config)
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -3716,7 +3472,7 @@ private predicate parameterCand(
|
||||
DataFlowCallable callable, ParameterPosition pos, AccessPathApprox apa, Configuration config
|
||||
) {
|
||||
exists(ParamNodeEx p |
|
||||
Stage5::revFlow(p, _, apa, config) and
|
||||
Stage4::revFlow(p, _, apa, config) and
|
||||
p.isParameterOf(callable, pos)
|
||||
)
|
||||
}
|
||||
@@ -3995,17 +3751,9 @@ predicate stageStats(
|
||||
n = 45 and
|
||||
Stage4::stats(false, nodes, fields, conscand, states, tuples, config)
|
||||
or
|
||||
stage = "5 Fwd" and
|
||||
n = 50 and
|
||||
Stage5::stats(true, nodes, fields, conscand, states, tuples, config)
|
||||
stage = "5 Fwd" and n = 50 and finalStats(true, nodes, fields, conscand, states, tuples)
|
||||
or
|
||||
stage = "5 Rev" and
|
||||
n = 55 and
|
||||
Stage5::stats(false, nodes, fields, conscand, states, tuples, config)
|
||||
or
|
||||
stage = "6 Fwd" and n = 60 and finalStats(true, nodes, fields, conscand, states, tuples)
|
||||
or
|
||||
stage = "6 Rev" and n = 65 and finalStats(false, nodes, fields, conscand, states, tuples)
|
||||
stage = "5 Rev" and n = 55 and finalStats(false, nodes, fields, conscand, states, tuples)
|
||||
}
|
||||
|
||||
private module FlowExploration {
|
||||
|
||||
@@ -621,6 +621,23 @@ private predicate parameterFlowThroughAllowed(ParamNodeEx p, ReturnKindExt kind)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if flow from a parameter at position `pos` inside `c` to a return node of
|
||||
* kind `kind` is allowed.
|
||||
*
|
||||
* We don't expect a parameter to return stored in itself, unless
|
||||
* explicitly allowed
|
||||
*/
|
||||
bindingset[c, pos, kind]
|
||||
private predicate parameterFlowThroughAllowed(
|
||||
DataFlowCallable c, ParameterPosition pos, ReturnKindExt kind
|
||||
) {
|
||||
exists(ParamNodeEx p |
|
||||
p.isParameterOf(c, pos) and
|
||||
parameterFlowThroughAllowed(p, kind)
|
||||
)
|
||||
}
|
||||
|
||||
private module Stage1 implements StageSig {
|
||||
class Ap = Unit;
|
||||
|
||||
@@ -963,12 +980,6 @@ private module Stage1 implements StageSig {
|
||||
pragma[nomagic]
|
||||
predicate revFlow(NodeEx node, Configuration config) { revFlow(node, _, config) }
|
||||
|
||||
pragma[nomagic]
|
||||
predicate revFlowAp(NodeEx node, Ap ap, Configuration config) {
|
||||
revFlow(node, config) and
|
||||
exists(ap)
|
||||
}
|
||||
|
||||
bindingset[node, state, config]
|
||||
predicate revFlow(NodeEx node, FlowState state, Ap ap, Configuration config) {
|
||||
revFlow(node, _, pragma[only_bind_into](config)) and
|
||||
@@ -1011,13 +1022,9 @@ private module Stage1 implements StageSig {
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
predicate returnMayFlowThrough(
|
||||
RetNodeEx ret, Ap argAp, Ap ap, ReturnKindExt kind, Configuration config
|
||||
) {
|
||||
predicate returnMayFlowThrough(RetNodeEx ret, ReturnKindExt kind, Configuration config) {
|
||||
throughFlowNodeCand(ret, config) and
|
||||
kind = ret.getKind() and
|
||||
exists(argAp) and
|
||||
exists(ap)
|
||||
kind = ret.getKind()
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
@@ -1182,8 +1189,6 @@ private signature module StageSig {
|
||||
|
||||
predicate revFlow(NodeEx node, Configuration config);
|
||||
|
||||
predicate revFlowAp(NodeEx node, Ap ap, Configuration config);
|
||||
|
||||
bindingset[node, state, config]
|
||||
predicate revFlow(NodeEx node, FlowState state, Ap ap, Configuration config);
|
||||
|
||||
@@ -1191,9 +1196,7 @@ private signature module StageSig {
|
||||
|
||||
predicate parameterMayFlowThrough(ParamNodeEx p, Ap ap, Configuration config);
|
||||
|
||||
predicate returnMayFlowThrough(
|
||||
RetNodeEx ret, Ap argAp, Ap ap, ReturnKindExt kind, Configuration config
|
||||
);
|
||||
predicate returnMayFlowThrough(RetNodeEx ret, ReturnKindExt kind, Configuration config);
|
||||
|
||||
predicate storeStepCand(
|
||||
NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, DataFlowType contentType,
|
||||
@@ -1278,43 +1281,21 @@ private module MkStage<StageSig PrevStage> {
|
||||
import Param
|
||||
|
||||
/* Begin: Stage logic. */
|
||||
// use an alias as a workaround for bad functionality-induced joins
|
||||
pragma[nomagic]
|
||||
private predicate revFlowApAlias(NodeEx node, ApApprox apa, Configuration config) {
|
||||
PrevStage::revFlowAp(node, apa, config)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate flowIntoCallApa(
|
||||
DataFlowCall call, ArgNodeEx arg, ParamNodeEx p, boolean allowsFieldFlow, ApApprox apa,
|
||||
Configuration config
|
||||
) {
|
||||
flowIntoCall(call, arg, p, allowsFieldFlow, config) and
|
||||
PrevStage::revFlowAp(p, pragma[only_bind_into](apa), pragma[only_bind_into](config)) and
|
||||
revFlowApAlias(arg, pragma[only_bind_into](apa), pragma[only_bind_into](config))
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate flowOutOfCallApa(
|
||||
DataFlowCall call, RetNodeEx ret, ReturnKindExt kind, NodeEx out, boolean allowsFieldFlow,
|
||||
ApApprox apa, Configuration config
|
||||
) {
|
||||
flowOutOfCall(call, ret, kind, out, allowsFieldFlow, config) and
|
||||
PrevStage::revFlowAp(out, pragma[only_bind_into](apa), pragma[only_bind_into](config)) and
|
||||
revFlowApAlias(ret, pragma[only_bind_into](apa), pragma[only_bind_into](config))
|
||||
bindingset[result, apa]
|
||||
private ApApprox unbindApa(ApApprox apa) {
|
||||
pragma[only_bind_out](apa) = pragma[only_bind_out](result)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate flowThroughOutOfCall(
|
||||
DataFlowCall call, CcCall ccc, RetNodeEx ret, NodeEx out, boolean allowsFieldFlow,
|
||||
ApApprox argApa, ApApprox apa, Configuration config
|
||||
DataFlowCall call, DataFlowCallable c, CcCall ccc, RetNodeEx ret, ReturnKindExt kind,
|
||||
NodeEx out, boolean allowsFieldFlow, Configuration config
|
||||
) {
|
||||
exists(ReturnKindExt kind |
|
||||
flowOutOfCallApa(call, ret, kind, out, allowsFieldFlow, apa, pragma[only_bind_into](config)) and
|
||||
PrevStage::callMayFlowThroughRev(call, pragma[only_bind_into](config)) and
|
||||
PrevStage::returnMayFlowThrough(ret, argApa, apa, kind, pragma[only_bind_into](config)) and
|
||||
matchesCall(ccc, call)
|
||||
)
|
||||
flowOutOfCall(call, ret, kind, out, allowsFieldFlow, pragma[only_bind_into](config)) and
|
||||
PrevStage::callMayFlowThroughRev(call, pragma[only_bind_into](config)) and
|
||||
PrevStage::returnMayFlowThrough(ret, kind, pragma[only_bind_into](config)) and
|
||||
matchesCall(ccc, call) and
|
||||
c = ret.getEnclosingCallable()
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1326,50 +1307,39 @@ private module MkStage<StageSig PrevStage> {
|
||||
* corresponding parameter position and access path of that argument, respectively.
|
||||
*/
|
||||
pragma[nomagic]
|
||||
additional predicate fwdFlow(
|
||||
NodeEx node, FlowState state, Cc cc, ParameterPositionOption summaryCtx, ApOption argAp,
|
||||
Ap ap, ApApprox apa, Configuration config
|
||||
) {
|
||||
fwdFlow0(node, state, cc, summaryCtx, argAp, ap, apa, config) and
|
||||
PrevStage::revFlow(node, state, apa, config) and
|
||||
filter(node, state, ap, config)
|
||||
}
|
||||
|
||||
pragma[inline]
|
||||
additional predicate fwdFlow(
|
||||
NodeEx node, FlowState state, Cc cc, ParameterPositionOption summaryCtx, ApOption argAp,
|
||||
Ap ap, Configuration config
|
||||
) {
|
||||
fwdFlow(node, state, cc, summaryCtx, argAp, ap, _, config)
|
||||
fwdFlow0(node, state, cc, summaryCtx, argAp, ap, config) and
|
||||
PrevStage::revFlow(node, state, unbindApa(getApprox(ap)), config) and
|
||||
filter(node, state, ap, config)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate fwdFlow0(
|
||||
NodeEx node, FlowState state, Cc cc, ParameterPositionOption summaryCtx, ApOption argAp,
|
||||
Ap ap, ApApprox apa, Configuration config
|
||||
Ap ap, Configuration config
|
||||
) {
|
||||
sourceNode(node, state, config) and
|
||||
(if hasSourceCallCtx(config) then cc = ccSomeCall() else cc = ccNone()) and
|
||||
argAp = apNone() and
|
||||
summaryCtx = TParameterPositionNone() and
|
||||
ap = getApNil(node) and
|
||||
apa = getApprox(ap)
|
||||
ap = getApNil(node)
|
||||
or
|
||||
exists(NodeEx mid, FlowState state0, Ap ap0, ApApprox apa0, LocalCc localCc |
|
||||
fwdFlow(mid, state0, cc, summaryCtx, argAp, ap0, apa0, config) and
|
||||
exists(NodeEx mid, FlowState state0, Ap ap0, LocalCc localCc |
|
||||
fwdFlow(mid, state0, cc, summaryCtx, argAp, ap0, config) and
|
||||
localCc = getLocalCc(mid, cc)
|
||||
|
|
||||
localStep(mid, state0, node, state, true, _, config, localCc) and
|
||||
ap = ap0 and
|
||||
apa = apa0
|
||||
ap = ap0
|
||||
or
|
||||
localStep(mid, state0, node, state, false, ap, config, localCc) and
|
||||
ap0 instanceof ApNil and
|
||||
apa = getApprox(ap)
|
||||
ap0 instanceof ApNil
|
||||
)
|
||||
or
|
||||
exists(NodeEx mid |
|
||||
fwdFlow(mid, pragma[only_bind_into](state), _, _, _, ap, apa, pragma[only_bind_into](config)) and
|
||||
fwdFlow(mid, pragma[only_bind_into](state), _, _, _, ap, pragma[only_bind_into](config)) and
|
||||
jumpStep(mid, node, config) and
|
||||
cc = ccNone() and
|
||||
summaryCtx = TParameterPositionNone() and
|
||||
@@ -1382,8 +1352,7 @@ private module MkStage<StageSig PrevStage> {
|
||||
cc = ccNone() and
|
||||
summaryCtx = TParameterPositionNone() and
|
||||
argAp = apNone() and
|
||||
ap = getApNil(node) and
|
||||
apa = getApprox(ap)
|
||||
ap = getApNil(node)
|
||||
)
|
||||
or
|
||||
exists(NodeEx mid, FlowState state0, ApNil nil |
|
||||
@@ -1392,32 +1361,32 @@ private module MkStage<StageSig PrevStage> {
|
||||
cc = ccNone() and
|
||||
summaryCtx = TParameterPositionNone() and
|
||||
argAp = apNone() and
|
||||
ap = getApNil(node) and
|
||||
apa = getApprox(ap)
|
||||
ap = getApNil(node)
|
||||
)
|
||||
or
|
||||
// store
|
||||
exists(TypedContent tc, Ap ap0 |
|
||||
fwdFlowStore(_, ap0, tc, node, state, cc, summaryCtx, argAp, config) and
|
||||
ap = apCons(tc, ap0) and
|
||||
apa = getApprox(ap)
|
||||
ap = apCons(tc, ap0)
|
||||
)
|
||||
or
|
||||
// read
|
||||
exists(Ap ap0, Content c |
|
||||
fwdFlowRead(ap0, c, _, node, state, cc, summaryCtx, argAp, config) and
|
||||
fwdFlowConsCand(ap0, c, ap, config) and
|
||||
apa = getApprox(ap)
|
||||
fwdFlowConsCand(ap0, c, ap, config)
|
||||
)
|
||||
or
|
||||
// flow into a callable
|
||||
fwdFlowIn(_, node, state, _, cc, _, _, ap, apa, config) and
|
||||
if PrevStage::parameterMayFlowThrough(node, apa, config)
|
||||
then (
|
||||
summaryCtx = TParameterPositionSome(node.(ParamNodeEx).getPosition()) and
|
||||
argAp = apSome(ap)
|
||||
) else (
|
||||
summaryCtx = TParameterPositionNone() and argAp = apNone()
|
||||
exists(ApApprox apa |
|
||||
fwdFlowIn(_, node, state, _, cc, _, _, ap, config) and
|
||||
apa = getApprox(ap) and
|
||||
if PrevStage::parameterMayFlowThrough(node, apa, config)
|
||||
then (
|
||||
summaryCtx = TParameterPositionSome(node.(ParamNodeEx).getPosition()) and
|
||||
argAp = apSome(ap)
|
||||
) else (
|
||||
summaryCtx = TParameterPositionNone() and argAp = apNone()
|
||||
)
|
||||
)
|
||||
or
|
||||
// flow out of a callable
|
||||
@@ -1425,8 +1394,8 @@ private module MkStage<StageSig PrevStage> {
|
||||
DataFlowCall call, RetNodeEx ret, boolean allowsFieldFlow, CcNoCall innercc,
|
||||
DataFlowCallable inner
|
||||
|
|
||||
fwdFlow(ret, state, innercc, summaryCtx, argAp, ap, apa, config) and
|
||||
flowOutOfCallApa(call, ret, _, node, allowsFieldFlow, apa, config) and
|
||||
fwdFlow(ret, state, innercc, summaryCtx, argAp, ap, config) and
|
||||
flowOutOfCall(call, ret, _, node, allowsFieldFlow, config) and
|
||||
inner = ret.getEnclosingCallable() and
|
||||
cc = getCallContextReturn(inner, call, innercc) and
|
||||
if allowsFieldFlow = false then ap instanceof ApNil else any()
|
||||
@@ -1434,7 +1403,7 @@ private module MkStage<StageSig PrevStage> {
|
||||
or
|
||||
// flow through a callable
|
||||
exists(DataFlowCall call, ParameterPosition summaryCtx0, Ap argAp0 |
|
||||
fwdFlowOutFromArg(call, node, state, summaryCtx0, argAp0, ap, apa, config) and
|
||||
fwdFlowOutFromArg(call, node, state, summaryCtx0, argAp0, ap, config) and
|
||||
fwdFlowIsEntered(call, cc, summaryCtx, argAp, summaryCtx0, argAp0, config)
|
||||
)
|
||||
}
|
||||
@@ -1444,9 +1413,9 @@ private module MkStage<StageSig PrevStage> {
|
||||
NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, FlowState state, Cc cc,
|
||||
ParameterPositionOption summaryCtx, ApOption argAp, Configuration config
|
||||
) {
|
||||
exists(DataFlowType contentType, ApApprox apa1 |
|
||||
fwdFlow(node1, state, cc, summaryCtx, argAp, ap1, apa1, config) and
|
||||
PrevStage::storeStepCand(node1, apa1, tc, node2, contentType, config) and
|
||||
exists(DataFlowType contentType |
|
||||
fwdFlow(node1, state, cc, summaryCtx, argAp, ap1, config) and
|
||||
PrevStage::storeStepCand(node1, unbindApa(getApprox(ap1)), tc, node2, contentType, config) and
|
||||
typecheckStore(ap1, contentType)
|
||||
)
|
||||
}
|
||||
@@ -1464,104 +1433,43 @@ private module MkStage<StageSig PrevStage> {
|
||||
)
|
||||
}
|
||||
|
||||
private class ApNonNil instanceof Ap {
|
||||
pragma[nomagic]
|
||||
ApNonNil() { not this instanceof ApNil }
|
||||
|
||||
string toString() { result = "" }
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate fwdFlowRead0(
|
||||
NodeEx node1, FlowState state, Cc cc, ParameterPositionOption summaryCtx, ApOption argAp,
|
||||
ApNonNil ap, Configuration config
|
||||
) {
|
||||
fwdFlow(node1, state, cc, summaryCtx, argAp, ap, config) and
|
||||
PrevStage::readStepCand(node1, _, _, config)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate fwdFlowRead(
|
||||
Ap ap, Content c, NodeEx node1, NodeEx node2, FlowState state, Cc cc,
|
||||
ParameterPositionOption summaryCtx, ApOption argAp, Configuration config
|
||||
) {
|
||||
fwdFlowRead0(node1, state, cc, summaryCtx, argAp, ap, config) and
|
||||
fwdFlow(node1, state, cc, summaryCtx, argAp, ap, config) and
|
||||
PrevStage::readStepCand(node1, c, node2, config) and
|
||||
getHeadContent(ap) = c
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate fwdFlowIn(
|
||||
DataFlowCall call, ParamNodeEx p, FlowState state, Cc outercc, CcCall innercc,
|
||||
ParameterPositionOption summaryCtx, ApOption argAp, Ap ap, ApApprox apa, Configuration config
|
||||
DataFlowCall call, ParamNodeEx p, FlowState state, Cc outercc, Cc innercc,
|
||||
ParameterPositionOption summaryCtx, ApOption argAp, Ap ap, Configuration config
|
||||
) {
|
||||
exists(ArgNodeEx arg, boolean allowsFieldFlow |
|
||||
fwdFlow(arg, state, outercc, summaryCtx, argAp, ap, apa, config) and
|
||||
flowIntoCallApa(call, arg, p, allowsFieldFlow, apa, config) and
|
||||
fwdFlow(arg, state, outercc, summaryCtx, argAp, ap, config) and
|
||||
flowIntoCall(call, arg, p, allowsFieldFlow, config) and
|
||||
innercc = getCallContextCall(call, p.getEnclosingCallable(), outercc) and
|
||||
if allowsFieldFlow = false then ap instanceof ApNil else any()
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate fwdFlowRetFromArg(
|
||||
RetNodeEx ret, FlowState state, CcCall ccc, ParameterPosition summaryCtx, ParamNodeEx p,
|
||||
Ap argAp, ApApprox argApa, Ap ap, ApApprox apa, Configuration config
|
||||
) {
|
||||
exists(DataFlowCallable c, ReturnKindExt kind |
|
||||
fwdFlow(pragma[only_bind_into](ret), state, ccc,
|
||||
TParameterPositionSome(pragma[only_bind_into](summaryCtx)), apSome(argAp), ap, apa, config) and
|
||||
getApprox(argAp) = argApa and
|
||||
c = ret.getEnclosingCallable() and
|
||||
kind = ret.getKind() and
|
||||
p.isParameterOf(c, pragma[only_bind_into](summaryCtx)) and
|
||||
parameterFlowThroughAllowed(p, kind)
|
||||
)
|
||||
}
|
||||
|
||||
pragma[inline]
|
||||
private predicate fwdFlowInMayFlowThrough(
|
||||
DataFlowCall call, Cc cc, CcCall innerCc, ParameterPositionOption summaryCtx, ApOption argAp,
|
||||
ParamNodeEx param, Ap ap, ApApprox apa, Configuration config
|
||||
) {
|
||||
fwdFlowIn(call, pragma[only_bind_into](param), _, cc, innerCc, summaryCtx, argAp, ap,
|
||||
pragma[only_bind_into](apa), pragma[only_bind_into](config)) and
|
||||
PrevStage::parameterMayFlowThrough(param, apa, config)
|
||||
}
|
||||
|
||||
// dedup before joining with `flowThroughOutOfCall`
|
||||
pragma[nomagic]
|
||||
private predicate fwdFlowInMayFlowThroughProj(
|
||||
DataFlowCall call, CcCall innerCc, ApApprox apa, Configuration config
|
||||
) {
|
||||
fwdFlowInMayFlowThrough(call, _, innerCc, _, _, _, _, apa, config)
|
||||
}
|
||||
|
||||
/**
|
||||
* Same as `flowThroughOutOfCall`, but restricted to calls that are reached
|
||||
* in the flow covered by `fwdFlow`, where data might flow through the target
|
||||
* callable and back out at `call`.
|
||||
*/
|
||||
pragma[nomagic]
|
||||
private predicate fwdFlowThroughOutOfCall(
|
||||
DataFlowCall call, CcCall ccc, RetNodeEx ret, NodeEx out, boolean allowsFieldFlow,
|
||||
ApApprox argApa, ApApprox apa, Configuration config
|
||||
) {
|
||||
fwdFlowInMayFlowThroughProj(call, ccc, argApa, config) and
|
||||
flowThroughOutOfCall(call, ccc, ret, out, allowsFieldFlow, argApa, apa, config)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate fwdFlowOutFromArg(
|
||||
DataFlowCall call, NodeEx out, FlowState state, ParameterPosition summaryCtx, Ap argAp, Ap ap,
|
||||
ApApprox apa, Configuration config
|
||||
Configuration config
|
||||
) {
|
||||
exists(RetNodeEx ret, boolean allowsFieldFlow, CcCall ccc, ApApprox argApa |
|
||||
fwdFlowRetFromArg(pragma[only_bind_into](ret), state, pragma[only_bind_into](ccc),
|
||||
summaryCtx, _, argAp, pragma[only_bind_into](argApa), ap, pragma[only_bind_into](apa),
|
||||
exists(
|
||||
DataFlowCallable c, RetNodeEx ret, ReturnKindExt kind, boolean allowsFieldFlow, CcCall ccc
|
||||
|
|
||||
fwdFlow(pragma[only_bind_into](ret), state, pragma[only_bind_into](ccc),
|
||||
TParameterPositionSome(pragma[only_bind_into](summaryCtx)), apSome(argAp), ap, config) and
|
||||
flowThroughOutOfCall(call, pragma[only_bind_into](c), ccc, ret, kind, out, allowsFieldFlow,
|
||||
config) and
|
||||
fwdFlowThroughOutOfCall(call, ccc, ret, out, allowsFieldFlow, argApa, apa, config) and
|
||||
(if allowsFieldFlow = false then ap instanceof ApNil else any())
|
||||
(if allowsFieldFlow = false then ap instanceof ApNil else any()) and
|
||||
parameterFlowThroughAllowed(c, pragma[only_bind_into](summaryCtx), kind)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1575,7 +1483,8 @@ private module MkStage<StageSig PrevStage> {
|
||||
ParameterPosition pos, Ap ap, Configuration config
|
||||
) {
|
||||
exists(ParamNodeEx param |
|
||||
fwdFlowInMayFlowThrough(call, cc, _, summaryCtx, argAp, param, ap, _, config) and
|
||||
fwdFlowIn(call, param, _, cc, _, summaryCtx, argAp, ap, config) and
|
||||
PrevStage::parameterMayFlowThrough(param, unbindApa(getApprox(ap)), config) and
|
||||
pos = param.getPosition()
|
||||
)
|
||||
}
|
||||
@@ -1596,55 +1505,41 @@ private module MkStage<StageSig PrevStage> {
|
||||
fwdFlowConsCand(ap1, c, ap2, config)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate returnFlowsThrough0(
|
||||
RetNodeEx ret, ReturnKindExt kind, FlowState state, CcCall ccc, DataFlowCallable c,
|
||||
ParameterPosition ppos, Ap argAp, Ap ap, Configuration config
|
||||
) {
|
||||
exists(boolean allowsFieldFlow |
|
||||
fwdFlow(ret, state, ccc, TParameterPositionSome(ppos), apSome(argAp), ap, config) and
|
||||
flowThroughOutOfCall(_, c, _, pragma[only_bind_into](ret), kind, _, allowsFieldFlow,
|
||||
pragma[only_bind_into](config)) and
|
||||
if allowsFieldFlow = false then ap instanceof ApNil else any()
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate returnFlowsThrough(
|
||||
RetNodeEx ret, ReturnKindExt kind, FlowState state, CcCall ccc, ParamNodeEx p, Ap argAp,
|
||||
Ap ap, Configuration config
|
||||
) {
|
||||
exists(boolean allowsFieldFlow, ApApprox argApa, ApApprox apa |
|
||||
fwdFlowRetFromArg(pragma[only_bind_into](ret), state, pragma[only_bind_into](ccc), _, p,
|
||||
argAp, pragma[only_bind_into](argApa), ap, pragma[only_bind_into](apa), config) and
|
||||
kind = ret.getKind() and
|
||||
fwdFlowThroughOutOfCall(_, ccc, ret, _, allowsFieldFlow, argApa, apa, config) and
|
||||
(if allowsFieldFlow = false then ap instanceof ApNil else any())
|
||||
exists(DataFlowCallable c, ParameterPosition ppos |
|
||||
returnFlowsThrough0(ret, kind, state, ccc, c, ppos, argAp, ap, config) and
|
||||
p.isParameterOf(c, ppos) and
|
||||
parameterFlowThroughAllowed(p, kind)
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate flowThroughIntoCall(
|
||||
DataFlowCall call, ArgNodeEx arg, ParamNodeEx p, boolean allowsFieldFlow, Ap argAp,
|
||||
Configuration config
|
||||
DataFlowCall call, ArgNodeEx arg, ParamNodeEx p, boolean allowsFieldFlow, Configuration config
|
||||
) {
|
||||
exists(ApApprox argApa |
|
||||
flowIntoCallApa(call, pragma[only_bind_into](arg), pragma[only_bind_into](p),
|
||||
allowsFieldFlow, argApa, pragma[only_bind_into](config)) and
|
||||
fwdFlow(arg, _, _, _, _, pragma[only_bind_into](argAp), argApa,
|
||||
exists(Ap argAp |
|
||||
flowIntoCall(call, pragma[only_bind_into](arg), pragma[only_bind_into](p), allowsFieldFlow,
|
||||
pragma[only_bind_into](config)) and
|
||||
fwdFlow(arg, _, _, _, _, pragma[only_bind_into](argAp), pragma[only_bind_into](config)) and
|
||||
returnFlowsThrough(_, _, _, _, p, pragma[only_bind_into](argAp), _,
|
||||
pragma[only_bind_into](config)) and
|
||||
if allowsFieldFlow = false then argAp instanceof ApNil else any()
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate flowIntoCallAp(
|
||||
DataFlowCall call, ArgNodeEx arg, ParamNodeEx p, boolean allowsFieldFlow, Ap ap,
|
||||
Configuration config
|
||||
) {
|
||||
exists(ApApprox apa |
|
||||
flowIntoCallApa(call, arg, p, allowsFieldFlow, apa, config) and
|
||||
fwdFlow(arg, _, _, _, _, ap, apa, config)
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate flowOutOfCallAp(
|
||||
DataFlowCall call, RetNodeEx ret, ReturnKindExt kind, NodeEx out, boolean allowsFieldFlow,
|
||||
Ap ap, Configuration config
|
||||
) {
|
||||
exists(ApApprox apa |
|
||||
flowOutOfCallApa(call, ret, kind, out, allowsFieldFlow, apa, config) and
|
||||
fwdFlow(ret, _, _, _, _, ap, apa, config)
|
||||
pragma[only_bind_into](config))
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1733,7 +1628,7 @@ private module MkStage<StageSig PrevStage> {
|
||||
// flow into a callable
|
||||
exists(ParamNodeEx p, boolean allowsFieldFlow |
|
||||
revFlow(p, state, TReturnCtxNone(), returnAp, ap, config) and
|
||||
flowIntoCallAp(_, node, p, allowsFieldFlow, ap, config) and
|
||||
flowIntoCall(_, node, p, allowsFieldFlow, config) and
|
||||
(if allowsFieldFlow = false then ap instanceof ApNil else any()) and
|
||||
returnCtx = TReturnCtxNone()
|
||||
)
|
||||
@@ -1787,41 +1682,22 @@ private module MkStage<StageSig PrevStage> {
|
||||
) {
|
||||
exists(NodeEx out, boolean allowsFieldFlow |
|
||||
revFlow(out, state, returnCtx, returnAp, ap, config) and
|
||||
flowOutOfCallAp(call, ret, kind, out, allowsFieldFlow, ap, config) and
|
||||
flowOutOfCall(call, ret, kind, out, allowsFieldFlow, config) and
|
||||
if allowsFieldFlow = false then ap instanceof ApNil else any()
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Same as `flowThroughIntoCall`, but restricted to calls that are reached
|
||||
* in the flow covered by `revFlow`, where data might flow through the target
|
||||
* callable and back out at `call`.
|
||||
*/
|
||||
pragma[nomagic]
|
||||
private predicate revFlowThroughIntoCall(
|
||||
DataFlowCall call, ArgNodeEx arg, ParamNodeEx p, boolean allowsFieldFlow, Ap argAp,
|
||||
Configuration config
|
||||
) {
|
||||
flowThroughIntoCall(call, arg, p, allowsFieldFlow, argAp, config) and
|
||||
revFlowIsReturned(call, _, _, _, _, config)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate revFlowParamToReturn(
|
||||
ParamNodeEx p, FlowState state, ReturnKindExt kind, Ap returnAp, Ap ap, Configuration config
|
||||
) {
|
||||
revFlow(p, state, TReturnCtxMaybeFlowThrough(kind), apSome(returnAp), ap, config) and
|
||||
parameterFlowThroughAllowed(p, kind)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate revFlowInToReturn(
|
||||
DataFlowCall call, ArgNodeEx arg, FlowState state, ReturnKindExt kind, Ap returnAp, Ap ap,
|
||||
Configuration config
|
||||
) {
|
||||
exists(ParamNodeEx p, boolean allowsFieldFlow |
|
||||
revFlowParamToReturn(p, state, kind, returnAp, ap, config) and
|
||||
revFlowThroughIntoCall(call, arg, p, allowsFieldFlow, ap, config)
|
||||
revFlow(pragma[only_bind_into](p), state,
|
||||
TReturnCtxMaybeFlowThrough(pragma[only_bind_into](kind)), apSome(returnAp), ap, config) and
|
||||
flowThroughIntoCall(call, arg, p, allowsFieldFlow, config) and
|
||||
(if allowsFieldFlow = false then ap instanceof ApNil else any()) and
|
||||
parameterFlowThroughAllowed(p, kind)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1874,11 +1750,6 @@ private module MkStage<StageSig PrevStage> {
|
||||
pragma[nomagic]
|
||||
predicate revFlow(NodeEx node, Configuration config) { revFlow(node, _, _, _, _, config) }
|
||||
|
||||
pragma[nomagic]
|
||||
predicate revFlowAp(NodeEx node, Ap ap, Configuration config) {
|
||||
revFlow(node, _, _, _, ap, config)
|
||||
}
|
||||
|
||||
// use an alias as a workaround for bad functionality-induced joins
|
||||
pragma[nomagic]
|
||||
additional predicate revFlowAlias(NodeEx node, Configuration config) {
|
||||
@@ -1915,9 +1786,9 @@ private module MkStage<StageSig PrevStage> {
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate parameterFlowsThroughRev(
|
||||
ParamNodeEx p, Ap ap, ReturnKindExt kind, Ap returnAp, Configuration config
|
||||
ParamNodeEx p, Ap ap, ReturnKindExt kind, Configuration config
|
||||
) {
|
||||
revFlow(p, _, TReturnCtxMaybeFlowThrough(kind), apSome(returnAp), ap, config) and
|
||||
revFlow(p, _, TReturnCtxMaybeFlowThrough(kind), apSome(_), ap, config) and
|
||||
parameterFlowThroughAllowed(p, kind)
|
||||
}
|
||||
|
||||
@@ -1925,36 +1796,27 @@ private module MkStage<StageSig PrevStage> {
|
||||
predicate parameterMayFlowThrough(ParamNodeEx p, Ap ap, Configuration config) {
|
||||
exists(RetNodeEx ret, ReturnKindExt kind |
|
||||
returnFlowsThrough(ret, kind, _, _, p, ap, _, config) and
|
||||
parameterFlowsThroughRev(p, ap, kind, _, config)
|
||||
parameterFlowsThroughRev(p, ap, kind, config)
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
predicate returnMayFlowThrough(
|
||||
RetNodeEx ret, Ap argAp, Ap ap, ReturnKindExt kind, Configuration config
|
||||
) {
|
||||
exists(ParamNodeEx p |
|
||||
returnFlowsThrough(ret, kind, _, _, p, argAp, ap, config) and
|
||||
parameterFlowsThroughRev(p, argAp, kind, ap, config)
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
predicate revFlowInToReturnIsReturned(
|
||||
DataFlowCall call, ArgNodeEx arg, FlowState state, ReturnCtx returnCtx, ApOption returnAp,
|
||||
Ap ap, Configuration config
|
||||
) {
|
||||
exists(ReturnKindExt returnKind0, Ap returnAp0 |
|
||||
revFlowInToReturn(call, arg, state, returnKind0, returnAp0, ap, config) and
|
||||
revFlowIsReturned(call, returnCtx, returnAp, returnKind0, returnAp0, config)
|
||||
predicate returnMayFlowThrough(RetNodeEx ret, ReturnKindExt kind, Configuration config) {
|
||||
exists(ParamNodeEx p, Ap ap |
|
||||
returnFlowsThrough(ret, kind, _, _, p, ap, _, config) and
|
||||
parameterFlowsThroughRev(p, ap, kind, config)
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
predicate callMayFlowThroughRev(DataFlowCall call, Configuration config) {
|
||||
exists(ArgNodeEx arg, FlowState state, ReturnCtx returnCtx, ApOption returnAp, Ap ap |
|
||||
exists(
|
||||
ReturnKindExt returnKind0, Ap returnAp0, ArgNodeEx arg, FlowState state,
|
||||
ReturnCtx returnCtx, ApOption returnAp, Ap ap
|
||||
|
|
||||
revFlow(arg, state, returnCtx, returnAp, ap, config) and
|
||||
revFlowInToReturnIsReturned(call, arg, state, returnCtx, returnAp, ap, config)
|
||||
revFlowInToReturn(call, arg, state, returnKind0, returnAp0, ap, config) and
|
||||
revFlowIsReturned(call, returnCtx, returnAp, returnKind0, returnAp0, config)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -2317,16 +2179,16 @@ private module LocalFlowBigStep {
|
||||
pragma[nomagic]
|
||||
predicate localFlowBigStep(
|
||||
NodeEx node1, FlowState state1, NodeEx node2, FlowState state2, boolean preservesValue,
|
||||
DataFlowType t, Configuration config, LocalCallContext callContext
|
||||
AccessPathFrontNil apf, Configuration config, LocalCallContext callContext
|
||||
) {
|
||||
localFlowStepPlus(node1, state1, node2, preservesValue, t, config, callContext) and
|
||||
localFlowStepPlus(node1, state1, node2, preservesValue, apf.getType(), config, callContext) and
|
||||
localFlowExit(node2, state1, config) and
|
||||
state1 = state2
|
||||
or
|
||||
additionalLocalFlowStepNodeCand2(node1, state1, node2, state2, config) and
|
||||
state1 != state2 and
|
||||
preservesValue = false and
|
||||
t = node2.getDataFlowType() and
|
||||
apf = TFrontNil(node2.getDataFlowType()) and
|
||||
callContext.relevantFor(node1.getEnclosingCallable()) and
|
||||
not exists(DataFlowCall call | call = callContext.(LocalCallContextSpecificCall).getCall() |
|
||||
isUnreachableInCallCached(node1.asNode(), call) or
|
||||
@@ -2340,87 +2202,11 @@ private import LocalFlowBigStep
|
||||
private module Stage3Param implements MkStage<Stage2>::StageParam {
|
||||
private module PrevStage = Stage2;
|
||||
|
||||
class Ap = ApproxAccessPathFront;
|
||||
|
||||
class ApNil = ApproxAccessPathFrontNil;
|
||||
|
||||
PrevStage::Ap getApprox(Ap ap) { result = ap.toBoolNonEmpty() }
|
||||
|
||||
ApNil getApNil(NodeEx node) {
|
||||
PrevStage::revFlow(node, _) and result = TApproxFrontNil(node.getDataFlowType())
|
||||
}
|
||||
|
||||
bindingset[tc, tail]
|
||||
Ap apCons(TypedContent tc, Ap tail) { result.getAHead() = tc and exists(tail) }
|
||||
|
||||
pragma[noinline]
|
||||
Content getHeadContent(Ap ap) { result = ap.getAHead().getContent() }
|
||||
|
||||
class ApOption = ApproxAccessPathFrontOption;
|
||||
|
||||
ApOption apNone() { result = TApproxAccessPathFrontNone() }
|
||||
|
||||
ApOption apSome(Ap ap) { result = TApproxAccessPathFrontSome(ap) }
|
||||
|
||||
import BooleanCallContext
|
||||
|
||||
predicate localStep(
|
||||
NodeEx node1, FlowState state1, NodeEx node2, FlowState state2, boolean preservesValue,
|
||||
ApproxAccessPathFrontNil ap, Configuration config, LocalCc lcc
|
||||
) {
|
||||
localFlowBigStep(node1, state1, node2, state2, preservesValue, ap.getType(), config, _) and
|
||||
exists(lcc)
|
||||
}
|
||||
|
||||
predicate flowOutOfCall = flowOutOfCallNodeCand2/6;
|
||||
|
||||
predicate flowIntoCall = flowIntoCallNodeCand2/5;
|
||||
|
||||
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.getAHead().getContent()
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate castingNodeEx(NodeEx node) { node.asNode() instanceof CastingNode }
|
||||
|
||||
bindingset[node, state, ap, config]
|
||||
predicate filter(NodeEx node, FlowState state, Ap ap, Configuration config) {
|
||||
exists(state) and
|
||||
exists(config) and
|
||||
(if castingNodeEx(node) then compatibleTypes(node.getDataFlowType(), ap.getType()) else any()) and
|
||||
(
|
||||
notExpectsContent(node)
|
||||
or
|
||||
expectsContentCand(node, ap, config)
|
||||
)
|
||||
}
|
||||
|
||||
bindingset[ap, contentType]
|
||||
predicate typecheckStore(Ap ap, DataFlowType contentType) {
|
||||
// We need to typecheck stores here, since reverse flow through a getter
|
||||
// might have a different type here compared to inside the getter.
|
||||
compatibleTypes(ap.getType(), contentType)
|
||||
}
|
||||
}
|
||||
|
||||
private module Stage3 implements StageSig {
|
||||
import MkStage<Stage2>::Stage<Stage3Param>
|
||||
}
|
||||
|
||||
private module Stage4Param implements MkStage<Stage3>::StageParam {
|
||||
private module PrevStage = Stage3;
|
||||
|
||||
class Ap = AccessPathFront;
|
||||
|
||||
class ApNil = AccessPathFrontNil;
|
||||
|
||||
PrevStage::Ap getApprox(Ap ap) { result = ap.toApprox() }
|
||||
PrevStage::Ap getApprox(Ap ap) { result = ap.toBoolNonEmpty() }
|
||||
|
||||
ApNil getApNil(NodeEx node) {
|
||||
PrevStage::revFlow(node, _) and result = TFrontNil(node.getDataFlowType())
|
||||
@@ -2440,42 +2226,16 @@ private module Stage4Param implements MkStage<Stage3>::StageParam {
|
||||
|
||||
import BooleanCallContext
|
||||
|
||||
pragma[nomagic]
|
||||
predicate localStep(
|
||||
NodeEx node1, FlowState state1, NodeEx node2, FlowState state2, boolean preservesValue,
|
||||
ApNil ap, Configuration config, LocalCc lcc
|
||||
) {
|
||||
localFlowBigStep(node1, state1, node2, state2, preservesValue, ap.getType(), config, _) and
|
||||
PrevStage::revFlow(node1, pragma[only_bind_into](state1), _, pragma[only_bind_into](config)) and
|
||||
PrevStage::revFlowAlias(node2, pragma[only_bind_into](state2), _, pragma[only_bind_into](config)) and
|
||||
exists(lcc)
|
||||
localFlowBigStep(node1, state1, node2, state2, preservesValue, ap, config, _) and exists(lcc)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
predicate flowOutOfCall(
|
||||
DataFlowCall call, RetNodeEx node1, ReturnKindExt kind, NodeEx node2, boolean allowsFieldFlow,
|
||||
Configuration config
|
||||
) {
|
||||
exists(FlowState state |
|
||||
flowOutOfCallNodeCand2(call, node1, kind, node2, allowsFieldFlow, config) and
|
||||
PrevStage::revFlow(node2, pragma[only_bind_into](state), _, pragma[only_bind_into](config)) and
|
||||
PrevStage::revFlowAlias(node1, pragma[only_bind_into](state), _,
|
||||
pragma[only_bind_into](config))
|
||||
)
|
||||
}
|
||||
predicate flowOutOfCall = flowOutOfCallNodeCand2/6;
|
||||
|
||||
pragma[nomagic]
|
||||
predicate flowIntoCall(
|
||||
DataFlowCall call, ArgNodeEx node1, ParamNodeEx node2, boolean allowsFieldFlow,
|
||||
Configuration config
|
||||
) {
|
||||
exists(FlowState state |
|
||||
flowIntoCallNodeCand2(call, node1, node2, allowsFieldFlow, config) and
|
||||
PrevStage::revFlow(node2, pragma[only_bind_into](state), _, pragma[only_bind_into](config)) and
|
||||
PrevStage::revFlowAlias(node1, pragma[only_bind_into](state), _,
|
||||
pragma[only_bind_into](config))
|
||||
)
|
||||
}
|
||||
predicate flowIntoCall = flowIntoCallNodeCand2/5;
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate clearSet(NodeEx node, ContentSet c, Configuration config) {
|
||||
@@ -2531,8 +2291,8 @@ private module Stage4Param implements MkStage<Stage3>::StageParam {
|
||||
}
|
||||
}
|
||||
|
||||
private module Stage4 implements StageSig {
|
||||
import MkStage<Stage3>::Stage<Stage4Param>
|
||||
private module Stage3 implements StageSig {
|
||||
import MkStage<Stage2>::Stage<Stage3Param>
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -2543,8 +2303,8 @@ private predicate flowCandSummaryCtx(
|
||||
NodeEx node, FlowState state, AccessPathFront argApf, Configuration config
|
||||
) {
|
||||
exists(AccessPathFront apf |
|
||||
Stage4::revFlow(node, state, TReturnCtxMaybeFlowThrough(_), _, apf, config) and
|
||||
Stage4::fwdFlow(node, state, any(Stage4::CcCall ccc), _, TAccessPathFrontSome(argApf), apf,
|
||||
Stage3::revFlow(node, state, TReturnCtxMaybeFlowThrough(_), _, apf, config) and
|
||||
Stage3::fwdFlow(node, state, any(Stage3::CcCall ccc), _, TAccessPathFrontSome(argApf), apf,
|
||||
config)
|
||||
)
|
||||
}
|
||||
@@ -2555,10 +2315,10 @@ private predicate flowCandSummaryCtx(
|
||||
*/
|
||||
private predicate expensiveLen2unfolding(TypedContent tc, Configuration config) {
|
||||
exists(int tails, int nodes, int apLimit, int tupleLimit |
|
||||
tails = strictcount(AccessPathFront apf | Stage4::consCand(tc, apf, config)) and
|
||||
tails = strictcount(AccessPathFront apf | Stage3::consCand(tc, apf, config)) and
|
||||
nodes =
|
||||
strictcount(NodeEx n, FlowState state |
|
||||
Stage4::revFlow(n, state, any(AccessPathFrontHead apf | apf.getHead() = tc), config)
|
||||
Stage3::revFlow(n, state, any(AccessPathFrontHead apf | apf.getHead() = tc), config)
|
||||
or
|
||||
flowCandSummaryCtx(n, state, any(AccessPathFrontHead apf | apf.getHead() = tc), config)
|
||||
) and
|
||||
@@ -2572,11 +2332,11 @@ private predicate expensiveLen2unfolding(TypedContent tc, Configuration config)
|
||||
private newtype TAccessPathApprox =
|
||||
TNil(DataFlowType t) or
|
||||
TConsNil(TypedContent tc, DataFlowType t) {
|
||||
Stage4::consCand(tc, TFrontNil(t), _) and
|
||||
Stage3::consCand(tc, TFrontNil(t), _) and
|
||||
not expensiveLen2unfolding(tc, _)
|
||||
} or
|
||||
TConsCons(TypedContent tc1, TypedContent tc2, int len) {
|
||||
Stage4::consCand(tc1, TFrontHead(tc2), _) and
|
||||
Stage3::consCand(tc1, TFrontHead(tc2), _) and
|
||||
len in [2 .. accessPathLimit()] and
|
||||
not expensiveLen2unfolding(tc1, _)
|
||||
} or
|
||||
@@ -2706,7 +2466,7 @@ private class AccessPathApproxCons1 extends AccessPathApproxCons, TCons1 {
|
||||
override AccessPathApprox pop(TypedContent head) {
|
||||
head = tc and
|
||||
(
|
||||
exists(TypedContent tc2 | Stage4::consCand(tc, TFrontHead(tc2), _) |
|
||||
exists(TypedContent tc2 | Stage3::consCand(tc, TFrontHead(tc2), _) |
|
||||
result = TConsCons(tc2, _, len - 1)
|
||||
or
|
||||
len = 2 and
|
||||
@@ -2717,7 +2477,7 @@ private class AccessPathApproxCons1 extends AccessPathApproxCons, TCons1 {
|
||||
or
|
||||
exists(DataFlowType t |
|
||||
len = 1 and
|
||||
Stage4::consCand(tc, TFrontNil(t), _) and
|
||||
Stage3::consCand(tc, TFrontNil(t), _) and
|
||||
result = TNil(t)
|
||||
)
|
||||
)
|
||||
@@ -2742,14 +2502,13 @@ private class AccessPathApproxOption extends TAccessPathApproxOption {
|
||||
}
|
||||
}
|
||||
|
||||
private module Stage5Param implements MkStage<Stage4>::StageParam {
|
||||
private module PrevStage = Stage4;
|
||||
private module Stage4Param implements MkStage<Stage3>::StageParam {
|
||||
private module PrevStage = Stage3;
|
||||
|
||||
class Ap = AccessPathApprox;
|
||||
|
||||
class ApNil = AccessPathApproxNil;
|
||||
|
||||
pragma[nomagic]
|
||||
PrevStage::Ap getApprox(Ap ap) { result = ap.getFront() }
|
||||
|
||||
ApNil getApNil(NodeEx node) {
|
||||
@@ -2775,9 +2534,7 @@ private module Stage5Param implements MkStage<Stage4>::StageParam {
|
||||
NodeEx node1, FlowState state1, NodeEx node2, FlowState state2, boolean preservesValue,
|
||||
ApNil ap, Configuration config, LocalCc lcc
|
||||
) {
|
||||
localFlowBigStep(node1, state1, node2, state2, preservesValue, ap.getType(), config, lcc) and
|
||||
PrevStage::revFlow(node1, pragma[only_bind_into](state1), _, pragma[only_bind_into](config)) and
|
||||
PrevStage::revFlowAlias(node2, pragma[only_bind_into](state2), _, pragma[only_bind_into](config))
|
||||
localFlowBigStep(node1, state1, node2, state2, preservesValue, ap.getFront(), config, lcc)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
@@ -2814,7 +2571,7 @@ private module Stage5Param implements MkStage<Stage4>::StageParam {
|
||||
predicate typecheckStore(Ap ap, DataFlowType contentType) { any() }
|
||||
}
|
||||
|
||||
private module Stage5 = MkStage<Stage4>::Stage<Stage5Param>;
|
||||
private module Stage4 = MkStage<Stage3>::Stage<Stage4Param>;
|
||||
|
||||
bindingset[conf, result]
|
||||
private Configuration unbindConf(Configuration conf) {
|
||||
@@ -2828,8 +2585,8 @@ private predicate nodeMayUseSummary0(
|
||||
) {
|
||||
exists(AccessPathApprox apa0 |
|
||||
c = n.getEnclosingCallable() and
|
||||
Stage5::revFlow(n, state, TReturnCtxMaybeFlowThrough(_), _, apa0, config) and
|
||||
Stage5::fwdFlow(n, state, any(CallContextCall ccc), TParameterPositionSome(pos),
|
||||
Stage4::revFlow(n, state, TReturnCtxMaybeFlowThrough(_), _, apa0, config) and
|
||||
Stage4::fwdFlow(n, state, any(CallContextCall ccc), TParameterPositionSome(pos),
|
||||
TAccessPathApproxSome(apa), apa0, config)
|
||||
)
|
||||
}
|
||||
@@ -2839,7 +2596,7 @@ private predicate nodeMayUseSummary(
|
||||
NodeEx n, FlowState state, AccessPathApprox apa, Configuration config
|
||||
) {
|
||||
exists(DataFlowCallable c, ParameterPosition pos, ParamNodeEx p |
|
||||
Stage5::parameterMayFlowThrough(p, apa, config) and
|
||||
Stage4::parameterMayFlowThrough(p, apa, config) and
|
||||
nodeMayUseSummary0(n, c, pos, state, apa, config) and
|
||||
p.isParameterOf(c, pos)
|
||||
)
|
||||
@@ -2849,8 +2606,8 @@ private newtype TSummaryCtx =
|
||||
TSummaryCtxNone() or
|
||||
TSummaryCtxSome(ParamNodeEx p, FlowState state, AccessPath ap) {
|
||||
exists(Configuration config |
|
||||
Stage5::parameterMayFlowThrough(p, ap.getApprox(), config) and
|
||||
Stage5::revFlow(p, state, _, config)
|
||||
Stage4::parameterMayFlowThrough(p, ap.getApprox(), config) and
|
||||
Stage4::revFlow(p, state, _, config)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -2899,7 +2656,7 @@ private int count1to2unfold(AccessPathApproxCons1 apa, Configuration config) {
|
||||
len = apa.len() and
|
||||
result =
|
||||
strictcount(AccessPathFront apf |
|
||||
Stage5::consCand(tc, any(AccessPathApprox ap | ap.getFront() = apf and ap.len() = len - 1),
|
||||
Stage4::consCand(tc, any(AccessPathApprox ap | ap.getFront() = apf and ap.len() = len - 1),
|
||||
config)
|
||||
)
|
||||
)
|
||||
@@ -2908,7 +2665,7 @@ private int count1to2unfold(AccessPathApproxCons1 apa, Configuration config) {
|
||||
private int countNodesUsingAccessPath(AccessPathApprox apa, Configuration config) {
|
||||
result =
|
||||
strictcount(NodeEx n, FlowState state |
|
||||
Stage5::revFlow(n, state, apa, config) or nodeMayUseSummary(n, state, apa, config)
|
||||
Stage4::revFlow(n, state, apa, config) or nodeMayUseSummary(n, state, apa, config)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -2929,7 +2686,7 @@ private predicate expensiveLen1to2unfolding(AccessPathApproxCons1 apa, Configura
|
||||
private AccessPathApprox getATail(AccessPathApprox apa, Configuration config) {
|
||||
exists(TypedContent head |
|
||||
apa.pop(head) = result and
|
||||
Stage5::consCand(head, result, config)
|
||||
Stage4::consCand(head, result, config)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -3011,7 +2768,7 @@ private newtype TPathNode =
|
||||
NodeEx node, FlowState state, CallContext cc, SummaryCtx sc, AccessPath ap, Configuration config
|
||||
) {
|
||||
// A PathNode is introduced by a source ...
|
||||
Stage5::revFlow(node, state, config) and
|
||||
Stage4::revFlow(node, state, config) and
|
||||
sourceNode(node, state, config) and
|
||||
(
|
||||
if hasSourceCallCtx(config)
|
||||
@@ -3025,7 +2782,7 @@ private newtype TPathNode =
|
||||
exists(PathNodeMid mid |
|
||||
pathStep(mid, node, state, cc, sc, ap) and
|
||||
pragma[only_bind_into](config) = mid.getConfiguration() and
|
||||
Stage5::revFlow(node, state, ap.getApprox(), pragma[only_bind_into](config))
|
||||
Stage4::revFlow(node, state, ap.getApprox(), pragma[only_bind_into](config))
|
||||
)
|
||||
} or
|
||||
TPathNodeSink(NodeEx node, FlowState state, Configuration config) {
|
||||
@@ -3167,7 +2924,7 @@ private class AccessPathCons2 extends AccessPath, TAccessPathCons2 {
|
||||
override TypedContent getHead() { result = head1 }
|
||||
|
||||
override AccessPath getTail() {
|
||||
Stage5::consCand(head1, result.getApprox(), _) and
|
||||
Stage4::consCand(head1, result.getApprox(), _) and
|
||||
result.getHead() = head2 and
|
||||
result.length() = len - 1
|
||||
}
|
||||
@@ -3198,7 +2955,7 @@ private class AccessPathCons1 extends AccessPath, TAccessPathCons1 {
|
||||
override TypedContent getHead() { result = head }
|
||||
|
||||
override AccessPath getTail() {
|
||||
Stage5::consCand(head, result.getApprox(), _) and result.length() = len - 1
|
||||
Stage4::consCand(head, result.getApprox(), _) and result.length() = len - 1
|
||||
}
|
||||
|
||||
override AccessPathFrontHead getFront() { result = TFrontHead(head) }
|
||||
@@ -3590,8 +3347,7 @@ private predicate pathStep(
|
||||
AccessPath ap0, NodeEx midnode, FlowState state0, Configuration conf, LocalCallContext localCC
|
||||
|
|
||||
pathNode(mid, midnode, state0, cc, sc, ap0, conf, localCC) and
|
||||
localFlowBigStep(midnode, state0, node, state, false, ap.(AccessPathNil).getType(), conf,
|
||||
localCC) and
|
||||
localFlowBigStep(midnode, state0, node, state, false, ap.getFront(), conf, localCC) and
|
||||
ap0 instanceof AccessPathNil
|
||||
)
|
||||
or
|
||||
@@ -3633,7 +3389,7 @@ private predicate pathReadStep(
|
||||
) {
|
||||
ap0 = mid.getAp() and
|
||||
tc = ap0.getHead() and
|
||||
Stage5::readStepCand(mid.getNodeEx(), tc.getContent(), node, mid.getConfiguration()) and
|
||||
Stage4::readStepCand(mid.getNodeEx(), tc.getContent(), node, mid.getConfiguration()) and
|
||||
state = mid.getState() and
|
||||
cc = mid.getCallContext()
|
||||
}
|
||||
@@ -3643,7 +3399,7 @@ private predicate pathStoreStep(
|
||||
PathNodeMid mid, NodeEx node, FlowState state, AccessPath ap0, TypedContent tc, CallContext cc
|
||||
) {
|
||||
ap0 = mid.getAp() and
|
||||
Stage5::storeStepCand(mid.getNodeEx(), _, tc, node, _, mid.getConfiguration()) and
|
||||
Stage4::storeStepCand(mid.getNodeEx(), _, tc, node, _, mid.getConfiguration()) and
|
||||
state = mid.getState() and
|
||||
cc = mid.getCallContext()
|
||||
}
|
||||
@@ -3680,7 +3436,7 @@ private NodeEx getAnOutNodeFlow(
|
||||
ReturnKindExt kind, DataFlowCall call, AccessPathApprox apa, Configuration config
|
||||
) {
|
||||
result.asNode() = kind.getAnOutNode(call) and
|
||||
Stage5::revFlow(result, _, apa, config)
|
||||
Stage4::revFlow(result, _, apa, config)
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -3716,7 +3472,7 @@ private predicate parameterCand(
|
||||
DataFlowCallable callable, ParameterPosition pos, AccessPathApprox apa, Configuration config
|
||||
) {
|
||||
exists(ParamNodeEx p |
|
||||
Stage5::revFlow(p, _, apa, config) and
|
||||
Stage4::revFlow(p, _, apa, config) and
|
||||
p.isParameterOf(callable, pos)
|
||||
)
|
||||
}
|
||||
@@ -3995,17 +3751,9 @@ predicate stageStats(
|
||||
n = 45 and
|
||||
Stage4::stats(false, nodes, fields, conscand, states, tuples, config)
|
||||
or
|
||||
stage = "5 Fwd" and
|
||||
n = 50 and
|
||||
Stage5::stats(true, nodes, fields, conscand, states, tuples, config)
|
||||
stage = "5 Fwd" and n = 50 and finalStats(true, nodes, fields, conscand, states, tuples)
|
||||
or
|
||||
stage = "5 Rev" and
|
||||
n = 55 and
|
||||
Stage5::stats(false, nodes, fields, conscand, states, tuples, config)
|
||||
or
|
||||
stage = "6 Fwd" and n = 60 and finalStats(true, nodes, fields, conscand, states, tuples)
|
||||
or
|
||||
stage = "6 Rev" and n = 65 and finalStats(false, nodes, fields, conscand, states, tuples)
|
||||
stage = "5 Rev" and n = 55 and finalStats(false, nodes, fields, conscand, states, tuples)
|
||||
}
|
||||
|
||||
private module FlowExploration {
|
||||
|
||||
@@ -621,6 +621,23 @@ private predicate parameterFlowThroughAllowed(ParamNodeEx p, ReturnKindExt kind)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if flow from a parameter at position `pos` inside `c` to a return node of
|
||||
* kind `kind` is allowed.
|
||||
*
|
||||
* We don't expect a parameter to return stored in itself, unless
|
||||
* explicitly allowed
|
||||
*/
|
||||
bindingset[c, pos, kind]
|
||||
private predicate parameterFlowThroughAllowed(
|
||||
DataFlowCallable c, ParameterPosition pos, ReturnKindExt kind
|
||||
) {
|
||||
exists(ParamNodeEx p |
|
||||
p.isParameterOf(c, pos) and
|
||||
parameterFlowThroughAllowed(p, kind)
|
||||
)
|
||||
}
|
||||
|
||||
private module Stage1 implements StageSig {
|
||||
class Ap = Unit;
|
||||
|
||||
@@ -963,12 +980,6 @@ private module Stage1 implements StageSig {
|
||||
pragma[nomagic]
|
||||
predicate revFlow(NodeEx node, Configuration config) { revFlow(node, _, config) }
|
||||
|
||||
pragma[nomagic]
|
||||
predicate revFlowAp(NodeEx node, Ap ap, Configuration config) {
|
||||
revFlow(node, config) and
|
||||
exists(ap)
|
||||
}
|
||||
|
||||
bindingset[node, state, config]
|
||||
predicate revFlow(NodeEx node, FlowState state, Ap ap, Configuration config) {
|
||||
revFlow(node, _, pragma[only_bind_into](config)) and
|
||||
@@ -1011,13 +1022,9 @@ private module Stage1 implements StageSig {
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
predicate returnMayFlowThrough(
|
||||
RetNodeEx ret, Ap argAp, Ap ap, ReturnKindExt kind, Configuration config
|
||||
) {
|
||||
predicate returnMayFlowThrough(RetNodeEx ret, ReturnKindExt kind, Configuration config) {
|
||||
throughFlowNodeCand(ret, config) and
|
||||
kind = ret.getKind() and
|
||||
exists(argAp) and
|
||||
exists(ap)
|
||||
kind = ret.getKind()
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
@@ -1182,8 +1189,6 @@ private signature module StageSig {
|
||||
|
||||
predicate revFlow(NodeEx node, Configuration config);
|
||||
|
||||
predicate revFlowAp(NodeEx node, Ap ap, Configuration config);
|
||||
|
||||
bindingset[node, state, config]
|
||||
predicate revFlow(NodeEx node, FlowState state, Ap ap, Configuration config);
|
||||
|
||||
@@ -1191,9 +1196,7 @@ private signature module StageSig {
|
||||
|
||||
predicate parameterMayFlowThrough(ParamNodeEx p, Ap ap, Configuration config);
|
||||
|
||||
predicate returnMayFlowThrough(
|
||||
RetNodeEx ret, Ap argAp, Ap ap, ReturnKindExt kind, Configuration config
|
||||
);
|
||||
predicate returnMayFlowThrough(RetNodeEx ret, ReturnKindExt kind, Configuration config);
|
||||
|
||||
predicate storeStepCand(
|
||||
NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, DataFlowType contentType,
|
||||
@@ -1278,43 +1281,21 @@ private module MkStage<StageSig PrevStage> {
|
||||
import Param
|
||||
|
||||
/* Begin: Stage logic. */
|
||||
// use an alias as a workaround for bad functionality-induced joins
|
||||
pragma[nomagic]
|
||||
private predicate revFlowApAlias(NodeEx node, ApApprox apa, Configuration config) {
|
||||
PrevStage::revFlowAp(node, apa, config)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate flowIntoCallApa(
|
||||
DataFlowCall call, ArgNodeEx arg, ParamNodeEx p, boolean allowsFieldFlow, ApApprox apa,
|
||||
Configuration config
|
||||
) {
|
||||
flowIntoCall(call, arg, p, allowsFieldFlow, config) and
|
||||
PrevStage::revFlowAp(p, pragma[only_bind_into](apa), pragma[only_bind_into](config)) and
|
||||
revFlowApAlias(arg, pragma[only_bind_into](apa), pragma[only_bind_into](config))
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate flowOutOfCallApa(
|
||||
DataFlowCall call, RetNodeEx ret, ReturnKindExt kind, NodeEx out, boolean allowsFieldFlow,
|
||||
ApApprox apa, Configuration config
|
||||
) {
|
||||
flowOutOfCall(call, ret, kind, out, allowsFieldFlow, config) and
|
||||
PrevStage::revFlowAp(out, pragma[only_bind_into](apa), pragma[only_bind_into](config)) and
|
||||
revFlowApAlias(ret, pragma[only_bind_into](apa), pragma[only_bind_into](config))
|
||||
bindingset[result, apa]
|
||||
private ApApprox unbindApa(ApApprox apa) {
|
||||
pragma[only_bind_out](apa) = pragma[only_bind_out](result)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate flowThroughOutOfCall(
|
||||
DataFlowCall call, CcCall ccc, RetNodeEx ret, NodeEx out, boolean allowsFieldFlow,
|
||||
ApApprox argApa, ApApprox apa, Configuration config
|
||||
DataFlowCall call, DataFlowCallable c, CcCall ccc, RetNodeEx ret, ReturnKindExt kind,
|
||||
NodeEx out, boolean allowsFieldFlow, Configuration config
|
||||
) {
|
||||
exists(ReturnKindExt kind |
|
||||
flowOutOfCallApa(call, ret, kind, out, allowsFieldFlow, apa, pragma[only_bind_into](config)) and
|
||||
PrevStage::callMayFlowThroughRev(call, pragma[only_bind_into](config)) and
|
||||
PrevStage::returnMayFlowThrough(ret, argApa, apa, kind, pragma[only_bind_into](config)) and
|
||||
matchesCall(ccc, call)
|
||||
)
|
||||
flowOutOfCall(call, ret, kind, out, allowsFieldFlow, pragma[only_bind_into](config)) and
|
||||
PrevStage::callMayFlowThroughRev(call, pragma[only_bind_into](config)) and
|
||||
PrevStage::returnMayFlowThrough(ret, kind, pragma[only_bind_into](config)) and
|
||||
matchesCall(ccc, call) and
|
||||
c = ret.getEnclosingCallable()
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1326,50 +1307,39 @@ private module MkStage<StageSig PrevStage> {
|
||||
* corresponding parameter position and access path of that argument, respectively.
|
||||
*/
|
||||
pragma[nomagic]
|
||||
additional predicate fwdFlow(
|
||||
NodeEx node, FlowState state, Cc cc, ParameterPositionOption summaryCtx, ApOption argAp,
|
||||
Ap ap, ApApprox apa, Configuration config
|
||||
) {
|
||||
fwdFlow0(node, state, cc, summaryCtx, argAp, ap, apa, config) and
|
||||
PrevStage::revFlow(node, state, apa, config) and
|
||||
filter(node, state, ap, config)
|
||||
}
|
||||
|
||||
pragma[inline]
|
||||
additional predicate fwdFlow(
|
||||
NodeEx node, FlowState state, Cc cc, ParameterPositionOption summaryCtx, ApOption argAp,
|
||||
Ap ap, Configuration config
|
||||
) {
|
||||
fwdFlow(node, state, cc, summaryCtx, argAp, ap, _, config)
|
||||
fwdFlow0(node, state, cc, summaryCtx, argAp, ap, config) and
|
||||
PrevStage::revFlow(node, state, unbindApa(getApprox(ap)), config) and
|
||||
filter(node, state, ap, config)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate fwdFlow0(
|
||||
NodeEx node, FlowState state, Cc cc, ParameterPositionOption summaryCtx, ApOption argAp,
|
||||
Ap ap, ApApprox apa, Configuration config
|
||||
Ap ap, Configuration config
|
||||
) {
|
||||
sourceNode(node, state, config) and
|
||||
(if hasSourceCallCtx(config) then cc = ccSomeCall() else cc = ccNone()) and
|
||||
argAp = apNone() and
|
||||
summaryCtx = TParameterPositionNone() and
|
||||
ap = getApNil(node) and
|
||||
apa = getApprox(ap)
|
||||
ap = getApNil(node)
|
||||
or
|
||||
exists(NodeEx mid, FlowState state0, Ap ap0, ApApprox apa0, LocalCc localCc |
|
||||
fwdFlow(mid, state0, cc, summaryCtx, argAp, ap0, apa0, config) and
|
||||
exists(NodeEx mid, FlowState state0, Ap ap0, LocalCc localCc |
|
||||
fwdFlow(mid, state0, cc, summaryCtx, argAp, ap0, config) and
|
||||
localCc = getLocalCc(mid, cc)
|
||||
|
|
||||
localStep(mid, state0, node, state, true, _, config, localCc) and
|
||||
ap = ap0 and
|
||||
apa = apa0
|
||||
ap = ap0
|
||||
or
|
||||
localStep(mid, state0, node, state, false, ap, config, localCc) and
|
||||
ap0 instanceof ApNil and
|
||||
apa = getApprox(ap)
|
||||
ap0 instanceof ApNil
|
||||
)
|
||||
or
|
||||
exists(NodeEx mid |
|
||||
fwdFlow(mid, pragma[only_bind_into](state), _, _, _, ap, apa, pragma[only_bind_into](config)) and
|
||||
fwdFlow(mid, pragma[only_bind_into](state), _, _, _, ap, pragma[only_bind_into](config)) and
|
||||
jumpStep(mid, node, config) and
|
||||
cc = ccNone() and
|
||||
summaryCtx = TParameterPositionNone() and
|
||||
@@ -1382,8 +1352,7 @@ private module MkStage<StageSig PrevStage> {
|
||||
cc = ccNone() and
|
||||
summaryCtx = TParameterPositionNone() and
|
||||
argAp = apNone() and
|
||||
ap = getApNil(node) and
|
||||
apa = getApprox(ap)
|
||||
ap = getApNil(node)
|
||||
)
|
||||
or
|
||||
exists(NodeEx mid, FlowState state0, ApNil nil |
|
||||
@@ -1392,32 +1361,32 @@ private module MkStage<StageSig PrevStage> {
|
||||
cc = ccNone() and
|
||||
summaryCtx = TParameterPositionNone() and
|
||||
argAp = apNone() and
|
||||
ap = getApNil(node) and
|
||||
apa = getApprox(ap)
|
||||
ap = getApNil(node)
|
||||
)
|
||||
or
|
||||
// store
|
||||
exists(TypedContent tc, Ap ap0 |
|
||||
fwdFlowStore(_, ap0, tc, node, state, cc, summaryCtx, argAp, config) and
|
||||
ap = apCons(tc, ap0) and
|
||||
apa = getApprox(ap)
|
||||
ap = apCons(tc, ap0)
|
||||
)
|
||||
or
|
||||
// read
|
||||
exists(Ap ap0, Content c |
|
||||
fwdFlowRead(ap0, c, _, node, state, cc, summaryCtx, argAp, config) and
|
||||
fwdFlowConsCand(ap0, c, ap, config) and
|
||||
apa = getApprox(ap)
|
||||
fwdFlowConsCand(ap0, c, ap, config)
|
||||
)
|
||||
or
|
||||
// flow into a callable
|
||||
fwdFlowIn(_, node, state, _, cc, _, _, ap, apa, config) and
|
||||
if PrevStage::parameterMayFlowThrough(node, apa, config)
|
||||
then (
|
||||
summaryCtx = TParameterPositionSome(node.(ParamNodeEx).getPosition()) and
|
||||
argAp = apSome(ap)
|
||||
) else (
|
||||
summaryCtx = TParameterPositionNone() and argAp = apNone()
|
||||
exists(ApApprox apa |
|
||||
fwdFlowIn(_, node, state, _, cc, _, _, ap, config) and
|
||||
apa = getApprox(ap) and
|
||||
if PrevStage::parameterMayFlowThrough(node, apa, config)
|
||||
then (
|
||||
summaryCtx = TParameterPositionSome(node.(ParamNodeEx).getPosition()) and
|
||||
argAp = apSome(ap)
|
||||
) else (
|
||||
summaryCtx = TParameterPositionNone() and argAp = apNone()
|
||||
)
|
||||
)
|
||||
or
|
||||
// flow out of a callable
|
||||
@@ -1425,8 +1394,8 @@ private module MkStage<StageSig PrevStage> {
|
||||
DataFlowCall call, RetNodeEx ret, boolean allowsFieldFlow, CcNoCall innercc,
|
||||
DataFlowCallable inner
|
||||
|
|
||||
fwdFlow(ret, state, innercc, summaryCtx, argAp, ap, apa, config) and
|
||||
flowOutOfCallApa(call, ret, _, node, allowsFieldFlow, apa, config) and
|
||||
fwdFlow(ret, state, innercc, summaryCtx, argAp, ap, config) and
|
||||
flowOutOfCall(call, ret, _, node, allowsFieldFlow, config) and
|
||||
inner = ret.getEnclosingCallable() and
|
||||
cc = getCallContextReturn(inner, call, innercc) and
|
||||
if allowsFieldFlow = false then ap instanceof ApNil else any()
|
||||
@@ -1434,7 +1403,7 @@ private module MkStage<StageSig PrevStage> {
|
||||
or
|
||||
// flow through a callable
|
||||
exists(DataFlowCall call, ParameterPosition summaryCtx0, Ap argAp0 |
|
||||
fwdFlowOutFromArg(call, node, state, summaryCtx0, argAp0, ap, apa, config) and
|
||||
fwdFlowOutFromArg(call, node, state, summaryCtx0, argAp0, ap, config) and
|
||||
fwdFlowIsEntered(call, cc, summaryCtx, argAp, summaryCtx0, argAp0, config)
|
||||
)
|
||||
}
|
||||
@@ -1444,9 +1413,9 @@ private module MkStage<StageSig PrevStage> {
|
||||
NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, FlowState state, Cc cc,
|
||||
ParameterPositionOption summaryCtx, ApOption argAp, Configuration config
|
||||
) {
|
||||
exists(DataFlowType contentType, ApApprox apa1 |
|
||||
fwdFlow(node1, state, cc, summaryCtx, argAp, ap1, apa1, config) and
|
||||
PrevStage::storeStepCand(node1, apa1, tc, node2, contentType, config) and
|
||||
exists(DataFlowType contentType |
|
||||
fwdFlow(node1, state, cc, summaryCtx, argAp, ap1, config) and
|
||||
PrevStage::storeStepCand(node1, unbindApa(getApprox(ap1)), tc, node2, contentType, config) and
|
||||
typecheckStore(ap1, contentType)
|
||||
)
|
||||
}
|
||||
@@ -1464,104 +1433,43 @@ private module MkStage<StageSig PrevStage> {
|
||||
)
|
||||
}
|
||||
|
||||
private class ApNonNil instanceof Ap {
|
||||
pragma[nomagic]
|
||||
ApNonNil() { not this instanceof ApNil }
|
||||
|
||||
string toString() { result = "" }
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate fwdFlowRead0(
|
||||
NodeEx node1, FlowState state, Cc cc, ParameterPositionOption summaryCtx, ApOption argAp,
|
||||
ApNonNil ap, Configuration config
|
||||
) {
|
||||
fwdFlow(node1, state, cc, summaryCtx, argAp, ap, config) and
|
||||
PrevStage::readStepCand(node1, _, _, config)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate fwdFlowRead(
|
||||
Ap ap, Content c, NodeEx node1, NodeEx node2, FlowState state, Cc cc,
|
||||
ParameterPositionOption summaryCtx, ApOption argAp, Configuration config
|
||||
) {
|
||||
fwdFlowRead0(node1, state, cc, summaryCtx, argAp, ap, config) and
|
||||
fwdFlow(node1, state, cc, summaryCtx, argAp, ap, config) and
|
||||
PrevStage::readStepCand(node1, c, node2, config) and
|
||||
getHeadContent(ap) = c
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate fwdFlowIn(
|
||||
DataFlowCall call, ParamNodeEx p, FlowState state, Cc outercc, CcCall innercc,
|
||||
ParameterPositionOption summaryCtx, ApOption argAp, Ap ap, ApApprox apa, Configuration config
|
||||
DataFlowCall call, ParamNodeEx p, FlowState state, Cc outercc, Cc innercc,
|
||||
ParameterPositionOption summaryCtx, ApOption argAp, Ap ap, Configuration config
|
||||
) {
|
||||
exists(ArgNodeEx arg, boolean allowsFieldFlow |
|
||||
fwdFlow(arg, state, outercc, summaryCtx, argAp, ap, apa, config) and
|
||||
flowIntoCallApa(call, arg, p, allowsFieldFlow, apa, config) and
|
||||
fwdFlow(arg, state, outercc, summaryCtx, argAp, ap, config) and
|
||||
flowIntoCall(call, arg, p, allowsFieldFlow, config) and
|
||||
innercc = getCallContextCall(call, p.getEnclosingCallable(), outercc) and
|
||||
if allowsFieldFlow = false then ap instanceof ApNil else any()
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate fwdFlowRetFromArg(
|
||||
RetNodeEx ret, FlowState state, CcCall ccc, ParameterPosition summaryCtx, ParamNodeEx p,
|
||||
Ap argAp, ApApprox argApa, Ap ap, ApApprox apa, Configuration config
|
||||
) {
|
||||
exists(DataFlowCallable c, ReturnKindExt kind |
|
||||
fwdFlow(pragma[only_bind_into](ret), state, ccc,
|
||||
TParameterPositionSome(pragma[only_bind_into](summaryCtx)), apSome(argAp), ap, apa, config) and
|
||||
getApprox(argAp) = argApa and
|
||||
c = ret.getEnclosingCallable() and
|
||||
kind = ret.getKind() and
|
||||
p.isParameterOf(c, pragma[only_bind_into](summaryCtx)) and
|
||||
parameterFlowThroughAllowed(p, kind)
|
||||
)
|
||||
}
|
||||
|
||||
pragma[inline]
|
||||
private predicate fwdFlowInMayFlowThrough(
|
||||
DataFlowCall call, Cc cc, CcCall innerCc, ParameterPositionOption summaryCtx, ApOption argAp,
|
||||
ParamNodeEx param, Ap ap, ApApprox apa, Configuration config
|
||||
) {
|
||||
fwdFlowIn(call, pragma[only_bind_into](param), _, cc, innerCc, summaryCtx, argAp, ap,
|
||||
pragma[only_bind_into](apa), pragma[only_bind_into](config)) and
|
||||
PrevStage::parameterMayFlowThrough(param, apa, config)
|
||||
}
|
||||
|
||||
// dedup before joining with `flowThroughOutOfCall`
|
||||
pragma[nomagic]
|
||||
private predicate fwdFlowInMayFlowThroughProj(
|
||||
DataFlowCall call, CcCall innerCc, ApApprox apa, Configuration config
|
||||
) {
|
||||
fwdFlowInMayFlowThrough(call, _, innerCc, _, _, _, _, apa, config)
|
||||
}
|
||||
|
||||
/**
|
||||
* Same as `flowThroughOutOfCall`, but restricted to calls that are reached
|
||||
* in the flow covered by `fwdFlow`, where data might flow through the target
|
||||
* callable and back out at `call`.
|
||||
*/
|
||||
pragma[nomagic]
|
||||
private predicate fwdFlowThroughOutOfCall(
|
||||
DataFlowCall call, CcCall ccc, RetNodeEx ret, NodeEx out, boolean allowsFieldFlow,
|
||||
ApApprox argApa, ApApprox apa, Configuration config
|
||||
) {
|
||||
fwdFlowInMayFlowThroughProj(call, ccc, argApa, config) and
|
||||
flowThroughOutOfCall(call, ccc, ret, out, allowsFieldFlow, argApa, apa, config)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate fwdFlowOutFromArg(
|
||||
DataFlowCall call, NodeEx out, FlowState state, ParameterPosition summaryCtx, Ap argAp, Ap ap,
|
||||
ApApprox apa, Configuration config
|
||||
Configuration config
|
||||
) {
|
||||
exists(RetNodeEx ret, boolean allowsFieldFlow, CcCall ccc, ApApprox argApa |
|
||||
fwdFlowRetFromArg(pragma[only_bind_into](ret), state, pragma[only_bind_into](ccc),
|
||||
summaryCtx, _, argAp, pragma[only_bind_into](argApa), ap, pragma[only_bind_into](apa),
|
||||
exists(
|
||||
DataFlowCallable c, RetNodeEx ret, ReturnKindExt kind, boolean allowsFieldFlow, CcCall ccc
|
||||
|
|
||||
fwdFlow(pragma[only_bind_into](ret), state, pragma[only_bind_into](ccc),
|
||||
TParameterPositionSome(pragma[only_bind_into](summaryCtx)), apSome(argAp), ap, config) and
|
||||
flowThroughOutOfCall(call, pragma[only_bind_into](c), ccc, ret, kind, out, allowsFieldFlow,
|
||||
config) and
|
||||
fwdFlowThroughOutOfCall(call, ccc, ret, out, allowsFieldFlow, argApa, apa, config) and
|
||||
(if allowsFieldFlow = false then ap instanceof ApNil else any())
|
||||
(if allowsFieldFlow = false then ap instanceof ApNil else any()) and
|
||||
parameterFlowThroughAllowed(c, pragma[only_bind_into](summaryCtx), kind)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1575,7 +1483,8 @@ private module MkStage<StageSig PrevStage> {
|
||||
ParameterPosition pos, Ap ap, Configuration config
|
||||
) {
|
||||
exists(ParamNodeEx param |
|
||||
fwdFlowInMayFlowThrough(call, cc, _, summaryCtx, argAp, param, ap, _, config) and
|
||||
fwdFlowIn(call, param, _, cc, _, summaryCtx, argAp, ap, config) and
|
||||
PrevStage::parameterMayFlowThrough(param, unbindApa(getApprox(ap)), config) and
|
||||
pos = param.getPosition()
|
||||
)
|
||||
}
|
||||
@@ -1596,55 +1505,41 @@ private module MkStage<StageSig PrevStage> {
|
||||
fwdFlowConsCand(ap1, c, ap2, config)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate returnFlowsThrough0(
|
||||
RetNodeEx ret, ReturnKindExt kind, FlowState state, CcCall ccc, DataFlowCallable c,
|
||||
ParameterPosition ppos, Ap argAp, Ap ap, Configuration config
|
||||
) {
|
||||
exists(boolean allowsFieldFlow |
|
||||
fwdFlow(ret, state, ccc, TParameterPositionSome(ppos), apSome(argAp), ap, config) and
|
||||
flowThroughOutOfCall(_, c, _, pragma[only_bind_into](ret), kind, _, allowsFieldFlow,
|
||||
pragma[only_bind_into](config)) and
|
||||
if allowsFieldFlow = false then ap instanceof ApNil else any()
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate returnFlowsThrough(
|
||||
RetNodeEx ret, ReturnKindExt kind, FlowState state, CcCall ccc, ParamNodeEx p, Ap argAp,
|
||||
Ap ap, Configuration config
|
||||
) {
|
||||
exists(boolean allowsFieldFlow, ApApprox argApa, ApApprox apa |
|
||||
fwdFlowRetFromArg(pragma[only_bind_into](ret), state, pragma[only_bind_into](ccc), _, p,
|
||||
argAp, pragma[only_bind_into](argApa), ap, pragma[only_bind_into](apa), config) and
|
||||
kind = ret.getKind() and
|
||||
fwdFlowThroughOutOfCall(_, ccc, ret, _, allowsFieldFlow, argApa, apa, config) and
|
||||
(if allowsFieldFlow = false then ap instanceof ApNil else any())
|
||||
exists(DataFlowCallable c, ParameterPosition ppos |
|
||||
returnFlowsThrough0(ret, kind, state, ccc, c, ppos, argAp, ap, config) and
|
||||
p.isParameterOf(c, ppos) and
|
||||
parameterFlowThroughAllowed(p, kind)
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate flowThroughIntoCall(
|
||||
DataFlowCall call, ArgNodeEx arg, ParamNodeEx p, boolean allowsFieldFlow, Ap argAp,
|
||||
Configuration config
|
||||
DataFlowCall call, ArgNodeEx arg, ParamNodeEx p, boolean allowsFieldFlow, Configuration config
|
||||
) {
|
||||
exists(ApApprox argApa |
|
||||
flowIntoCallApa(call, pragma[only_bind_into](arg), pragma[only_bind_into](p),
|
||||
allowsFieldFlow, argApa, pragma[only_bind_into](config)) and
|
||||
fwdFlow(arg, _, _, _, _, pragma[only_bind_into](argAp), argApa,
|
||||
exists(Ap argAp |
|
||||
flowIntoCall(call, pragma[only_bind_into](arg), pragma[only_bind_into](p), allowsFieldFlow,
|
||||
pragma[only_bind_into](config)) and
|
||||
fwdFlow(arg, _, _, _, _, pragma[only_bind_into](argAp), pragma[only_bind_into](config)) and
|
||||
returnFlowsThrough(_, _, _, _, p, pragma[only_bind_into](argAp), _,
|
||||
pragma[only_bind_into](config)) and
|
||||
if allowsFieldFlow = false then argAp instanceof ApNil else any()
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate flowIntoCallAp(
|
||||
DataFlowCall call, ArgNodeEx arg, ParamNodeEx p, boolean allowsFieldFlow, Ap ap,
|
||||
Configuration config
|
||||
) {
|
||||
exists(ApApprox apa |
|
||||
flowIntoCallApa(call, arg, p, allowsFieldFlow, apa, config) and
|
||||
fwdFlow(arg, _, _, _, _, ap, apa, config)
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate flowOutOfCallAp(
|
||||
DataFlowCall call, RetNodeEx ret, ReturnKindExt kind, NodeEx out, boolean allowsFieldFlow,
|
||||
Ap ap, Configuration config
|
||||
) {
|
||||
exists(ApApprox apa |
|
||||
flowOutOfCallApa(call, ret, kind, out, allowsFieldFlow, apa, config) and
|
||||
fwdFlow(ret, _, _, _, _, ap, apa, config)
|
||||
pragma[only_bind_into](config))
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1733,7 +1628,7 @@ private module MkStage<StageSig PrevStage> {
|
||||
// flow into a callable
|
||||
exists(ParamNodeEx p, boolean allowsFieldFlow |
|
||||
revFlow(p, state, TReturnCtxNone(), returnAp, ap, config) and
|
||||
flowIntoCallAp(_, node, p, allowsFieldFlow, ap, config) and
|
||||
flowIntoCall(_, node, p, allowsFieldFlow, config) and
|
||||
(if allowsFieldFlow = false then ap instanceof ApNil else any()) and
|
||||
returnCtx = TReturnCtxNone()
|
||||
)
|
||||
@@ -1787,41 +1682,22 @@ private module MkStage<StageSig PrevStage> {
|
||||
) {
|
||||
exists(NodeEx out, boolean allowsFieldFlow |
|
||||
revFlow(out, state, returnCtx, returnAp, ap, config) and
|
||||
flowOutOfCallAp(call, ret, kind, out, allowsFieldFlow, ap, config) and
|
||||
flowOutOfCall(call, ret, kind, out, allowsFieldFlow, config) and
|
||||
if allowsFieldFlow = false then ap instanceof ApNil else any()
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Same as `flowThroughIntoCall`, but restricted to calls that are reached
|
||||
* in the flow covered by `revFlow`, where data might flow through the target
|
||||
* callable and back out at `call`.
|
||||
*/
|
||||
pragma[nomagic]
|
||||
private predicate revFlowThroughIntoCall(
|
||||
DataFlowCall call, ArgNodeEx arg, ParamNodeEx p, boolean allowsFieldFlow, Ap argAp,
|
||||
Configuration config
|
||||
) {
|
||||
flowThroughIntoCall(call, arg, p, allowsFieldFlow, argAp, config) and
|
||||
revFlowIsReturned(call, _, _, _, _, config)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate revFlowParamToReturn(
|
||||
ParamNodeEx p, FlowState state, ReturnKindExt kind, Ap returnAp, Ap ap, Configuration config
|
||||
) {
|
||||
revFlow(p, state, TReturnCtxMaybeFlowThrough(kind), apSome(returnAp), ap, config) and
|
||||
parameterFlowThroughAllowed(p, kind)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate revFlowInToReturn(
|
||||
DataFlowCall call, ArgNodeEx arg, FlowState state, ReturnKindExt kind, Ap returnAp, Ap ap,
|
||||
Configuration config
|
||||
) {
|
||||
exists(ParamNodeEx p, boolean allowsFieldFlow |
|
||||
revFlowParamToReturn(p, state, kind, returnAp, ap, config) and
|
||||
revFlowThroughIntoCall(call, arg, p, allowsFieldFlow, ap, config)
|
||||
revFlow(pragma[only_bind_into](p), state,
|
||||
TReturnCtxMaybeFlowThrough(pragma[only_bind_into](kind)), apSome(returnAp), ap, config) and
|
||||
flowThroughIntoCall(call, arg, p, allowsFieldFlow, config) and
|
||||
(if allowsFieldFlow = false then ap instanceof ApNil else any()) and
|
||||
parameterFlowThroughAllowed(p, kind)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1874,11 +1750,6 @@ private module MkStage<StageSig PrevStage> {
|
||||
pragma[nomagic]
|
||||
predicate revFlow(NodeEx node, Configuration config) { revFlow(node, _, _, _, _, config) }
|
||||
|
||||
pragma[nomagic]
|
||||
predicate revFlowAp(NodeEx node, Ap ap, Configuration config) {
|
||||
revFlow(node, _, _, _, ap, config)
|
||||
}
|
||||
|
||||
// use an alias as a workaround for bad functionality-induced joins
|
||||
pragma[nomagic]
|
||||
additional predicate revFlowAlias(NodeEx node, Configuration config) {
|
||||
@@ -1915,9 +1786,9 @@ private module MkStage<StageSig PrevStage> {
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate parameterFlowsThroughRev(
|
||||
ParamNodeEx p, Ap ap, ReturnKindExt kind, Ap returnAp, Configuration config
|
||||
ParamNodeEx p, Ap ap, ReturnKindExt kind, Configuration config
|
||||
) {
|
||||
revFlow(p, _, TReturnCtxMaybeFlowThrough(kind), apSome(returnAp), ap, config) and
|
||||
revFlow(p, _, TReturnCtxMaybeFlowThrough(kind), apSome(_), ap, config) and
|
||||
parameterFlowThroughAllowed(p, kind)
|
||||
}
|
||||
|
||||
@@ -1925,36 +1796,27 @@ private module MkStage<StageSig PrevStage> {
|
||||
predicate parameterMayFlowThrough(ParamNodeEx p, Ap ap, Configuration config) {
|
||||
exists(RetNodeEx ret, ReturnKindExt kind |
|
||||
returnFlowsThrough(ret, kind, _, _, p, ap, _, config) and
|
||||
parameterFlowsThroughRev(p, ap, kind, _, config)
|
||||
parameterFlowsThroughRev(p, ap, kind, config)
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
predicate returnMayFlowThrough(
|
||||
RetNodeEx ret, Ap argAp, Ap ap, ReturnKindExt kind, Configuration config
|
||||
) {
|
||||
exists(ParamNodeEx p |
|
||||
returnFlowsThrough(ret, kind, _, _, p, argAp, ap, config) and
|
||||
parameterFlowsThroughRev(p, argAp, kind, ap, config)
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
predicate revFlowInToReturnIsReturned(
|
||||
DataFlowCall call, ArgNodeEx arg, FlowState state, ReturnCtx returnCtx, ApOption returnAp,
|
||||
Ap ap, Configuration config
|
||||
) {
|
||||
exists(ReturnKindExt returnKind0, Ap returnAp0 |
|
||||
revFlowInToReturn(call, arg, state, returnKind0, returnAp0, ap, config) and
|
||||
revFlowIsReturned(call, returnCtx, returnAp, returnKind0, returnAp0, config)
|
||||
predicate returnMayFlowThrough(RetNodeEx ret, ReturnKindExt kind, Configuration config) {
|
||||
exists(ParamNodeEx p, Ap ap |
|
||||
returnFlowsThrough(ret, kind, _, _, p, ap, _, config) and
|
||||
parameterFlowsThroughRev(p, ap, kind, config)
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
predicate callMayFlowThroughRev(DataFlowCall call, Configuration config) {
|
||||
exists(ArgNodeEx arg, FlowState state, ReturnCtx returnCtx, ApOption returnAp, Ap ap |
|
||||
exists(
|
||||
ReturnKindExt returnKind0, Ap returnAp0, ArgNodeEx arg, FlowState state,
|
||||
ReturnCtx returnCtx, ApOption returnAp, Ap ap
|
||||
|
|
||||
revFlow(arg, state, returnCtx, returnAp, ap, config) and
|
||||
revFlowInToReturnIsReturned(call, arg, state, returnCtx, returnAp, ap, config)
|
||||
revFlowInToReturn(call, arg, state, returnKind0, returnAp0, ap, config) and
|
||||
revFlowIsReturned(call, returnCtx, returnAp, returnKind0, returnAp0, config)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -2317,16 +2179,16 @@ private module LocalFlowBigStep {
|
||||
pragma[nomagic]
|
||||
predicate localFlowBigStep(
|
||||
NodeEx node1, FlowState state1, NodeEx node2, FlowState state2, boolean preservesValue,
|
||||
DataFlowType t, Configuration config, LocalCallContext callContext
|
||||
AccessPathFrontNil apf, Configuration config, LocalCallContext callContext
|
||||
) {
|
||||
localFlowStepPlus(node1, state1, node2, preservesValue, t, config, callContext) and
|
||||
localFlowStepPlus(node1, state1, node2, preservesValue, apf.getType(), config, callContext) and
|
||||
localFlowExit(node2, state1, config) and
|
||||
state1 = state2
|
||||
or
|
||||
additionalLocalFlowStepNodeCand2(node1, state1, node2, state2, config) and
|
||||
state1 != state2 and
|
||||
preservesValue = false and
|
||||
t = node2.getDataFlowType() and
|
||||
apf = TFrontNil(node2.getDataFlowType()) and
|
||||
callContext.relevantFor(node1.getEnclosingCallable()) and
|
||||
not exists(DataFlowCall call | call = callContext.(LocalCallContextSpecificCall).getCall() |
|
||||
isUnreachableInCallCached(node1.asNode(), call) or
|
||||
@@ -2340,87 +2202,11 @@ private import LocalFlowBigStep
|
||||
private module Stage3Param implements MkStage<Stage2>::StageParam {
|
||||
private module PrevStage = Stage2;
|
||||
|
||||
class Ap = ApproxAccessPathFront;
|
||||
|
||||
class ApNil = ApproxAccessPathFrontNil;
|
||||
|
||||
PrevStage::Ap getApprox(Ap ap) { result = ap.toBoolNonEmpty() }
|
||||
|
||||
ApNil getApNil(NodeEx node) {
|
||||
PrevStage::revFlow(node, _) and result = TApproxFrontNil(node.getDataFlowType())
|
||||
}
|
||||
|
||||
bindingset[tc, tail]
|
||||
Ap apCons(TypedContent tc, Ap tail) { result.getAHead() = tc and exists(tail) }
|
||||
|
||||
pragma[noinline]
|
||||
Content getHeadContent(Ap ap) { result = ap.getAHead().getContent() }
|
||||
|
||||
class ApOption = ApproxAccessPathFrontOption;
|
||||
|
||||
ApOption apNone() { result = TApproxAccessPathFrontNone() }
|
||||
|
||||
ApOption apSome(Ap ap) { result = TApproxAccessPathFrontSome(ap) }
|
||||
|
||||
import BooleanCallContext
|
||||
|
||||
predicate localStep(
|
||||
NodeEx node1, FlowState state1, NodeEx node2, FlowState state2, boolean preservesValue,
|
||||
ApproxAccessPathFrontNil ap, Configuration config, LocalCc lcc
|
||||
) {
|
||||
localFlowBigStep(node1, state1, node2, state2, preservesValue, ap.getType(), config, _) and
|
||||
exists(lcc)
|
||||
}
|
||||
|
||||
predicate flowOutOfCall = flowOutOfCallNodeCand2/6;
|
||||
|
||||
predicate flowIntoCall = flowIntoCallNodeCand2/5;
|
||||
|
||||
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.getAHead().getContent()
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate castingNodeEx(NodeEx node) { node.asNode() instanceof CastingNode }
|
||||
|
||||
bindingset[node, state, ap, config]
|
||||
predicate filter(NodeEx node, FlowState state, Ap ap, Configuration config) {
|
||||
exists(state) and
|
||||
exists(config) and
|
||||
(if castingNodeEx(node) then compatibleTypes(node.getDataFlowType(), ap.getType()) else any()) and
|
||||
(
|
||||
notExpectsContent(node)
|
||||
or
|
||||
expectsContentCand(node, ap, config)
|
||||
)
|
||||
}
|
||||
|
||||
bindingset[ap, contentType]
|
||||
predicate typecheckStore(Ap ap, DataFlowType contentType) {
|
||||
// We need to typecheck stores here, since reverse flow through a getter
|
||||
// might have a different type here compared to inside the getter.
|
||||
compatibleTypes(ap.getType(), contentType)
|
||||
}
|
||||
}
|
||||
|
||||
private module Stage3 implements StageSig {
|
||||
import MkStage<Stage2>::Stage<Stage3Param>
|
||||
}
|
||||
|
||||
private module Stage4Param implements MkStage<Stage3>::StageParam {
|
||||
private module PrevStage = Stage3;
|
||||
|
||||
class Ap = AccessPathFront;
|
||||
|
||||
class ApNil = AccessPathFrontNil;
|
||||
|
||||
PrevStage::Ap getApprox(Ap ap) { result = ap.toApprox() }
|
||||
PrevStage::Ap getApprox(Ap ap) { result = ap.toBoolNonEmpty() }
|
||||
|
||||
ApNil getApNil(NodeEx node) {
|
||||
PrevStage::revFlow(node, _) and result = TFrontNil(node.getDataFlowType())
|
||||
@@ -2440,42 +2226,16 @@ private module Stage4Param implements MkStage<Stage3>::StageParam {
|
||||
|
||||
import BooleanCallContext
|
||||
|
||||
pragma[nomagic]
|
||||
predicate localStep(
|
||||
NodeEx node1, FlowState state1, NodeEx node2, FlowState state2, boolean preservesValue,
|
||||
ApNil ap, Configuration config, LocalCc lcc
|
||||
) {
|
||||
localFlowBigStep(node1, state1, node2, state2, preservesValue, ap.getType(), config, _) and
|
||||
PrevStage::revFlow(node1, pragma[only_bind_into](state1), _, pragma[only_bind_into](config)) and
|
||||
PrevStage::revFlowAlias(node2, pragma[only_bind_into](state2), _, pragma[only_bind_into](config)) and
|
||||
exists(lcc)
|
||||
localFlowBigStep(node1, state1, node2, state2, preservesValue, ap, config, _) and exists(lcc)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
predicate flowOutOfCall(
|
||||
DataFlowCall call, RetNodeEx node1, ReturnKindExt kind, NodeEx node2, boolean allowsFieldFlow,
|
||||
Configuration config
|
||||
) {
|
||||
exists(FlowState state |
|
||||
flowOutOfCallNodeCand2(call, node1, kind, node2, allowsFieldFlow, config) and
|
||||
PrevStage::revFlow(node2, pragma[only_bind_into](state), _, pragma[only_bind_into](config)) and
|
||||
PrevStage::revFlowAlias(node1, pragma[only_bind_into](state), _,
|
||||
pragma[only_bind_into](config))
|
||||
)
|
||||
}
|
||||
predicate flowOutOfCall = flowOutOfCallNodeCand2/6;
|
||||
|
||||
pragma[nomagic]
|
||||
predicate flowIntoCall(
|
||||
DataFlowCall call, ArgNodeEx node1, ParamNodeEx node2, boolean allowsFieldFlow,
|
||||
Configuration config
|
||||
) {
|
||||
exists(FlowState state |
|
||||
flowIntoCallNodeCand2(call, node1, node2, allowsFieldFlow, config) and
|
||||
PrevStage::revFlow(node2, pragma[only_bind_into](state), _, pragma[only_bind_into](config)) and
|
||||
PrevStage::revFlowAlias(node1, pragma[only_bind_into](state), _,
|
||||
pragma[only_bind_into](config))
|
||||
)
|
||||
}
|
||||
predicate flowIntoCall = flowIntoCallNodeCand2/5;
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate clearSet(NodeEx node, ContentSet c, Configuration config) {
|
||||
@@ -2531,8 +2291,8 @@ private module Stage4Param implements MkStage<Stage3>::StageParam {
|
||||
}
|
||||
}
|
||||
|
||||
private module Stage4 implements StageSig {
|
||||
import MkStage<Stage3>::Stage<Stage4Param>
|
||||
private module Stage3 implements StageSig {
|
||||
import MkStage<Stage2>::Stage<Stage3Param>
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -2543,8 +2303,8 @@ private predicate flowCandSummaryCtx(
|
||||
NodeEx node, FlowState state, AccessPathFront argApf, Configuration config
|
||||
) {
|
||||
exists(AccessPathFront apf |
|
||||
Stage4::revFlow(node, state, TReturnCtxMaybeFlowThrough(_), _, apf, config) and
|
||||
Stage4::fwdFlow(node, state, any(Stage4::CcCall ccc), _, TAccessPathFrontSome(argApf), apf,
|
||||
Stage3::revFlow(node, state, TReturnCtxMaybeFlowThrough(_), _, apf, config) and
|
||||
Stage3::fwdFlow(node, state, any(Stage3::CcCall ccc), _, TAccessPathFrontSome(argApf), apf,
|
||||
config)
|
||||
)
|
||||
}
|
||||
@@ -2555,10 +2315,10 @@ private predicate flowCandSummaryCtx(
|
||||
*/
|
||||
private predicate expensiveLen2unfolding(TypedContent tc, Configuration config) {
|
||||
exists(int tails, int nodes, int apLimit, int tupleLimit |
|
||||
tails = strictcount(AccessPathFront apf | Stage4::consCand(tc, apf, config)) and
|
||||
tails = strictcount(AccessPathFront apf | Stage3::consCand(tc, apf, config)) and
|
||||
nodes =
|
||||
strictcount(NodeEx n, FlowState state |
|
||||
Stage4::revFlow(n, state, any(AccessPathFrontHead apf | apf.getHead() = tc), config)
|
||||
Stage3::revFlow(n, state, any(AccessPathFrontHead apf | apf.getHead() = tc), config)
|
||||
or
|
||||
flowCandSummaryCtx(n, state, any(AccessPathFrontHead apf | apf.getHead() = tc), config)
|
||||
) and
|
||||
@@ -2572,11 +2332,11 @@ private predicate expensiveLen2unfolding(TypedContent tc, Configuration config)
|
||||
private newtype TAccessPathApprox =
|
||||
TNil(DataFlowType t) or
|
||||
TConsNil(TypedContent tc, DataFlowType t) {
|
||||
Stage4::consCand(tc, TFrontNil(t), _) and
|
||||
Stage3::consCand(tc, TFrontNil(t), _) and
|
||||
not expensiveLen2unfolding(tc, _)
|
||||
} or
|
||||
TConsCons(TypedContent tc1, TypedContent tc2, int len) {
|
||||
Stage4::consCand(tc1, TFrontHead(tc2), _) and
|
||||
Stage3::consCand(tc1, TFrontHead(tc2), _) and
|
||||
len in [2 .. accessPathLimit()] and
|
||||
not expensiveLen2unfolding(tc1, _)
|
||||
} or
|
||||
@@ -2706,7 +2466,7 @@ private class AccessPathApproxCons1 extends AccessPathApproxCons, TCons1 {
|
||||
override AccessPathApprox pop(TypedContent head) {
|
||||
head = tc and
|
||||
(
|
||||
exists(TypedContent tc2 | Stage4::consCand(tc, TFrontHead(tc2), _) |
|
||||
exists(TypedContent tc2 | Stage3::consCand(tc, TFrontHead(tc2), _) |
|
||||
result = TConsCons(tc2, _, len - 1)
|
||||
or
|
||||
len = 2 and
|
||||
@@ -2717,7 +2477,7 @@ private class AccessPathApproxCons1 extends AccessPathApproxCons, TCons1 {
|
||||
or
|
||||
exists(DataFlowType t |
|
||||
len = 1 and
|
||||
Stage4::consCand(tc, TFrontNil(t), _) and
|
||||
Stage3::consCand(tc, TFrontNil(t), _) and
|
||||
result = TNil(t)
|
||||
)
|
||||
)
|
||||
@@ -2742,14 +2502,13 @@ private class AccessPathApproxOption extends TAccessPathApproxOption {
|
||||
}
|
||||
}
|
||||
|
||||
private module Stage5Param implements MkStage<Stage4>::StageParam {
|
||||
private module PrevStage = Stage4;
|
||||
private module Stage4Param implements MkStage<Stage3>::StageParam {
|
||||
private module PrevStage = Stage3;
|
||||
|
||||
class Ap = AccessPathApprox;
|
||||
|
||||
class ApNil = AccessPathApproxNil;
|
||||
|
||||
pragma[nomagic]
|
||||
PrevStage::Ap getApprox(Ap ap) { result = ap.getFront() }
|
||||
|
||||
ApNil getApNil(NodeEx node) {
|
||||
@@ -2775,9 +2534,7 @@ private module Stage5Param implements MkStage<Stage4>::StageParam {
|
||||
NodeEx node1, FlowState state1, NodeEx node2, FlowState state2, boolean preservesValue,
|
||||
ApNil ap, Configuration config, LocalCc lcc
|
||||
) {
|
||||
localFlowBigStep(node1, state1, node2, state2, preservesValue, ap.getType(), config, lcc) and
|
||||
PrevStage::revFlow(node1, pragma[only_bind_into](state1), _, pragma[only_bind_into](config)) and
|
||||
PrevStage::revFlowAlias(node2, pragma[only_bind_into](state2), _, pragma[only_bind_into](config))
|
||||
localFlowBigStep(node1, state1, node2, state2, preservesValue, ap.getFront(), config, lcc)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
@@ -2814,7 +2571,7 @@ private module Stage5Param implements MkStage<Stage4>::StageParam {
|
||||
predicate typecheckStore(Ap ap, DataFlowType contentType) { any() }
|
||||
}
|
||||
|
||||
private module Stage5 = MkStage<Stage4>::Stage<Stage5Param>;
|
||||
private module Stage4 = MkStage<Stage3>::Stage<Stage4Param>;
|
||||
|
||||
bindingset[conf, result]
|
||||
private Configuration unbindConf(Configuration conf) {
|
||||
@@ -2828,8 +2585,8 @@ private predicate nodeMayUseSummary0(
|
||||
) {
|
||||
exists(AccessPathApprox apa0 |
|
||||
c = n.getEnclosingCallable() and
|
||||
Stage5::revFlow(n, state, TReturnCtxMaybeFlowThrough(_), _, apa0, config) and
|
||||
Stage5::fwdFlow(n, state, any(CallContextCall ccc), TParameterPositionSome(pos),
|
||||
Stage4::revFlow(n, state, TReturnCtxMaybeFlowThrough(_), _, apa0, config) and
|
||||
Stage4::fwdFlow(n, state, any(CallContextCall ccc), TParameterPositionSome(pos),
|
||||
TAccessPathApproxSome(apa), apa0, config)
|
||||
)
|
||||
}
|
||||
@@ -2839,7 +2596,7 @@ private predicate nodeMayUseSummary(
|
||||
NodeEx n, FlowState state, AccessPathApprox apa, Configuration config
|
||||
) {
|
||||
exists(DataFlowCallable c, ParameterPosition pos, ParamNodeEx p |
|
||||
Stage5::parameterMayFlowThrough(p, apa, config) and
|
||||
Stage4::parameterMayFlowThrough(p, apa, config) and
|
||||
nodeMayUseSummary0(n, c, pos, state, apa, config) and
|
||||
p.isParameterOf(c, pos)
|
||||
)
|
||||
@@ -2849,8 +2606,8 @@ private newtype TSummaryCtx =
|
||||
TSummaryCtxNone() or
|
||||
TSummaryCtxSome(ParamNodeEx p, FlowState state, AccessPath ap) {
|
||||
exists(Configuration config |
|
||||
Stage5::parameterMayFlowThrough(p, ap.getApprox(), config) and
|
||||
Stage5::revFlow(p, state, _, config)
|
||||
Stage4::parameterMayFlowThrough(p, ap.getApprox(), config) and
|
||||
Stage4::revFlow(p, state, _, config)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -2899,7 +2656,7 @@ private int count1to2unfold(AccessPathApproxCons1 apa, Configuration config) {
|
||||
len = apa.len() and
|
||||
result =
|
||||
strictcount(AccessPathFront apf |
|
||||
Stage5::consCand(tc, any(AccessPathApprox ap | ap.getFront() = apf and ap.len() = len - 1),
|
||||
Stage4::consCand(tc, any(AccessPathApprox ap | ap.getFront() = apf and ap.len() = len - 1),
|
||||
config)
|
||||
)
|
||||
)
|
||||
@@ -2908,7 +2665,7 @@ private int count1to2unfold(AccessPathApproxCons1 apa, Configuration config) {
|
||||
private int countNodesUsingAccessPath(AccessPathApprox apa, Configuration config) {
|
||||
result =
|
||||
strictcount(NodeEx n, FlowState state |
|
||||
Stage5::revFlow(n, state, apa, config) or nodeMayUseSummary(n, state, apa, config)
|
||||
Stage4::revFlow(n, state, apa, config) or nodeMayUseSummary(n, state, apa, config)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -2929,7 +2686,7 @@ private predicate expensiveLen1to2unfolding(AccessPathApproxCons1 apa, Configura
|
||||
private AccessPathApprox getATail(AccessPathApprox apa, Configuration config) {
|
||||
exists(TypedContent head |
|
||||
apa.pop(head) = result and
|
||||
Stage5::consCand(head, result, config)
|
||||
Stage4::consCand(head, result, config)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -3011,7 +2768,7 @@ private newtype TPathNode =
|
||||
NodeEx node, FlowState state, CallContext cc, SummaryCtx sc, AccessPath ap, Configuration config
|
||||
) {
|
||||
// A PathNode is introduced by a source ...
|
||||
Stage5::revFlow(node, state, config) and
|
||||
Stage4::revFlow(node, state, config) and
|
||||
sourceNode(node, state, config) and
|
||||
(
|
||||
if hasSourceCallCtx(config)
|
||||
@@ -3025,7 +2782,7 @@ private newtype TPathNode =
|
||||
exists(PathNodeMid mid |
|
||||
pathStep(mid, node, state, cc, sc, ap) and
|
||||
pragma[only_bind_into](config) = mid.getConfiguration() and
|
||||
Stage5::revFlow(node, state, ap.getApprox(), pragma[only_bind_into](config))
|
||||
Stage4::revFlow(node, state, ap.getApprox(), pragma[only_bind_into](config))
|
||||
)
|
||||
} or
|
||||
TPathNodeSink(NodeEx node, FlowState state, Configuration config) {
|
||||
@@ -3167,7 +2924,7 @@ private class AccessPathCons2 extends AccessPath, TAccessPathCons2 {
|
||||
override TypedContent getHead() { result = head1 }
|
||||
|
||||
override AccessPath getTail() {
|
||||
Stage5::consCand(head1, result.getApprox(), _) and
|
||||
Stage4::consCand(head1, result.getApprox(), _) and
|
||||
result.getHead() = head2 and
|
||||
result.length() = len - 1
|
||||
}
|
||||
@@ -3198,7 +2955,7 @@ private class AccessPathCons1 extends AccessPath, TAccessPathCons1 {
|
||||
override TypedContent getHead() { result = head }
|
||||
|
||||
override AccessPath getTail() {
|
||||
Stage5::consCand(head, result.getApprox(), _) and result.length() = len - 1
|
||||
Stage4::consCand(head, result.getApprox(), _) and result.length() = len - 1
|
||||
}
|
||||
|
||||
override AccessPathFrontHead getFront() { result = TFrontHead(head) }
|
||||
@@ -3590,8 +3347,7 @@ private predicate pathStep(
|
||||
AccessPath ap0, NodeEx midnode, FlowState state0, Configuration conf, LocalCallContext localCC
|
||||
|
|
||||
pathNode(mid, midnode, state0, cc, sc, ap0, conf, localCC) and
|
||||
localFlowBigStep(midnode, state0, node, state, false, ap.(AccessPathNil).getType(), conf,
|
||||
localCC) and
|
||||
localFlowBigStep(midnode, state0, node, state, false, ap.getFront(), conf, localCC) and
|
||||
ap0 instanceof AccessPathNil
|
||||
)
|
||||
or
|
||||
@@ -3633,7 +3389,7 @@ private predicate pathReadStep(
|
||||
) {
|
||||
ap0 = mid.getAp() and
|
||||
tc = ap0.getHead() and
|
||||
Stage5::readStepCand(mid.getNodeEx(), tc.getContent(), node, mid.getConfiguration()) and
|
||||
Stage4::readStepCand(mid.getNodeEx(), tc.getContent(), node, mid.getConfiguration()) and
|
||||
state = mid.getState() and
|
||||
cc = mid.getCallContext()
|
||||
}
|
||||
@@ -3643,7 +3399,7 @@ private predicate pathStoreStep(
|
||||
PathNodeMid mid, NodeEx node, FlowState state, AccessPath ap0, TypedContent tc, CallContext cc
|
||||
) {
|
||||
ap0 = mid.getAp() and
|
||||
Stage5::storeStepCand(mid.getNodeEx(), _, tc, node, _, mid.getConfiguration()) and
|
||||
Stage4::storeStepCand(mid.getNodeEx(), _, tc, node, _, mid.getConfiguration()) and
|
||||
state = mid.getState() and
|
||||
cc = mid.getCallContext()
|
||||
}
|
||||
@@ -3680,7 +3436,7 @@ private NodeEx getAnOutNodeFlow(
|
||||
ReturnKindExt kind, DataFlowCall call, AccessPathApprox apa, Configuration config
|
||||
) {
|
||||
result.asNode() = kind.getAnOutNode(call) and
|
||||
Stage5::revFlow(result, _, apa, config)
|
||||
Stage4::revFlow(result, _, apa, config)
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -3716,7 +3472,7 @@ private predicate parameterCand(
|
||||
DataFlowCallable callable, ParameterPosition pos, AccessPathApprox apa, Configuration config
|
||||
) {
|
||||
exists(ParamNodeEx p |
|
||||
Stage5::revFlow(p, _, apa, config) and
|
||||
Stage4::revFlow(p, _, apa, config) and
|
||||
p.isParameterOf(callable, pos)
|
||||
)
|
||||
}
|
||||
@@ -3995,17 +3751,9 @@ predicate stageStats(
|
||||
n = 45 and
|
||||
Stage4::stats(false, nodes, fields, conscand, states, tuples, config)
|
||||
or
|
||||
stage = "5 Fwd" and
|
||||
n = 50 and
|
||||
Stage5::stats(true, nodes, fields, conscand, states, tuples, config)
|
||||
stage = "5 Fwd" and n = 50 and finalStats(true, nodes, fields, conscand, states, tuples)
|
||||
or
|
||||
stage = "5 Rev" and
|
||||
n = 55 and
|
||||
Stage5::stats(false, nodes, fields, conscand, states, tuples, config)
|
||||
or
|
||||
stage = "6 Fwd" and n = 60 and finalStats(true, nodes, fields, conscand, states, tuples)
|
||||
or
|
||||
stage = "6 Rev" and n = 65 and finalStats(false, nodes, fields, conscand, states, tuples)
|
||||
stage = "5 Rev" and n = 55 and finalStats(false, nodes, fields, conscand, states, tuples)
|
||||
}
|
||||
|
||||
private module FlowExploration {
|
||||
|
||||
@@ -621,6 +621,23 @@ private predicate parameterFlowThroughAllowed(ParamNodeEx p, ReturnKindExt kind)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if flow from a parameter at position `pos` inside `c` to a return node of
|
||||
* kind `kind` is allowed.
|
||||
*
|
||||
* We don't expect a parameter to return stored in itself, unless
|
||||
* explicitly allowed
|
||||
*/
|
||||
bindingset[c, pos, kind]
|
||||
private predicate parameterFlowThroughAllowed(
|
||||
DataFlowCallable c, ParameterPosition pos, ReturnKindExt kind
|
||||
) {
|
||||
exists(ParamNodeEx p |
|
||||
p.isParameterOf(c, pos) and
|
||||
parameterFlowThroughAllowed(p, kind)
|
||||
)
|
||||
}
|
||||
|
||||
private module Stage1 implements StageSig {
|
||||
class Ap = Unit;
|
||||
|
||||
@@ -963,12 +980,6 @@ private module Stage1 implements StageSig {
|
||||
pragma[nomagic]
|
||||
predicate revFlow(NodeEx node, Configuration config) { revFlow(node, _, config) }
|
||||
|
||||
pragma[nomagic]
|
||||
predicate revFlowAp(NodeEx node, Ap ap, Configuration config) {
|
||||
revFlow(node, config) and
|
||||
exists(ap)
|
||||
}
|
||||
|
||||
bindingset[node, state, config]
|
||||
predicate revFlow(NodeEx node, FlowState state, Ap ap, Configuration config) {
|
||||
revFlow(node, _, pragma[only_bind_into](config)) and
|
||||
@@ -1011,13 +1022,9 @@ private module Stage1 implements StageSig {
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
predicate returnMayFlowThrough(
|
||||
RetNodeEx ret, Ap argAp, Ap ap, ReturnKindExt kind, Configuration config
|
||||
) {
|
||||
predicate returnMayFlowThrough(RetNodeEx ret, ReturnKindExt kind, Configuration config) {
|
||||
throughFlowNodeCand(ret, config) and
|
||||
kind = ret.getKind() and
|
||||
exists(argAp) and
|
||||
exists(ap)
|
||||
kind = ret.getKind()
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
@@ -1182,8 +1189,6 @@ private signature module StageSig {
|
||||
|
||||
predicate revFlow(NodeEx node, Configuration config);
|
||||
|
||||
predicate revFlowAp(NodeEx node, Ap ap, Configuration config);
|
||||
|
||||
bindingset[node, state, config]
|
||||
predicate revFlow(NodeEx node, FlowState state, Ap ap, Configuration config);
|
||||
|
||||
@@ -1191,9 +1196,7 @@ private signature module StageSig {
|
||||
|
||||
predicate parameterMayFlowThrough(ParamNodeEx p, Ap ap, Configuration config);
|
||||
|
||||
predicate returnMayFlowThrough(
|
||||
RetNodeEx ret, Ap argAp, Ap ap, ReturnKindExt kind, Configuration config
|
||||
);
|
||||
predicate returnMayFlowThrough(RetNodeEx ret, ReturnKindExt kind, Configuration config);
|
||||
|
||||
predicate storeStepCand(
|
||||
NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, DataFlowType contentType,
|
||||
@@ -1278,43 +1281,21 @@ private module MkStage<StageSig PrevStage> {
|
||||
import Param
|
||||
|
||||
/* Begin: Stage logic. */
|
||||
// use an alias as a workaround for bad functionality-induced joins
|
||||
pragma[nomagic]
|
||||
private predicate revFlowApAlias(NodeEx node, ApApprox apa, Configuration config) {
|
||||
PrevStage::revFlowAp(node, apa, config)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate flowIntoCallApa(
|
||||
DataFlowCall call, ArgNodeEx arg, ParamNodeEx p, boolean allowsFieldFlow, ApApprox apa,
|
||||
Configuration config
|
||||
) {
|
||||
flowIntoCall(call, arg, p, allowsFieldFlow, config) and
|
||||
PrevStage::revFlowAp(p, pragma[only_bind_into](apa), pragma[only_bind_into](config)) and
|
||||
revFlowApAlias(arg, pragma[only_bind_into](apa), pragma[only_bind_into](config))
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate flowOutOfCallApa(
|
||||
DataFlowCall call, RetNodeEx ret, ReturnKindExt kind, NodeEx out, boolean allowsFieldFlow,
|
||||
ApApprox apa, Configuration config
|
||||
) {
|
||||
flowOutOfCall(call, ret, kind, out, allowsFieldFlow, config) and
|
||||
PrevStage::revFlowAp(out, pragma[only_bind_into](apa), pragma[only_bind_into](config)) and
|
||||
revFlowApAlias(ret, pragma[only_bind_into](apa), pragma[only_bind_into](config))
|
||||
bindingset[result, apa]
|
||||
private ApApprox unbindApa(ApApprox apa) {
|
||||
pragma[only_bind_out](apa) = pragma[only_bind_out](result)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate flowThroughOutOfCall(
|
||||
DataFlowCall call, CcCall ccc, RetNodeEx ret, NodeEx out, boolean allowsFieldFlow,
|
||||
ApApprox argApa, ApApprox apa, Configuration config
|
||||
DataFlowCall call, DataFlowCallable c, CcCall ccc, RetNodeEx ret, ReturnKindExt kind,
|
||||
NodeEx out, boolean allowsFieldFlow, Configuration config
|
||||
) {
|
||||
exists(ReturnKindExt kind |
|
||||
flowOutOfCallApa(call, ret, kind, out, allowsFieldFlow, apa, pragma[only_bind_into](config)) and
|
||||
PrevStage::callMayFlowThroughRev(call, pragma[only_bind_into](config)) and
|
||||
PrevStage::returnMayFlowThrough(ret, argApa, apa, kind, pragma[only_bind_into](config)) and
|
||||
matchesCall(ccc, call)
|
||||
)
|
||||
flowOutOfCall(call, ret, kind, out, allowsFieldFlow, pragma[only_bind_into](config)) and
|
||||
PrevStage::callMayFlowThroughRev(call, pragma[only_bind_into](config)) and
|
||||
PrevStage::returnMayFlowThrough(ret, kind, pragma[only_bind_into](config)) and
|
||||
matchesCall(ccc, call) and
|
||||
c = ret.getEnclosingCallable()
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1326,50 +1307,39 @@ private module MkStage<StageSig PrevStage> {
|
||||
* corresponding parameter position and access path of that argument, respectively.
|
||||
*/
|
||||
pragma[nomagic]
|
||||
additional predicate fwdFlow(
|
||||
NodeEx node, FlowState state, Cc cc, ParameterPositionOption summaryCtx, ApOption argAp,
|
||||
Ap ap, ApApprox apa, Configuration config
|
||||
) {
|
||||
fwdFlow0(node, state, cc, summaryCtx, argAp, ap, apa, config) and
|
||||
PrevStage::revFlow(node, state, apa, config) and
|
||||
filter(node, state, ap, config)
|
||||
}
|
||||
|
||||
pragma[inline]
|
||||
additional predicate fwdFlow(
|
||||
NodeEx node, FlowState state, Cc cc, ParameterPositionOption summaryCtx, ApOption argAp,
|
||||
Ap ap, Configuration config
|
||||
) {
|
||||
fwdFlow(node, state, cc, summaryCtx, argAp, ap, _, config)
|
||||
fwdFlow0(node, state, cc, summaryCtx, argAp, ap, config) and
|
||||
PrevStage::revFlow(node, state, unbindApa(getApprox(ap)), config) and
|
||||
filter(node, state, ap, config)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate fwdFlow0(
|
||||
NodeEx node, FlowState state, Cc cc, ParameterPositionOption summaryCtx, ApOption argAp,
|
||||
Ap ap, ApApprox apa, Configuration config
|
||||
Ap ap, Configuration config
|
||||
) {
|
||||
sourceNode(node, state, config) and
|
||||
(if hasSourceCallCtx(config) then cc = ccSomeCall() else cc = ccNone()) and
|
||||
argAp = apNone() and
|
||||
summaryCtx = TParameterPositionNone() and
|
||||
ap = getApNil(node) and
|
||||
apa = getApprox(ap)
|
||||
ap = getApNil(node)
|
||||
or
|
||||
exists(NodeEx mid, FlowState state0, Ap ap0, ApApprox apa0, LocalCc localCc |
|
||||
fwdFlow(mid, state0, cc, summaryCtx, argAp, ap0, apa0, config) and
|
||||
exists(NodeEx mid, FlowState state0, Ap ap0, LocalCc localCc |
|
||||
fwdFlow(mid, state0, cc, summaryCtx, argAp, ap0, config) and
|
||||
localCc = getLocalCc(mid, cc)
|
||||
|
|
||||
localStep(mid, state0, node, state, true, _, config, localCc) and
|
||||
ap = ap0 and
|
||||
apa = apa0
|
||||
ap = ap0
|
||||
or
|
||||
localStep(mid, state0, node, state, false, ap, config, localCc) and
|
||||
ap0 instanceof ApNil and
|
||||
apa = getApprox(ap)
|
||||
ap0 instanceof ApNil
|
||||
)
|
||||
or
|
||||
exists(NodeEx mid |
|
||||
fwdFlow(mid, pragma[only_bind_into](state), _, _, _, ap, apa, pragma[only_bind_into](config)) and
|
||||
fwdFlow(mid, pragma[only_bind_into](state), _, _, _, ap, pragma[only_bind_into](config)) and
|
||||
jumpStep(mid, node, config) and
|
||||
cc = ccNone() and
|
||||
summaryCtx = TParameterPositionNone() and
|
||||
@@ -1382,8 +1352,7 @@ private module MkStage<StageSig PrevStage> {
|
||||
cc = ccNone() and
|
||||
summaryCtx = TParameterPositionNone() and
|
||||
argAp = apNone() and
|
||||
ap = getApNil(node) and
|
||||
apa = getApprox(ap)
|
||||
ap = getApNil(node)
|
||||
)
|
||||
or
|
||||
exists(NodeEx mid, FlowState state0, ApNil nil |
|
||||
@@ -1392,32 +1361,32 @@ private module MkStage<StageSig PrevStage> {
|
||||
cc = ccNone() and
|
||||
summaryCtx = TParameterPositionNone() and
|
||||
argAp = apNone() and
|
||||
ap = getApNil(node) and
|
||||
apa = getApprox(ap)
|
||||
ap = getApNil(node)
|
||||
)
|
||||
or
|
||||
// store
|
||||
exists(TypedContent tc, Ap ap0 |
|
||||
fwdFlowStore(_, ap0, tc, node, state, cc, summaryCtx, argAp, config) and
|
||||
ap = apCons(tc, ap0) and
|
||||
apa = getApprox(ap)
|
||||
ap = apCons(tc, ap0)
|
||||
)
|
||||
or
|
||||
// read
|
||||
exists(Ap ap0, Content c |
|
||||
fwdFlowRead(ap0, c, _, node, state, cc, summaryCtx, argAp, config) and
|
||||
fwdFlowConsCand(ap0, c, ap, config) and
|
||||
apa = getApprox(ap)
|
||||
fwdFlowConsCand(ap0, c, ap, config)
|
||||
)
|
||||
or
|
||||
// flow into a callable
|
||||
fwdFlowIn(_, node, state, _, cc, _, _, ap, apa, config) and
|
||||
if PrevStage::parameterMayFlowThrough(node, apa, config)
|
||||
then (
|
||||
summaryCtx = TParameterPositionSome(node.(ParamNodeEx).getPosition()) and
|
||||
argAp = apSome(ap)
|
||||
) else (
|
||||
summaryCtx = TParameterPositionNone() and argAp = apNone()
|
||||
exists(ApApprox apa |
|
||||
fwdFlowIn(_, node, state, _, cc, _, _, ap, config) and
|
||||
apa = getApprox(ap) and
|
||||
if PrevStage::parameterMayFlowThrough(node, apa, config)
|
||||
then (
|
||||
summaryCtx = TParameterPositionSome(node.(ParamNodeEx).getPosition()) and
|
||||
argAp = apSome(ap)
|
||||
) else (
|
||||
summaryCtx = TParameterPositionNone() and argAp = apNone()
|
||||
)
|
||||
)
|
||||
or
|
||||
// flow out of a callable
|
||||
@@ -1425,8 +1394,8 @@ private module MkStage<StageSig PrevStage> {
|
||||
DataFlowCall call, RetNodeEx ret, boolean allowsFieldFlow, CcNoCall innercc,
|
||||
DataFlowCallable inner
|
||||
|
|
||||
fwdFlow(ret, state, innercc, summaryCtx, argAp, ap, apa, config) and
|
||||
flowOutOfCallApa(call, ret, _, node, allowsFieldFlow, apa, config) and
|
||||
fwdFlow(ret, state, innercc, summaryCtx, argAp, ap, config) and
|
||||
flowOutOfCall(call, ret, _, node, allowsFieldFlow, config) and
|
||||
inner = ret.getEnclosingCallable() and
|
||||
cc = getCallContextReturn(inner, call, innercc) and
|
||||
if allowsFieldFlow = false then ap instanceof ApNil else any()
|
||||
@@ -1434,7 +1403,7 @@ private module MkStage<StageSig PrevStage> {
|
||||
or
|
||||
// flow through a callable
|
||||
exists(DataFlowCall call, ParameterPosition summaryCtx0, Ap argAp0 |
|
||||
fwdFlowOutFromArg(call, node, state, summaryCtx0, argAp0, ap, apa, config) and
|
||||
fwdFlowOutFromArg(call, node, state, summaryCtx0, argAp0, ap, config) and
|
||||
fwdFlowIsEntered(call, cc, summaryCtx, argAp, summaryCtx0, argAp0, config)
|
||||
)
|
||||
}
|
||||
@@ -1444,9 +1413,9 @@ private module MkStage<StageSig PrevStage> {
|
||||
NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, FlowState state, Cc cc,
|
||||
ParameterPositionOption summaryCtx, ApOption argAp, Configuration config
|
||||
) {
|
||||
exists(DataFlowType contentType, ApApprox apa1 |
|
||||
fwdFlow(node1, state, cc, summaryCtx, argAp, ap1, apa1, config) and
|
||||
PrevStage::storeStepCand(node1, apa1, tc, node2, contentType, config) and
|
||||
exists(DataFlowType contentType |
|
||||
fwdFlow(node1, state, cc, summaryCtx, argAp, ap1, config) and
|
||||
PrevStage::storeStepCand(node1, unbindApa(getApprox(ap1)), tc, node2, contentType, config) and
|
||||
typecheckStore(ap1, contentType)
|
||||
)
|
||||
}
|
||||
@@ -1464,104 +1433,43 @@ private module MkStage<StageSig PrevStage> {
|
||||
)
|
||||
}
|
||||
|
||||
private class ApNonNil instanceof Ap {
|
||||
pragma[nomagic]
|
||||
ApNonNil() { not this instanceof ApNil }
|
||||
|
||||
string toString() { result = "" }
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate fwdFlowRead0(
|
||||
NodeEx node1, FlowState state, Cc cc, ParameterPositionOption summaryCtx, ApOption argAp,
|
||||
ApNonNil ap, Configuration config
|
||||
) {
|
||||
fwdFlow(node1, state, cc, summaryCtx, argAp, ap, config) and
|
||||
PrevStage::readStepCand(node1, _, _, config)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate fwdFlowRead(
|
||||
Ap ap, Content c, NodeEx node1, NodeEx node2, FlowState state, Cc cc,
|
||||
ParameterPositionOption summaryCtx, ApOption argAp, Configuration config
|
||||
) {
|
||||
fwdFlowRead0(node1, state, cc, summaryCtx, argAp, ap, config) and
|
||||
fwdFlow(node1, state, cc, summaryCtx, argAp, ap, config) and
|
||||
PrevStage::readStepCand(node1, c, node2, config) and
|
||||
getHeadContent(ap) = c
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate fwdFlowIn(
|
||||
DataFlowCall call, ParamNodeEx p, FlowState state, Cc outercc, CcCall innercc,
|
||||
ParameterPositionOption summaryCtx, ApOption argAp, Ap ap, ApApprox apa, Configuration config
|
||||
DataFlowCall call, ParamNodeEx p, FlowState state, Cc outercc, Cc innercc,
|
||||
ParameterPositionOption summaryCtx, ApOption argAp, Ap ap, Configuration config
|
||||
) {
|
||||
exists(ArgNodeEx arg, boolean allowsFieldFlow |
|
||||
fwdFlow(arg, state, outercc, summaryCtx, argAp, ap, apa, config) and
|
||||
flowIntoCallApa(call, arg, p, allowsFieldFlow, apa, config) and
|
||||
fwdFlow(arg, state, outercc, summaryCtx, argAp, ap, config) and
|
||||
flowIntoCall(call, arg, p, allowsFieldFlow, config) and
|
||||
innercc = getCallContextCall(call, p.getEnclosingCallable(), outercc) and
|
||||
if allowsFieldFlow = false then ap instanceof ApNil else any()
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate fwdFlowRetFromArg(
|
||||
RetNodeEx ret, FlowState state, CcCall ccc, ParameterPosition summaryCtx, ParamNodeEx p,
|
||||
Ap argAp, ApApprox argApa, Ap ap, ApApprox apa, Configuration config
|
||||
) {
|
||||
exists(DataFlowCallable c, ReturnKindExt kind |
|
||||
fwdFlow(pragma[only_bind_into](ret), state, ccc,
|
||||
TParameterPositionSome(pragma[only_bind_into](summaryCtx)), apSome(argAp), ap, apa, config) and
|
||||
getApprox(argAp) = argApa and
|
||||
c = ret.getEnclosingCallable() and
|
||||
kind = ret.getKind() and
|
||||
p.isParameterOf(c, pragma[only_bind_into](summaryCtx)) and
|
||||
parameterFlowThroughAllowed(p, kind)
|
||||
)
|
||||
}
|
||||
|
||||
pragma[inline]
|
||||
private predicate fwdFlowInMayFlowThrough(
|
||||
DataFlowCall call, Cc cc, CcCall innerCc, ParameterPositionOption summaryCtx, ApOption argAp,
|
||||
ParamNodeEx param, Ap ap, ApApprox apa, Configuration config
|
||||
) {
|
||||
fwdFlowIn(call, pragma[only_bind_into](param), _, cc, innerCc, summaryCtx, argAp, ap,
|
||||
pragma[only_bind_into](apa), pragma[only_bind_into](config)) and
|
||||
PrevStage::parameterMayFlowThrough(param, apa, config)
|
||||
}
|
||||
|
||||
// dedup before joining with `flowThroughOutOfCall`
|
||||
pragma[nomagic]
|
||||
private predicate fwdFlowInMayFlowThroughProj(
|
||||
DataFlowCall call, CcCall innerCc, ApApprox apa, Configuration config
|
||||
) {
|
||||
fwdFlowInMayFlowThrough(call, _, innerCc, _, _, _, _, apa, config)
|
||||
}
|
||||
|
||||
/**
|
||||
* Same as `flowThroughOutOfCall`, but restricted to calls that are reached
|
||||
* in the flow covered by `fwdFlow`, where data might flow through the target
|
||||
* callable and back out at `call`.
|
||||
*/
|
||||
pragma[nomagic]
|
||||
private predicate fwdFlowThroughOutOfCall(
|
||||
DataFlowCall call, CcCall ccc, RetNodeEx ret, NodeEx out, boolean allowsFieldFlow,
|
||||
ApApprox argApa, ApApprox apa, Configuration config
|
||||
) {
|
||||
fwdFlowInMayFlowThroughProj(call, ccc, argApa, config) and
|
||||
flowThroughOutOfCall(call, ccc, ret, out, allowsFieldFlow, argApa, apa, config)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate fwdFlowOutFromArg(
|
||||
DataFlowCall call, NodeEx out, FlowState state, ParameterPosition summaryCtx, Ap argAp, Ap ap,
|
||||
ApApprox apa, Configuration config
|
||||
Configuration config
|
||||
) {
|
||||
exists(RetNodeEx ret, boolean allowsFieldFlow, CcCall ccc, ApApprox argApa |
|
||||
fwdFlowRetFromArg(pragma[only_bind_into](ret), state, pragma[only_bind_into](ccc),
|
||||
summaryCtx, _, argAp, pragma[only_bind_into](argApa), ap, pragma[only_bind_into](apa),
|
||||
exists(
|
||||
DataFlowCallable c, RetNodeEx ret, ReturnKindExt kind, boolean allowsFieldFlow, CcCall ccc
|
||||
|
|
||||
fwdFlow(pragma[only_bind_into](ret), state, pragma[only_bind_into](ccc),
|
||||
TParameterPositionSome(pragma[only_bind_into](summaryCtx)), apSome(argAp), ap, config) and
|
||||
flowThroughOutOfCall(call, pragma[only_bind_into](c), ccc, ret, kind, out, allowsFieldFlow,
|
||||
config) and
|
||||
fwdFlowThroughOutOfCall(call, ccc, ret, out, allowsFieldFlow, argApa, apa, config) and
|
||||
(if allowsFieldFlow = false then ap instanceof ApNil else any())
|
||||
(if allowsFieldFlow = false then ap instanceof ApNil else any()) and
|
||||
parameterFlowThroughAllowed(c, pragma[only_bind_into](summaryCtx), kind)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1575,7 +1483,8 @@ private module MkStage<StageSig PrevStage> {
|
||||
ParameterPosition pos, Ap ap, Configuration config
|
||||
) {
|
||||
exists(ParamNodeEx param |
|
||||
fwdFlowInMayFlowThrough(call, cc, _, summaryCtx, argAp, param, ap, _, config) and
|
||||
fwdFlowIn(call, param, _, cc, _, summaryCtx, argAp, ap, config) and
|
||||
PrevStage::parameterMayFlowThrough(param, unbindApa(getApprox(ap)), config) and
|
||||
pos = param.getPosition()
|
||||
)
|
||||
}
|
||||
@@ -1596,55 +1505,41 @@ private module MkStage<StageSig PrevStage> {
|
||||
fwdFlowConsCand(ap1, c, ap2, config)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate returnFlowsThrough0(
|
||||
RetNodeEx ret, ReturnKindExt kind, FlowState state, CcCall ccc, DataFlowCallable c,
|
||||
ParameterPosition ppos, Ap argAp, Ap ap, Configuration config
|
||||
) {
|
||||
exists(boolean allowsFieldFlow |
|
||||
fwdFlow(ret, state, ccc, TParameterPositionSome(ppos), apSome(argAp), ap, config) and
|
||||
flowThroughOutOfCall(_, c, _, pragma[only_bind_into](ret), kind, _, allowsFieldFlow,
|
||||
pragma[only_bind_into](config)) and
|
||||
if allowsFieldFlow = false then ap instanceof ApNil else any()
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate returnFlowsThrough(
|
||||
RetNodeEx ret, ReturnKindExt kind, FlowState state, CcCall ccc, ParamNodeEx p, Ap argAp,
|
||||
Ap ap, Configuration config
|
||||
) {
|
||||
exists(boolean allowsFieldFlow, ApApprox argApa, ApApprox apa |
|
||||
fwdFlowRetFromArg(pragma[only_bind_into](ret), state, pragma[only_bind_into](ccc), _, p,
|
||||
argAp, pragma[only_bind_into](argApa), ap, pragma[only_bind_into](apa), config) and
|
||||
kind = ret.getKind() and
|
||||
fwdFlowThroughOutOfCall(_, ccc, ret, _, allowsFieldFlow, argApa, apa, config) and
|
||||
(if allowsFieldFlow = false then ap instanceof ApNil else any())
|
||||
exists(DataFlowCallable c, ParameterPosition ppos |
|
||||
returnFlowsThrough0(ret, kind, state, ccc, c, ppos, argAp, ap, config) and
|
||||
p.isParameterOf(c, ppos) and
|
||||
parameterFlowThroughAllowed(p, kind)
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate flowThroughIntoCall(
|
||||
DataFlowCall call, ArgNodeEx arg, ParamNodeEx p, boolean allowsFieldFlow, Ap argAp,
|
||||
Configuration config
|
||||
DataFlowCall call, ArgNodeEx arg, ParamNodeEx p, boolean allowsFieldFlow, Configuration config
|
||||
) {
|
||||
exists(ApApprox argApa |
|
||||
flowIntoCallApa(call, pragma[only_bind_into](arg), pragma[only_bind_into](p),
|
||||
allowsFieldFlow, argApa, pragma[only_bind_into](config)) and
|
||||
fwdFlow(arg, _, _, _, _, pragma[only_bind_into](argAp), argApa,
|
||||
exists(Ap argAp |
|
||||
flowIntoCall(call, pragma[only_bind_into](arg), pragma[only_bind_into](p), allowsFieldFlow,
|
||||
pragma[only_bind_into](config)) and
|
||||
fwdFlow(arg, _, _, _, _, pragma[only_bind_into](argAp), pragma[only_bind_into](config)) and
|
||||
returnFlowsThrough(_, _, _, _, p, pragma[only_bind_into](argAp), _,
|
||||
pragma[only_bind_into](config)) and
|
||||
if allowsFieldFlow = false then argAp instanceof ApNil else any()
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate flowIntoCallAp(
|
||||
DataFlowCall call, ArgNodeEx arg, ParamNodeEx p, boolean allowsFieldFlow, Ap ap,
|
||||
Configuration config
|
||||
) {
|
||||
exists(ApApprox apa |
|
||||
flowIntoCallApa(call, arg, p, allowsFieldFlow, apa, config) and
|
||||
fwdFlow(arg, _, _, _, _, ap, apa, config)
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate flowOutOfCallAp(
|
||||
DataFlowCall call, RetNodeEx ret, ReturnKindExt kind, NodeEx out, boolean allowsFieldFlow,
|
||||
Ap ap, Configuration config
|
||||
) {
|
||||
exists(ApApprox apa |
|
||||
flowOutOfCallApa(call, ret, kind, out, allowsFieldFlow, apa, config) and
|
||||
fwdFlow(ret, _, _, _, _, ap, apa, config)
|
||||
pragma[only_bind_into](config))
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1733,7 +1628,7 @@ private module MkStage<StageSig PrevStage> {
|
||||
// flow into a callable
|
||||
exists(ParamNodeEx p, boolean allowsFieldFlow |
|
||||
revFlow(p, state, TReturnCtxNone(), returnAp, ap, config) and
|
||||
flowIntoCallAp(_, node, p, allowsFieldFlow, ap, config) and
|
||||
flowIntoCall(_, node, p, allowsFieldFlow, config) and
|
||||
(if allowsFieldFlow = false then ap instanceof ApNil else any()) and
|
||||
returnCtx = TReturnCtxNone()
|
||||
)
|
||||
@@ -1787,41 +1682,22 @@ private module MkStage<StageSig PrevStage> {
|
||||
) {
|
||||
exists(NodeEx out, boolean allowsFieldFlow |
|
||||
revFlow(out, state, returnCtx, returnAp, ap, config) and
|
||||
flowOutOfCallAp(call, ret, kind, out, allowsFieldFlow, ap, config) and
|
||||
flowOutOfCall(call, ret, kind, out, allowsFieldFlow, config) and
|
||||
if allowsFieldFlow = false then ap instanceof ApNil else any()
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Same as `flowThroughIntoCall`, but restricted to calls that are reached
|
||||
* in the flow covered by `revFlow`, where data might flow through the target
|
||||
* callable and back out at `call`.
|
||||
*/
|
||||
pragma[nomagic]
|
||||
private predicate revFlowThroughIntoCall(
|
||||
DataFlowCall call, ArgNodeEx arg, ParamNodeEx p, boolean allowsFieldFlow, Ap argAp,
|
||||
Configuration config
|
||||
) {
|
||||
flowThroughIntoCall(call, arg, p, allowsFieldFlow, argAp, config) and
|
||||
revFlowIsReturned(call, _, _, _, _, config)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate revFlowParamToReturn(
|
||||
ParamNodeEx p, FlowState state, ReturnKindExt kind, Ap returnAp, Ap ap, Configuration config
|
||||
) {
|
||||
revFlow(p, state, TReturnCtxMaybeFlowThrough(kind), apSome(returnAp), ap, config) and
|
||||
parameterFlowThroughAllowed(p, kind)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate revFlowInToReturn(
|
||||
DataFlowCall call, ArgNodeEx arg, FlowState state, ReturnKindExt kind, Ap returnAp, Ap ap,
|
||||
Configuration config
|
||||
) {
|
||||
exists(ParamNodeEx p, boolean allowsFieldFlow |
|
||||
revFlowParamToReturn(p, state, kind, returnAp, ap, config) and
|
||||
revFlowThroughIntoCall(call, arg, p, allowsFieldFlow, ap, config)
|
||||
revFlow(pragma[only_bind_into](p), state,
|
||||
TReturnCtxMaybeFlowThrough(pragma[only_bind_into](kind)), apSome(returnAp), ap, config) and
|
||||
flowThroughIntoCall(call, arg, p, allowsFieldFlow, config) and
|
||||
(if allowsFieldFlow = false then ap instanceof ApNil else any()) and
|
||||
parameterFlowThroughAllowed(p, kind)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1874,11 +1750,6 @@ private module MkStage<StageSig PrevStage> {
|
||||
pragma[nomagic]
|
||||
predicate revFlow(NodeEx node, Configuration config) { revFlow(node, _, _, _, _, config) }
|
||||
|
||||
pragma[nomagic]
|
||||
predicate revFlowAp(NodeEx node, Ap ap, Configuration config) {
|
||||
revFlow(node, _, _, _, ap, config)
|
||||
}
|
||||
|
||||
// use an alias as a workaround for bad functionality-induced joins
|
||||
pragma[nomagic]
|
||||
additional predicate revFlowAlias(NodeEx node, Configuration config) {
|
||||
@@ -1915,9 +1786,9 @@ private module MkStage<StageSig PrevStage> {
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate parameterFlowsThroughRev(
|
||||
ParamNodeEx p, Ap ap, ReturnKindExt kind, Ap returnAp, Configuration config
|
||||
ParamNodeEx p, Ap ap, ReturnKindExt kind, Configuration config
|
||||
) {
|
||||
revFlow(p, _, TReturnCtxMaybeFlowThrough(kind), apSome(returnAp), ap, config) and
|
||||
revFlow(p, _, TReturnCtxMaybeFlowThrough(kind), apSome(_), ap, config) and
|
||||
parameterFlowThroughAllowed(p, kind)
|
||||
}
|
||||
|
||||
@@ -1925,36 +1796,27 @@ private module MkStage<StageSig PrevStage> {
|
||||
predicate parameterMayFlowThrough(ParamNodeEx p, Ap ap, Configuration config) {
|
||||
exists(RetNodeEx ret, ReturnKindExt kind |
|
||||
returnFlowsThrough(ret, kind, _, _, p, ap, _, config) and
|
||||
parameterFlowsThroughRev(p, ap, kind, _, config)
|
||||
parameterFlowsThroughRev(p, ap, kind, config)
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
predicate returnMayFlowThrough(
|
||||
RetNodeEx ret, Ap argAp, Ap ap, ReturnKindExt kind, Configuration config
|
||||
) {
|
||||
exists(ParamNodeEx p |
|
||||
returnFlowsThrough(ret, kind, _, _, p, argAp, ap, config) and
|
||||
parameterFlowsThroughRev(p, argAp, kind, ap, config)
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
predicate revFlowInToReturnIsReturned(
|
||||
DataFlowCall call, ArgNodeEx arg, FlowState state, ReturnCtx returnCtx, ApOption returnAp,
|
||||
Ap ap, Configuration config
|
||||
) {
|
||||
exists(ReturnKindExt returnKind0, Ap returnAp0 |
|
||||
revFlowInToReturn(call, arg, state, returnKind0, returnAp0, ap, config) and
|
||||
revFlowIsReturned(call, returnCtx, returnAp, returnKind0, returnAp0, config)
|
||||
predicate returnMayFlowThrough(RetNodeEx ret, ReturnKindExt kind, Configuration config) {
|
||||
exists(ParamNodeEx p, Ap ap |
|
||||
returnFlowsThrough(ret, kind, _, _, p, ap, _, config) and
|
||||
parameterFlowsThroughRev(p, ap, kind, config)
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
predicate callMayFlowThroughRev(DataFlowCall call, Configuration config) {
|
||||
exists(ArgNodeEx arg, FlowState state, ReturnCtx returnCtx, ApOption returnAp, Ap ap |
|
||||
exists(
|
||||
ReturnKindExt returnKind0, Ap returnAp0, ArgNodeEx arg, FlowState state,
|
||||
ReturnCtx returnCtx, ApOption returnAp, Ap ap
|
||||
|
|
||||
revFlow(arg, state, returnCtx, returnAp, ap, config) and
|
||||
revFlowInToReturnIsReturned(call, arg, state, returnCtx, returnAp, ap, config)
|
||||
revFlowInToReturn(call, arg, state, returnKind0, returnAp0, ap, config) and
|
||||
revFlowIsReturned(call, returnCtx, returnAp, returnKind0, returnAp0, config)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -2317,16 +2179,16 @@ private module LocalFlowBigStep {
|
||||
pragma[nomagic]
|
||||
predicate localFlowBigStep(
|
||||
NodeEx node1, FlowState state1, NodeEx node2, FlowState state2, boolean preservesValue,
|
||||
DataFlowType t, Configuration config, LocalCallContext callContext
|
||||
AccessPathFrontNil apf, Configuration config, LocalCallContext callContext
|
||||
) {
|
||||
localFlowStepPlus(node1, state1, node2, preservesValue, t, config, callContext) and
|
||||
localFlowStepPlus(node1, state1, node2, preservesValue, apf.getType(), config, callContext) and
|
||||
localFlowExit(node2, state1, config) and
|
||||
state1 = state2
|
||||
or
|
||||
additionalLocalFlowStepNodeCand2(node1, state1, node2, state2, config) and
|
||||
state1 != state2 and
|
||||
preservesValue = false and
|
||||
t = node2.getDataFlowType() and
|
||||
apf = TFrontNil(node2.getDataFlowType()) and
|
||||
callContext.relevantFor(node1.getEnclosingCallable()) and
|
||||
not exists(DataFlowCall call | call = callContext.(LocalCallContextSpecificCall).getCall() |
|
||||
isUnreachableInCallCached(node1.asNode(), call) or
|
||||
@@ -2340,87 +2202,11 @@ private import LocalFlowBigStep
|
||||
private module Stage3Param implements MkStage<Stage2>::StageParam {
|
||||
private module PrevStage = Stage2;
|
||||
|
||||
class Ap = ApproxAccessPathFront;
|
||||
|
||||
class ApNil = ApproxAccessPathFrontNil;
|
||||
|
||||
PrevStage::Ap getApprox(Ap ap) { result = ap.toBoolNonEmpty() }
|
||||
|
||||
ApNil getApNil(NodeEx node) {
|
||||
PrevStage::revFlow(node, _) and result = TApproxFrontNil(node.getDataFlowType())
|
||||
}
|
||||
|
||||
bindingset[tc, tail]
|
||||
Ap apCons(TypedContent tc, Ap tail) { result.getAHead() = tc and exists(tail) }
|
||||
|
||||
pragma[noinline]
|
||||
Content getHeadContent(Ap ap) { result = ap.getAHead().getContent() }
|
||||
|
||||
class ApOption = ApproxAccessPathFrontOption;
|
||||
|
||||
ApOption apNone() { result = TApproxAccessPathFrontNone() }
|
||||
|
||||
ApOption apSome(Ap ap) { result = TApproxAccessPathFrontSome(ap) }
|
||||
|
||||
import BooleanCallContext
|
||||
|
||||
predicate localStep(
|
||||
NodeEx node1, FlowState state1, NodeEx node2, FlowState state2, boolean preservesValue,
|
||||
ApproxAccessPathFrontNil ap, Configuration config, LocalCc lcc
|
||||
) {
|
||||
localFlowBigStep(node1, state1, node2, state2, preservesValue, ap.getType(), config, _) and
|
||||
exists(lcc)
|
||||
}
|
||||
|
||||
predicate flowOutOfCall = flowOutOfCallNodeCand2/6;
|
||||
|
||||
predicate flowIntoCall = flowIntoCallNodeCand2/5;
|
||||
|
||||
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.getAHead().getContent()
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate castingNodeEx(NodeEx node) { node.asNode() instanceof CastingNode }
|
||||
|
||||
bindingset[node, state, ap, config]
|
||||
predicate filter(NodeEx node, FlowState state, Ap ap, Configuration config) {
|
||||
exists(state) and
|
||||
exists(config) and
|
||||
(if castingNodeEx(node) then compatibleTypes(node.getDataFlowType(), ap.getType()) else any()) and
|
||||
(
|
||||
notExpectsContent(node)
|
||||
or
|
||||
expectsContentCand(node, ap, config)
|
||||
)
|
||||
}
|
||||
|
||||
bindingset[ap, contentType]
|
||||
predicate typecheckStore(Ap ap, DataFlowType contentType) {
|
||||
// We need to typecheck stores here, since reverse flow through a getter
|
||||
// might have a different type here compared to inside the getter.
|
||||
compatibleTypes(ap.getType(), contentType)
|
||||
}
|
||||
}
|
||||
|
||||
private module Stage3 implements StageSig {
|
||||
import MkStage<Stage2>::Stage<Stage3Param>
|
||||
}
|
||||
|
||||
private module Stage4Param implements MkStage<Stage3>::StageParam {
|
||||
private module PrevStage = Stage3;
|
||||
|
||||
class Ap = AccessPathFront;
|
||||
|
||||
class ApNil = AccessPathFrontNil;
|
||||
|
||||
PrevStage::Ap getApprox(Ap ap) { result = ap.toApprox() }
|
||||
PrevStage::Ap getApprox(Ap ap) { result = ap.toBoolNonEmpty() }
|
||||
|
||||
ApNil getApNil(NodeEx node) {
|
||||
PrevStage::revFlow(node, _) and result = TFrontNil(node.getDataFlowType())
|
||||
@@ -2440,42 +2226,16 @@ private module Stage4Param implements MkStage<Stage3>::StageParam {
|
||||
|
||||
import BooleanCallContext
|
||||
|
||||
pragma[nomagic]
|
||||
predicate localStep(
|
||||
NodeEx node1, FlowState state1, NodeEx node2, FlowState state2, boolean preservesValue,
|
||||
ApNil ap, Configuration config, LocalCc lcc
|
||||
) {
|
||||
localFlowBigStep(node1, state1, node2, state2, preservesValue, ap.getType(), config, _) and
|
||||
PrevStage::revFlow(node1, pragma[only_bind_into](state1), _, pragma[only_bind_into](config)) and
|
||||
PrevStage::revFlowAlias(node2, pragma[only_bind_into](state2), _, pragma[only_bind_into](config)) and
|
||||
exists(lcc)
|
||||
localFlowBigStep(node1, state1, node2, state2, preservesValue, ap, config, _) and exists(lcc)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
predicate flowOutOfCall(
|
||||
DataFlowCall call, RetNodeEx node1, ReturnKindExt kind, NodeEx node2, boolean allowsFieldFlow,
|
||||
Configuration config
|
||||
) {
|
||||
exists(FlowState state |
|
||||
flowOutOfCallNodeCand2(call, node1, kind, node2, allowsFieldFlow, config) and
|
||||
PrevStage::revFlow(node2, pragma[only_bind_into](state), _, pragma[only_bind_into](config)) and
|
||||
PrevStage::revFlowAlias(node1, pragma[only_bind_into](state), _,
|
||||
pragma[only_bind_into](config))
|
||||
)
|
||||
}
|
||||
predicate flowOutOfCall = flowOutOfCallNodeCand2/6;
|
||||
|
||||
pragma[nomagic]
|
||||
predicate flowIntoCall(
|
||||
DataFlowCall call, ArgNodeEx node1, ParamNodeEx node2, boolean allowsFieldFlow,
|
||||
Configuration config
|
||||
) {
|
||||
exists(FlowState state |
|
||||
flowIntoCallNodeCand2(call, node1, node2, allowsFieldFlow, config) and
|
||||
PrevStage::revFlow(node2, pragma[only_bind_into](state), _, pragma[only_bind_into](config)) and
|
||||
PrevStage::revFlowAlias(node1, pragma[only_bind_into](state), _,
|
||||
pragma[only_bind_into](config))
|
||||
)
|
||||
}
|
||||
predicate flowIntoCall = flowIntoCallNodeCand2/5;
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate clearSet(NodeEx node, ContentSet c, Configuration config) {
|
||||
@@ -2531,8 +2291,8 @@ private module Stage4Param implements MkStage<Stage3>::StageParam {
|
||||
}
|
||||
}
|
||||
|
||||
private module Stage4 implements StageSig {
|
||||
import MkStage<Stage3>::Stage<Stage4Param>
|
||||
private module Stage3 implements StageSig {
|
||||
import MkStage<Stage2>::Stage<Stage3Param>
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -2543,8 +2303,8 @@ private predicate flowCandSummaryCtx(
|
||||
NodeEx node, FlowState state, AccessPathFront argApf, Configuration config
|
||||
) {
|
||||
exists(AccessPathFront apf |
|
||||
Stage4::revFlow(node, state, TReturnCtxMaybeFlowThrough(_), _, apf, config) and
|
||||
Stage4::fwdFlow(node, state, any(Stage4::CcCall ccc), _, TAccessPathFrontSome(argApf), apf,
|
||||
Stage3::revFlow(node, state, TReturnCtxMaybeFlowThrough(_), _, apf, config) and
|
||||
Stage3::fwdFlow(node, state, any(Stage3::CcCall ccc), _, TAccessPathFrontSome(argApf), apf,
|
||||
config)
|
||||
)
|
||||
}
|
||||
@@ -2555,10 +2315,10 @@ private predicate flowCandSummaryCtx(
|
||||
*/
|
||||
private predicate expensiveLen2unfolding(TypedContent tc, Configuration config) {
|
||||
exists(int tails, int nodes, int apLimit, int tupleLimit |
|
||||
tails = strictcount(AccessPathFront apf | Stage4::consCand(tc, apf, config)) and
|
||||
tails = strictcount(AccessPathFront apf | Stage3::consCand(tc, apf, config)) and
|
||||
nodes =
|
||||
strictcount(NodeEx n, FlowState state |
|
||||
Stage4::revFlow(n, state, any(AccessPathFrontHead apf | apf.getHead() = tc), config)
|
||||
Stage3::revFlow(n, state, any(AccessPathFrontHead apf | apf.getHead() = tc), config)
|
||||
or
|
||||
flowCandSummaryCtx(n, state, any(AccessPathFrontHead apf | apf.getHead() = tc), config)
|
||||
) and
|
||||
@@ -2572,11 +2332,11 @@ private predicate expensiveLen2unfolding(TypedContent tc, Configuration config)
|
||||
private newtype TAccessPathApprox =
|
||||
TNil(DataFlowType t) or
|
||||
TConsNil(TypedContent tc, DataFlowType t) {
|
||||
Stage4::consCand(tc, TFrontNil(t), _) and
|
||||
Stage3::consCand(tc, TFrontNil(t), _) and
|
||||
not expensiveLen2unfolding(tc, _)
|
||||
} or
|
||||
TConsCons(TypedContent tc1, TypedContent tc2, int len) {
|
||||
Stage4::consCand(tc1, TFrontHead(tc2), _) and
|
||||
Stage3::consCand(tc1, TFrontHead(tc2), _) and
|
||||
len in [2 .. accessPathLimit()] and
|
||||
not expensiveLen2unfolding(tc1, _)
|
||||
} or
|
||||
@@ -2706,7 +2466,7 @@ private class AccessPathApproxCons1 extends AccessPathApproxCons, TCons1 {
|
||||
override AccessPathApprox pop(TypedContent head) {
|
||||
head = tc and
|
||||
(
|
||||
exists(TypedContent tc2 | Stage4::consCand(tc, TFrontHead(tc2), _) |
|
||||
exists(TypedContent tc2 | Stage3::consCand(tc, TFrontHead(tc2), _) |
|
||||
result = TConsCons(tc2, _, len - 1)
|
||||
or
|
||||
len = 2 and
|
||||
@@ -2717,7 +2477,7 @@ private class AccessPathApproxCons1 extends AccessPathApproxCons, TCons1 {
|
||||
or
|
||||
exists(DataFlowType t |
|
||||
len = 1 and
|
||||
Stage4::consCand(tc, TFrontNil(t), _) and
|
||||
Stage3::consCand(tc, TFrontNil(t), _) and
|
||||
result = TNil(t)
|
||||
)
|
||||
)
|
||||
@@ -2742,14 +2502,13 @@ private class AccessPathApproxOption extends TAccessPathApproxOption {
|
||||
}
|
||||
}
|
||||
|
||||
private module Stage5Param implements MkStage<Stage4>::StageParam {
|
||||
private module PrevStage = Stage4;
|
||||
private module Stage4Param implements MkStage<Stage3>::StageParam {
|
||||
private module PrevStage = Stage3;
|
||||
|
||||
class Ap = AccessPathApprox;
|
||||
|
||||
class ApNil = AccessPathApproxNil;
|
||||
|
||||
pragma[nomagic]
|
||||
PrevStage::Ap getApprox(Ap ap) { result = ap.getFront() }
|
||||
|
||||
ApNil getApNil(NodeEx node) {
|
||||
@@ -2775,9 +2534,7 @@ private module Stage5Param implements MkStage<Stage4>::StageParam {
|
||||
NodeEx node1, FlowState state1, NodeEx node2, FlowState state2, boolean preservesValue,
|
||||
ApNil ap, Configuration config, LocalCc lcc
|
||||
) {
|
||||
localFlowBigStep(node1, state1, node2, state2, preservesValue, ap.getType(), config, lcc) and
|
||||
PrevStage::revFlow(node1, pragma[only_bind_into](state1), _, pragma[only_bind_into](config)) and
|
||||
PrevStage::revFlowAlias(node2, pragma[only_bind_into](state2), _, pragma[only_bind_into](config))
|
||||
localFlowBigStep(node1, state1, node2, state2, preservesValue, ap.getFront(), config, lcc)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
@@ -2814,7 +2571,7 @@ private module Stage5Param implements MkStage<Stage4>::StageParam {
|
||||
predicate typecheckStore(Ap ap, DataFlowType contentType) { any() }
|
||||
}
|
||||
|
||||
private module Stage5 = MkStage<Stage4>::Stage<Stage5Param>;
|
||||
private module Stage4 = MkStage<Stage3>::Stage<Stage4Param>;
|
||||
|
||||
bindingset[conf, result]
|
||||
private Configuration unbindConf(Configuration conf) {
|
||||
@@ -2828,8 +2585,8 @@ private predicate nodeMayUseSummary0(
|
||||
) {
|
||||
exists(AccessPathApprox apa0 |
|
||||
c = n.getEnclosingCallable() and
|
||||
Stage5::revFlow(n, state, TReturnCtxMaybeFlowThrough(_), _, apa0, config) and
|
||||
Stage5::fwdFlow(n, state, any(CallContextCall ccc), TParameterPositionSome(pos),
|
||||
Stage4::revFlow(n, state, TReturnCtxMaybeFlowThrough(_), _, apa0, config) and
|
||||
Stage4::fwdFlow(n, state, any(CallContextCall ccc), TParameterPositionSome(pos),
|
||||
TAccessPathApproxSome(apa), apa0, config)
|
||||
)
|
||||
}
|
||||
@@ -2839,7 +2596,7 @@ private predicate nodeMayUseSummary(
|
||||
NodeEx n, FlowState state, AccessPathApprox apa, Configuration config
|
||||
) {
|
||||
exists(DataFlowCallable c, ParameterPosition pos, ParamNodeEx p |
|
||||
Stage5::parameterMayFlowThrough(p, apa, config) and
|
||||
Stage4::parameterMayFlowThrough(p, apa, config) and
|
||||
nodeMayUseSummary0(n, c, pos, state, apa, config) and
|
||||
p.isParameterOf(c, pos)
|
||||
)
|
||||
@@ -2849,8 +2606,8 @@ private newtype TSummaryCtx =
|
||||
TSummaryCtxNone() or
|
||||
TSummaryCtxSome(ParamNodeEx p, FlowState state, AccessPath ap) {
|
||||
exists(Configuration config |
|
||||
Stage5::parameterMayFlowThrough(p, ap.getApprox(), config) and
|
||||
Stage5::revFlow(p, state, _, config)
|
||||
Stage4::parameterMayFlowThrough(p, ap.getApprox(), config) and
|
||||
Stage4::revFlow(p, state, _, config)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -2899,7 +2656,7 @@ private int count1to2unfold(AccessPathApproxCons1 apa, Configuration config) {
|
||||
len = apa.len() and
|
||||
result =
|
||||
strictcount(AccessPathFront apf |
|
||||
Stage5::consCand(tc, any(AccessPathApprox ap | ap.getFront() = apf and ap.len() = len - 1),
|
||||
Stage4::consCand(tc, any(AccessPathApprox ap | ap.getFront() = apf and ap.len() = len - 1),
|
||||
config)
|
||||
)
|
||||
)
|
||||
@@ -2908,7 +2665,7 @@ private int count1to2unfold(AccessPathApproxCons1 apa, Configuration config) {
|
||||
private int countNodesUsingAccessPath(AccessPathApprox apa, Configuration config) {
|
||||
result =
|
||||
strictcount(NodeEx n, FlowState state |
|
||||
Stage5::revFlow(n, state, apa, config) or nodeMayUseSummary(n, state, apa, config)
|
||||
Stage4::revFlow(n, state, apa, config) or nodeMayUseSummary(n, state, apa, config)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -2929,7 +2686,7 @@ private predicate expensiveLen1to2unfolding(AccessPathApproxCons1 apa, Configura
|
||||
private AccessPathApprox getATail(AccessPathApprox apa, Configuration config) {
|
||||
exists(TypedContent head |
|
||||
apa.pop(head) = result and
|
||||
Stage5::consCand(head, result, config)
|
||||
Stage4::consCand(head, result, config)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -3011,7 +2768,7 @@ private newtype TPathNode =
|
||||
NodeEx node, FlowState state, CallContext cc, SummaryCtx sc, AccessPath ap, Configuration config
|
||||
) {
|
||||
// A PathNode is introduced by a source ...
|
||||
Stage5::revFlow(node, state, config) and
|
||||
Stage4::revFlow(node, state, config) and
|
||||
sourceNode(node, state, config) and
|
||||
(
|
||||
if hasSourceCallCtx(config)
|
||||
@@ -3025,7 +2782,7 @@ private newtype TPathNode =
|
||||
exists(PathNodeMid mid |
|
||||
pathStep(mid, node, state, cc, sc, ap) and
|
||||
pragma[only_bind_into](config) = mid.getConfiguration() and
|
||||
Stage5::revFlow(node, state, ap.getApprox(), pragma[only_bind_into](config))
|
||||
Stage4::revFlow(node, state, ap.getApprox(), pragma[only_bind_into](config))
|
||||
)
|
||||
} or
|
||||
TPathNodeSink(NodeEx node, FlowState state, Configuration config) {
|
||||
@@ -3167,7 +2924,7 @@ private class AccessPathCons2 extends AccessPath, TAccessPathCons2 {
|
||||
override TypedContent getHead() { result = head1 }
|
||||
|
||||
override AccessPath getTail() {
|
||||
Stage5::consCand(head1, result.getApprox(), _) and
|
||||
Stage4::consCand(head1, result.getApprox(), _) and
|
||||
result.getHead() = head2 and
|
||||
result.length() = len - 1
|
||||
}
|
||||
@@ -3198,7 +2955,7 @@ private class AccessPathCons1 extends AccessPath, TAccessPathCons1 {
|
||||
override TypedContent getHead() { result = head }
|
||||
|
||||
override AccessPath getTail() {
|
||||
Stage5::consCand(head, result.getApprox(), _) and result.length() = len - 1
|
||||
Stage4::consCand(head, result.getApprox(), _) and result.length() = len - 1
|
||||
}
|
||||
|
||||
override AccessPathFrontHead getFront() { result = TFrontHead(head) }
|
||||
@@ -3590,8 +3347,7 @@ private predicate pathStep(
|
||||
AccessPath ap0, NodeEx midnode, FlowState state0, Configuration conf, LocalCallContext localCC
|
||||
|
|
||||
pathNode(mid, midnode, state0, cc, sc, ap0, conf, localCC) and
|
||||
localFlowBigStep(midnode, state0, node, state, false, ap.(AccessPathNil).getType(), conf,
|
||||
localCC) and
|
||||
localFlowBigStep(midnode, state0, node, state, false, ap.getFront(), conf, localCC) and
|
||||
ap0 instanceof AccessPathNil
|
||||
)
|
||||
or
|
||||
@@ -3633,7 +3389,7 @@ private predicate pathReadStep(
|
||||
) {
|
||||
ap0 = mid.getAp() and
|
||||
tc = ap0.getHead() and
|
||||
Stage5::readStepCand(mid.getNodeEx(), tc.getContent(), node, mid.getConfiguration()) and
|
||||
Stage4::readStepCand(mid.getNodeEx(), tc.getContent(), node, mid.getConfiguration()) and
|
||||
state = mid.getState() and
|
||||
cc = mid.getCallContext()
|
||||
}
|
||||
@@ -3643,7 +3399,7 @@ private predicate pathStoreStep(
|
||||
PathNodeMid mid, NodeEx node, FlowState state, AccessPath ap0, TypedContent tc, CallContext cc
|
||||
) {
|
||||
ap0 = mid.getAp() and
|
||||
Stage5::storeStepCand(mid.getNodeEx(), _, tc, node, _, mid.getConfiguration()) and
|
||||
Stage4::storeStepCand(mid.getNodeEx(), _, tc, node, _, mid.getConfiguration()) and
|
||||
state = mid.getState() and
|
||||
cc = mid.getCallContext()
|
||||
}
|
||||
@@ -3680,7 +3436,7 @@ private NodeEx getAnOutNodeFlow(
|
||||
ReturnKindExt kind, DataFlowCall call, AccessPathApprox apa, Configuration config
|
||||
) {
|
||||
result.asNode() = kind.getAnOutNode(call) and
|
||||
Stage5::revFlow(result, _, apa, config)
|
||||
Stage4::revFlow(result, _, apa, config)
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -3716,7 +3472,7 @@ private predicate parameterCand(
|
||||
DataFlowCallable callable, ParameterPosition pos, AccessPathApprox apa, Configuration config
|
||||
) {
|
||||
exists(ParamNodeEx p |
|
||||
Stage5::revFlow(p, _, apa, config) and
|
||||
Stage4::revFlow(p, _, apa, config) and
|
||||
p.isParameterOf(callable, pos)
|
||||
)
|
||||
}
|
||||
@@ -3995,17 +3751,9 @@ predicate stageStats(
|
||||
n = 45 and
|
||||
Stage4::stats(false, nodes, fields, conscand, states, tuples, config)
|
||||
or
|
||||
stage = "5 Fwd" and
|
||||
n = 50 and
|
||||
Stage5::stats(true, nodes, fields, conscand, states, tuples, config)
|
||||
stage = "5 Fwd" and n = 50 and finalStats(true, nodes, fields, conscand, states, tuples)
|
||||
or
|
||||
stage = "5 Rev" and
|
||||
n = 55 and
|
||||
Stage5::stats(false, nodes, fields, conscand, states, tuples, config)
|
||||
or
|
||||
stage = "6 Fwd" and n = 60 and finalStats(true, nodes, fields, conscand, states, tuples)
|
||||
or
|
||||
stage = "6 Rev" and n = 65 and finalStats(false, nodes, fields, conscand, states, tuples)
|
||||
stage = "5 Rev" and n = 55 and finalStats(false, nodes, fields, conscand, states, tuples)
|
||||
}
|
||||
|
||||
private module FlowExploration {
|
||||
|
||||
@@ -926,46 +926,18 @@ private module Cached {
|
||||
TReturnCtxNoFlowThrough() or
|
||||
TReturnCtxMaybeFlowThrough(ReturnKindExt kind)
|
||||
|
||||
cached
|
||||
newtype TTypedContentApprox =
|
||||
MkTypedContentApprox(ContentApprox c, DataFlowType t) {
|
||||
exists(Content cont |
|
||||
c = getContentApprox(cont) and
|
||||
store(_, cont, _, _, t)
|
||||
)
|
||||
}
|
||||
|
||||
cached
|
||||
newtype TTypedContent = MkTypedContent(Content c, DataFlowType t) { store(_, c, _, _, t) }
|
||||
|
||||
cached
|
||||
TypedContent getATypedContent(TypedContentApprox c) {
|
||||
exists(ContentApprox cls, DataFlowType t, Content cont |
|
||||
c = MkTypedContentApprox(cls, pragma[only_bind_into](t)) and
|
||||
result = MkTypedContent(cont, pragma[only_bind_into](t)) and
|
||||
cls = getContentApprox(cont)
|
||||
)
|
||||
}
|
||||
|
||||
cached
|
||||
newtype TAccessPathFront =
|
||||
TFrontNil(DataFlowType t) or
|
||||
TFrontHead(TypedContent tc)
|
||||
|
||||
cached
|
||||
newtype TApproxAccessPathFront =
|
||||
TApproxFrontNil(DataFlowType t) or
|
||||
TApproxFrontHead(TypedContentApprox tc)
|
||||
|
||||
cached
|
||||
newtype TAccessPathFrontOption =
|
||||
TAccessPathFrontNone() or
|
||||
TAccessPathFrontSome(AccessPathFront apf)
|
||||
|
||||
cached
|
||||
newtype TApproxAccessPathFrontOption =
|
||||
TApproxAccessPathFrontNone() or
|
||||
TApproxAccessPathFrontSome(ApproxAccessPathFront apf)
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1381,75 +1353,6 @@ class ReturnCtx extends TReturnCtx {
|
||||
}
|
||||
}
|
||||
|
||||
/** An approximated `Content` tagged with the type of a containing object. */
|
||||
class TypedContentApprox extends MkTypedContentApprox {
|
||||
private ContentApprox c;
|
||||
private DataFlowType t;
|
||||
|
||||
TypedContentApprox() { this = MkTypedContentApprox(c, t) }
|
||||
|
||||
/** Gets a typed content approximated by this value. */
|
||||
TypedContent getATypedContent() { result = getATypedContent(this) }
|
||||
|
||||
/** Gets the container type. */
|
||||
DataFlowType getContainerType() { result = t }
|
||||
|
||||
/** Gets a textual representation of this approximated content. */
|
||||
string toString() { result = c.toString() }
|
||||
}
|
||||
|
||||
/**
|
||||
* The front of an approximated access path. This is either a head or a nil.
|
||||
*/
|
||||
abstract class ApproxAccessPathFront extends TApproxAccessPathFront {
|
||||
abstract string toString();
|
||||
|
||||
abstract DataFlowType getType();
|
||||
|
||||
abstract boolean toBoolNonEmpty();
|
||||
|
||||
pragma[nomagic]
|
||||
TypedContent getAHead() {
|
||||
exists(TypedContentApprox cont |
|
||||
this = TApproxFrontHead(cont) and
|
||||
result = cont.getATypedContent()
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
class ApproxAccessPathFrontNil extends ApproxAccessPathFront, TApproxFrontNil {
|
||||
private DataFlowType t;
|
||||
|
||||
ApproxAccessPathFrontNil() { this = TApproxFrontNil(t) }
|
||||
|
||||
override string toString() { result = ppReprType(t) }
|
||||
|
||||
override DataFlowType getType() { result = t }
|
||||
|
||||
override boolean toBoolNonEmpty() { result = false }
|
||||
}
|
||||
|
||||
class ApproxAccessPathFrontHead extends ApproxAccessPathFront, TApproxFrontHead {
|
||||
private TypedContentApprox tc;
|
||||
|
||||
ApproxAccessPathFrontHead() { this = TApproxFrontHead(tc) }
|
||||
|
||||
override string toString() { result = tc.toString() }
|
||||
|
||||
override DataFlowType getType() { result = tc.getContainerType() }
|
||||
|
||||
override boolean toBoolNonEmpty() { result = true }
|
||||
}
|
||||
|
||||
/** An optional approximated access path front. */
|
||||
class ApproxAccessPathFrontOption extends TApproxAccessPathFrontOption {
|
||||
string toString() {
|
||||
this = TApproxAccessPathFrontNone() and result = "<none>"
|
||||
or
|
||||
this = TApproxAccessPathFrontSome(any(ApproxAccessPathFront apf | result = apf.toString()))
|
||||
}
|
||||
}
|
||||
|
||||
/** A `Content` tagged with the type of a containing object. */
|
||||
class TypedContent extends MkTypedContent {
|
||||
private Content c;
|
||||
@@ -1482,7 +1385,7 @@ abstract class AccessPathFront extends TAccessPathFront {
|
||||
|
||||
abstract DataFlowType getType();
|
||||
|
||||
abstract ApproxAccessPathFront toApprox();
|
||||
abstract boolean toBoolNonEmpty();
|
||||
|
||||
TypedContent getHead() { this = TFrontHead(result) }
|
||||
}
|
||||
@@ -1496,7 +1399,7 @@ class AccessPathFrontNil extends AccessPathFront, TFrontNil {
|
||||
|
||||
override DataFlowType getType() { result = t }
|
||||
|
||||
override ApproxAccessPathFront toApprox() { result = TApproxFrontNil(t) }
|
||||
override boolean toBoolNonEmpty() { result = false }
|
||||
}
|
||||
|
||||
class AccessPathFrontHead extends AccessPathFront, TFrontHead {
|
||||
@@ -1508,7 +1411,7 @@ class AccessPathFrontHead extends AccessPathFront, TFrontHead {
|
||||
|
||||
override DataFlowType getType() { result = tc.getContainerType() }
|
||||
|
||||
override ApproxAccessPathFront toApprox() { result.getAHead() = tc }
|
||||
override boolean toBoolNonEmpty() { result = true }
|
||||
}
|
||||
|
||||
/** An optional access path front. */
|
||||
|
||||
@@ -260,9 +260,4 @@ module Consistency {
|
||||
not exists(unique(ParameterPosition pos0 | isParameterNode(p, c, pos0))) and
|
||||
msg = "Parameter node with multiple positions."
|
||||
}
|
||||
|
||||
query predicate uniqueContentApprox(Content c, string msg) {
|
||||
not exists(unique(ContentApprox approx | approx = getContentApprox(c))) and
|
||||
msg = "Non-unique content approximation."
|
||||
}
|
||||
}
|
||||
|
||||
@@ -400,13 +400,6 @@ predicate additionalLambdaFlowStep(Node nodeFrom, Node nodeTo, boolean preserves
|
||||
*/
|
||||
predicate allowParameterReturnInSelf(ParameterNode p) { none() }
|
||||
|
||||
/** An approximated `Content`. */
|
||||
class ContentApprox = Unit;
|
||||
|
||||
/** Gets an approximated value for content `c`. */
|
||||
pragma[inline]
|
||||
ContentApprox getContentApprox(Content c) { any() }
|
||||
|
||||
private class MyConsistencyConfiguration extends Consistency::ConsistencyConfiguration {
|
||||
override predicate argHasPostUpdateExclude(ArgumentNode n) {
|
||||
// The rules for whether an IR argument gets a post-update node are too
|
||||
|
||||
@@ -1,644 +0,0 @@
|
||||
/**
|
||||
* INTERNAL: Do not use.
|
||||
*
|
||||
* An IR taint tracking library that uses an IR DataFlow configuration to track
|
||||
* taint from user inputs as defined by `semmle.code.cpp.security.Security`.
|
||||
*/
|
||||
|
||||
import cpp
|
||||
import semmle.code.cpp.security.Security
|
||||
private import semmle.code.cpp.ir.dataflow.DataFlow
|
||||
private import semmle.code.cpp.ir.dataflow.internal.DataFlowUtil
|
||||
private import semmle.code.cpp.ir.dataflow.DataFlow3
|
||||
private import semmle.code.cpp.ir.IR
|
||||
private import semmle.code.cpp.ir.dataflow.ResolveCall
|
||||
private import semmle.code.cpp.controlflow.IRGuards
|
||||
private import semmle.code.cpp.models.interfaces.Taint
|
||||
private import semmle.code.cpp.models.interfaces.DataFlow
|
||||
private import semmle.code.cpp.ir.dataflow.TaintTracking
|
||||
private import semmle.code.cpp.ir.dataflow.TaintTracking2
|
||||
private import semmle.code.cpp.ir.dataflow.TaintTracking3
|
||||
private import semmle.code.cpp.ir.dataflow.internal.ModelUtil
|
||||
|
||||
/**
|
||||
* A predictable instruction is one where an external user can predict
|
||||
* the value. For example, a literal in the source code is considered
|
||||
* predictable.
|
||||
*/
|
||||
private predicate predictableInstruction(Instruction instr) {
|
||||
instr instanceof ConstantInstruction
|
||||
or
|
||||
instr instanceof StringConstantInstruction
|
||||
or
|
||||
// This could be a conversion on a string literal
|
||||
predictableInstruction(instr.(UnaryInstruction).getUnary())
|
||||
}
|
||||
|
||||
/**
|
||||
* Functions that we should only allow taint to flow through (to the return
|
||||
* value) if all but the source argument are 'predictable'. This is done to
|
||||
* emulate the old security library's implementation rather than due to any
|
||||
* strong belief that this is the right approach.
|
||||
*
|
||||
* Note that the list itself is not very principled; it consists of all the
|
||||
* functions listed in the old security library's [default] `isPureFunction`
|
||||
* that have more than one argument, but are not in the old taint tracking
|
||||
* library's `returnArgument` predicate.
|
||||
*/
|
||||
predicate predictableOnlyFlow(string name) {
|
||||
name =
|
||||
[
|
||||
"strcasestr", "strchnul", "strchr", "strchrnul", "strcmp", "strcspn", "strncmp", "strndup",
|
||||
"strnlen", "strrchr", "strspn", "strstr", "strtod", "strtof", "strtol", "strtoll", "strtoq",
|
||||
"strtoul"
|
||||
]
|
||||
}
|
||||
|
||||
private DataFlow::Node getNodeForSource(Expr source) {
|
||||
isUserInput(source, _) and
|
||||
result = getNodeForExpr(source)
|
||||
}
|
||||
|
||||
private DataFlow::Node getNodeForExpr(Expr node) {
|
||||
result = DataFlow::exprNode(node)
|
||||
or
|
||||
// Some of the sources in `isUserInput` are intended to match the value of
|
||||
// an expression, while others (those modeled below) are intended to match
|
||||
// the taint that propagates out of an argument, like the `char *` argument
|
||||
// to `gets`. It's impossible here to tell which is which, but the "access
|
||||
// to argv" source is definitely not intended to match an output argument,
|
||||
// and it causes false positives if we let it.
|
||||
//
|
||||
// This case goes together with the similar (but not identical) rule in
|
||||
// `nodeIsBarrierIn`.
|
||||
result = DataFlow::definitionByReferenceNodeFromArgument(node) and
|
||||
not argv(node.(VariableAccess).getTarget())
|
||||
}
|
||||
|
||||
private class DefaultTaintTrackingCfg extends TaintTracking::Configuration {
|
||||
DefaultTaintTrackingCfg() { this = "DefaultTaintTrackingCfg" }
|
||||
|
||||
override predicate isSource(DataFlow::Node source) { source = getNodeForSource(_) }
|
||||
|
||||
override predicate isSink(DataFlow::Node sink) { exists(adjustedSink(sink)) }
|
||||
|
||||
override predicate isSanitizer(DataFlow::Node node) { nodeIsBarrier(node) }
|
||||
|
||||
override predicate isSanitizerIn(DataFlow::Node node) { nodeIsBarrierIn(node) }
|
||||
}
|
||||
|
||||
private class ToGlobalVarTaintTrackingCfg extends TaintTracking::Configuration {
|
||||
ToGlobalVarTaintTrackingCfg() { this = "GlobalVarTaintTrackingCfg" }
|
||||
|
||||
override predicate isSource(DataFlow::Node source) { source = getNodeForSource(_) }
|
||||
|
||||
override predicate isSink(DataFlow::Node sink) {
|
||||
sink.asVariable() instanceof GlobalOrNamespaceVariable
|
||||
}
|
||||
|
||||
override predicate isAdditionalTaintStep(DataFlow::Node n1, DataFlow::Node n2) {
|
||||
writesVariable(n1.asInstruction(), n2.asVariable().(GlobalOrNamespaceVariable))
|
||||
or
|
||||
readsVariable(n2.asInstruction(), n1.asVariable().(GlobalOrNamespaceVariable))
|
||||
}
|
||||
|
||||
override predicate isSanitizer(DataFlow::Node node) { nodeIsBarrier(node) }
|
||||
|
||||
override predicate isSanitizerIn(DataFlow::Node node) { nodeIsBarrierIn(node) }
|
||||
}
|
||||
|
||||
private class FromGlobalVarTaintTrackingCfg extends TaintTracking2::Configuration {
|
||||
FromGlobalVarTaintTrackingCfg() { this = "FromGlobalVarTaintTrackingCfg" }
|
||||
|
||||
override predicate isSource(DataFlow::Node source) {
|
||||
// This set of sources should be reasonably small, which is good for
|
||||
// performance since the set of sinks is very large.
|
||||
exists(ToGlobalVarTaintTrackingCfg otherCfg | otherCfg.hasFlowTo(source))
|
||||
}
|
||||
|
||||
override predicate isSink(DataFlow::Node sink) { exists(adjustedSink(sink)) }
|
||||
|
||||
override predicate isAdditionalTaintStep(DataFlow::Node n1, DataFlow::Node n2) {
|
||||
// Additional step for flow out of variables. There is no flow _into_
|
||||
// variables in this configuration, so this step only serves to take flow
|
||||
// out of a variable that's a source.
|
||||
readsVariable(n2.asInstruction(), n1.asVariable())
|
||||
}
|
||||
|
||||
override predicate isSanitizer(DataFlow::Node node) { nodeIsBarrier(node) }
|
||||
|
||||
override predicate isSanitizerIn(DataFlow::Node node) { nodeIsBarrierIn(node) }
|
||||
}
|
||||
|
||||
private predicate readsVariable(LoadInstruction load, Variable var) {
|
||||
load.getSourceAddress().(VariableAddressInstruction).getAstVariable() = var
|
||||
}
|
||||
|
||||
private predicate writesVariable(StoreInstruction store, Variable var) {
|
||||
store.getDestinationAddress().(VariableAddressInstruction).getAstVariable() = var
|
||||
}
|
||||
|
||||
/**
|
||||
* A variable that has any kind of upper-bound check anywhere in the program. This is
|
||||
* biased towards being inclusive because there are a lot of valid ways of doing an
|
||||
* upper bounds checks if we don't consider where it occurs, for example:
|
||||
* ```
|
||||
* if (x < 10) { sink(x); }
|
||||
*
|
||||
* if (10 > y) { sink(y); }
|
||||
*
|
||||
* if (z > 10) { z = 10; }
|
||||
* sink(z);
|
||||
* ```
|
||||
*/
|
||||
// TODO: This coarse overapproximation, ported from the old taint tracking
|
||||
// library, could be replaced with an actual semantic check that a particular
|
||||
// variable _access_ is guarded by an upper-bound check. We probably don't want
|
||||
// to do this right away since it could expose a lot of FPs that were
|
||||
// previously suppressed by this predicate by coincidence.
|
||||
private predicate hasUpperBoundsCheck(Variable var) {
|
||||
exists(RelationalOperation oper, VariableAccess access |
|
||||
oper.getAnOperand() = access and
|
||||
access.getTarget() = var and
|
||||
// Comparing to 0 is not an upper bound check
|
||||
not oper.getAnOperand().getValue() = "0"
|
||||
)
|
||||
}
|
||||
|
||||
private predicate nodeIsBarrierEqualityCandidate(
|
||||
DataFlow::Node node, Operand access, Variable checkedVar
|
||||
) {
|
||||
readsVariable(node.asInstruction(), checkedVar) and
|
||||
any(IRGuardCondition guard).ensuresEq(access, _, _, node.asInstruction().getBlock(), true)
|
||||
}
|
||||
|
||||
cached
|
||||
private module Cached {
|
||||
cached
|
||||
predicate nodeIsBarrier(DataFlow::Node node) {
|
||||
exists(Variable checkedVar |
|
||||
readsVariable(node.asInstruction(), checkedVar) and
|
||||
hasUpperBoundsCheck(checkedVar)
|
||||
)
|
||||
or
|
||||
exists(Variable checkedVar, Operand access |
|
||||
/*
|
||||
* This node is guarded by a condition that forces the accessed variable
|
||||
* to equal something else. For example:
|
||||
* ```
|
||||
* x = taintsource()
|
||||
* if (x == 10) {
|
||||
* taintsink(x); // not considered tainted
|
||||
* }
|
||||
* ```
|
||||
*/
|
||||
|
||||
nodeIsBarrierEqualityCandidate(node, access, checkedVar) and
|
||||
readsVariable(access.getDef(), checkedVar)
|
||||
)
|
||||
}
|
||||
|
||||
cached
|
||||
predicate nodeIsBarrierIn(DataFlow::Node node) {
|
||||
// don't use dataflow into taint sources, as this leads to duplicate results.
|
||||
exists(Expr source | isUserInput(source, _) |
|
||||
node = DataFlow::exprNode(source)
|
||||
or
|
||||
// This case goes together with the similar (but not identical) rule in
|
||||
// `getNodeForSource`.
|
||||
node = DataFlow::definitionByReferenceNodeFromArgument(source)
|
||||
)
|
||||
or
|
||||
// don't use dataflow into binary instructions if both operands are unpredictable
|
||||
exists(BinaryInstruction iTo |
|
||||
iTo = node.asInstruction() and
|
||||
not predictableInstruction(iTo.getLeft()) and
|
||||
not predictableInstruction(iTo.getRight()) and
|
||||
// propagate taint from either the pointer or the offset, regardless of predictability
|
||||
not iTo instanceof PointerArithmeticInstruction
|
||||
)
|
||||
or
|
||||
// don't use dataflow through calls to pure functions if two or more operands
|
||||
// are unpredictable
|
||||
exists(Instruction iFrom1, Instruction iFrom2, CallInstruction iTo |
|
||||
iTo = node.asInstruction() and
|
||||
isPureFunction(iTo.getStaticCallTarget().getName()) and
|
||||
iFrom1 = iTo.getAnArgument() and
|
||||
iFrom2 = iTo.getAnArgument() and
|
||||
not predictableInstruction(iFrom1) and
|
||||
not predictableInstruction(iFrom2) and
|
||||
iFrom1 != iFrom2
|
||||
)
|
||||
}
|
||||
|
||||
cached
|
||||
Element adjustedSink(DataFlow::Node sink) {
|
||||
// TODO: is it more appropriate to use asConvertedExpr here and avoid
|
||||
// `getConversion*`? Or will that cause us to miss some cases where there's
|
||||
// flow to a conversion (like a `ReferenceDereferenceExpr`) and we want to
|
||||
// pretend there was flow to the converted `Expr` for the sake of
|
||||
// compatibility.
|
||||
sink.asExpr().getConversion*() = result
|
||||
or
|
||||
// For compatibility, send flow from arguments to parameters, even for
|
||||
// functions with no body.
|
||||
exists(FunctionCall call, int i |
|
||||
sink.asExpr() = call.getArgument(pragma[only_bind_into](i)) and
|
||||
result = resolveCall(call).getParameter(pragma[only_bind_into](i))
|
||||
)
|
||||
or
|
||||
// For compatibility, send flow into a `Variable` if there is flow to any
|
||||
// Load or Store of that variable.
|
||||
exists(CopyInstruction copy |
|
||||
copy.getSourceValue() = sink.asInstruction() and
|
||||
(
|
||||
readsVariable(copy, result) or
|
||||
writesVariable(copy, result)
|
||||
) and
|
||||
not hasUpperBoundsCheck(result)
|
||||
)
|
||||
or
|
||||
// For compatibility, send flow into a `NotExpr` even if it's part of a
|
||||
// short-circuiting condition and thus might get skipped.
|
||||
result.(NotExpr).getOperand() = sink.asExpr()
|
||||
or
|
||||
// Taint postfix and prefix crement operations when their operand is tainted.
|
||||
result.(CrementOperation).getAnOperand() = sink.asExpr()
|
||||
or
|
||||
// Taint `e1 += e2`, `e &= e2` and friends when `e1` or `e2` is tainted.
|
||||
result.(AssignOperation).getAnOperand() = sink.asExpr()
|
||||
or
|
||||
result =
|
||||
sink.asOperand()
|
||||
.(SideEffectOperand)
|
||||
.getUse()
|
||||
.(ReadSideEffectInstruction)
|
||||
.getArgumentDef()
|
||||
.getUnconvertedResultExpression()
|
||||
}
|
||||
|
||||
/**
|
||||
* Step to return value of a modeled function when an input taints the
|
||||
* dereference of the return value.
|
||||
*/
|
||||
cached
|
||||
predicate additionalTaintStep(DataFlow::Node n1, DataFlow::Node n2) {
|
||||
exists(CallInstruction call, Function func, FunctionInput modelIn, FunctionOutput modelOut |
|
||||
n1.asOperand() = callInput(call, modelIn) and
|
||||
(
|
||||
func.(TaintFunction).hasTaintFlow(modelIn, modelOut)
|
||||
or
|
||||
func.(DataFlowFunction).hasDataFlow(modelIn, modelOut)
|
||||
) and
|
||||
call.getStaticCallTarget() = func and
|
||||
modelOut.isReturnValueDeref() and
|
||||
call = n2.asInstruction()
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private import Cached
|
||||
|
||||
/**
|
||||
* Holds if `tainted` may contain taint from `source`.
|
||||
*
|
||||
* A tainted expression is either directly user input, or is
|
||||
* computed from user input in a way that users can probably
|
||||
* control the exact output of the computation.
|
||||
*
|
||||
* This doesn't include data flow through global variables.
|
||||
* If you need that you must call `taintedIncludingGlobalVars`.
|
||||
*/
|
||||
cached
|
||||
predicate tainted(Expr source, Element tainted) {
|
||||
exists(DefaultTaintTrackingCfg cfg, DataFlow::Node sink |
|
||||
cfg.hasFlow(getNodeForSource(source), sink) and
|
||||
tainted = adjustedSink(sink)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `tainted` may contain taint from `source`, where the taint passed
|
||||
* through a global variable named `globalVar`.
|
||||
*
|
||||
* A tainted expression is either directly user input, or is
|
||||
* computed from user input in a way that users can probably
|
||||
* control the exact output of the computation.
|
||||
*
|
||||
* This version gives the same results as tainted but also includes
|
||||
* data flow through global variables.
|
||||
*
|
||||
* The parameter `globalVar` is the qualified name of the last global variable
|
||||
* used to move the value from source to tainted. If the taint did not pass
|
||||
* through a global variable, then `globalVar = ""`.
|
||||
*/
|
||||
cached
|
||||
predicate taintedIncludingGlobalVars(Expr source, Element tainted, string globalVar) {
|
||||
tainted(source, tainted) and
|
||||
globalVar = ""
|
||||
or
|
||||
exists(
|
||||
ToGlobalVarTaintTrackingCfg toCfg, FromGlobalVarTaintTrackingCfg fromCfg,
|
||||
DataFlow::VariableNode variableNode, GlobalOrNamespaceVariable global, DataFlow::Node sink
|
||||
|
|
||||
global = variableNode.getVariable() and
|
||||
toCfg.hasFlow(getNodeForSource(source), variableNode) and
|
||||
fromCfg.hasFlow(variableNode, sink) and
|
||||
tainted = adjustedSink(sink) and
|
||||
global = globalVarFromId(globalVar)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the global variable whose qualified name is `id`. Use this predicate
|
||||
* together with `taintedIncludingGlobalVars`. Example:
|
||||
*
|
||||
* ```
|
||||
* exists(string varName |
|
||||
* taintedIncludingGlobalVars(source, tainted, varName) and
|
||||
* var = globalVarFromId(varName)
|
||||
* )
|
||||
* ```
|
||||
*/
|
||||
GlobalOrNamespaceVariable globalVarFromId(string id) { id = result.getQualifiedName() }
|
||||
|
||||
/**
|
||||
* Provides definitions for augmenting source/sink pairs with data-flow paths
|
||||
* between them. From a `@kind path-problem` query, import this module in the
|
||||
* global scope, extend `TaintTrackingConfiguration`, and use `taintedWithPath`
|
||||
* in place of `tainted`.
|
||||
*
|
||||
* Importing this module will also import the query predicates that contain the
|
||||
* taint paths.
|
||||
*/
|
||||
module TaintedWithPath {
|
||||
private newtype TSingleton = MkSingleton()
|
||||
|
||||
/**
|
||||
* A taint-tracking configuration that matches sources and sinks in the same
|
||||
* way as the `tainted` predicate.
|
||||
*
|
||||
* Override `isSink` and `taintThroughGlobals` as needed, but do not provide
|
||||
* a characteristic predicate.
|
||||
*/
|
||||
class TaintTrackingConfiguration extends TSingleton {
|
||||
/** Override this to specify which elements are sources in this configuration. */
|
||||
predicate isSource(Expr source) { exists(getNodeForSource(source)) }
|
||||
|
||||
/** Override this to specify which elements are sinks in this configuration. */
|
||||
abstract predicate isSink(Element e);
|
||||
|
||||
/** Override this to specify which expressions are barriers in this configuration. */
|
||||
predicate isBarrier(Expr e) { nodeIsBarrier(getNodeForExpr(e)) }
|
||||
|
||||
/**
|
||||
* Override this predicate to `any()` to allow taint to flow through global
|
||||
* variables.
|
||||
*/
|
||||
predicate taintThroughGlobals() { none() }
|
||||
|
||||
/** Gets a textual representation of this element. */
|
||||
string toString() { result = "TaintTrackingConfiguration" }
|
||||
}
|
||||
|
||||
private class AdjustedConfiguration extends TaintTracking3::Configuration {
|
||||
AdjustedConfiguration() { this = "AdjustedConfiguration" }
|
||||
|
||||
override predicate isSource(DataFlow::Node source) {
|
||||
exists(TaintTrackingConfiguration cfg, Expr e |
|
||||
cfg.isSource(e) and source = getNodeForExpr(e)
|
||||
)
|
||||
}
|
||||
|
||||
override predicate isSink(DataFlow::Node sink) {
|
||||
exists(TaintTrackingConfiguration cfg | cfg.isSink(adjustedSink(sink)))
|
||||
}
|
||||
|
||||
override predicate isAdditionalTaintStep(DataFlow::Node n1, DataFlow::Node n2) {
|
||||
// Steps into and out of global variables
|
||||
exists(TaintTrackingConfiguration cfg | cfg.taintThroughGlobals() |
|
||||
writesVariable(n1.asInstruction(), n2.asVariable().(GlobalOrNamespaceVariable))
|
||||
or
|
||||
readsVariable(n2.asInstruction(), n1.asVariable().(GlobalOrNamespaceVariable))
|
||||
)
|
||||
or
|
||||
additionalTaintStep(n1, n2)
|
||||
}
|
||||
|
||||
override predicate isSanitizer(DataFlow::Node node) {
|
||||
exists(TaintTrackingConfiguration cfg, Expr e | cfg.isBarrier(e) and node = getNodeForExpr(e))
|
||||
}
|
||||
|
||||
override predicate isSanitizerIn(DataFlow::Node node) { nodeIsBarrierIn(node) }
|
||||
}
|
||||
|
||||
/*
|
||||
* A sink `Element` may map to multiple `DataFlowX::PathNode`s via (the
|
||||
* inverse of) `adjustedSink`. For example, an `Expr` maps to all its
|
||||
* conversions, and a `Variable` maps to all loads and stores from it. Because
|
||||
* the path node is part of the tuple that constitutes the alert, this leads
|
||||
* to duplicate alerts.
|
||||
*
|
||||
* To avoid showing duplicates, we edit the graph to replace the final node
|
||||
* coming from the data-flow library with a node that matches exactly the
|
||||
* `Element` sink that's requested.
|
||||
*
|
||||
* The same is done for sources.
|
||||
*/
|
||||
|
||||
private newtype TPathNode =
|
||||
TWrapPathNode(DataFlow3::PathNode n) or
|
||||
// There's a single newtype constructor for both sources and sinks since
|
||||
// that makes it easiest to deal with the case where source = sink.
|
||||
TEndpointPathNode(Element e) {
|
||||
exists(AdjustedConfiguration cfg, DataFlow3::Node sourceNode, DataFlow3::Node sinkNode |
|
||||
cfg.hasFlow(sourceNode, sinkNode)
|
||||
|
|
||||
sourceNode = getNodeForExpr(e) and
|
||||
exists(TaintTrackingConfiguration ttCfg | ttCfg.isSource(e))
|
||||
or
|
||||
e = adjustedSink(sinkNode) and
|
||||
exists(TaintTrackingConfiguration ttCfg | ttCfg.isSink(e))
|
||||
)
|
||||
}
|
||||
|
||||
/** An opaque type used for the nodes of a data-flow path. */
|
||||
class PathNode extends TPathNode {
|
||||
/** Gets a textual representation of this element. */
|
||||
string toString() { none() }
|
||||
|
||||
/**
|
||||
* Holds if this element is at the specified location.
|
||||
* The location spans column `startcolumn` of line `startline` to
|
||||
* column `endcolumn` of line `endline` in file `filepath`.
|
||||
* For more information, see
|
||||
* [Locations](https://codeql.github.com/docs/writing-codeql-queries/providing-locations-in-codeql-queries/).
|
||||
*/
|
||||
predicate hasLocationInfo(
|
||||
string filepath, int startline, int startcolumn, int endline, int endcolumn
|
||||
) {
|
||||
none()
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* INTERNAL: Do not use.
|
||||
*/
|
||||
module Private {
|
||||
/** Gets a predecessor `PathNode` of `pathNode`, if any. */
|
||||
PathNode getAPredecessor(PathNode pathNode) { edges(result, pathNode) }
|
||||
|
||||
/** Gets the element that `pathNode` wraps, if any. */
|
||||
Element getElementFromPathNode(PathNode pathNode) {
|
||||
exists(DataFlow::Node node | node = pathNode.(WrapPathNode).inner().getNode() |
|
||||
result = node.asInstruction().getAst()
|
||||
or
|
||||
result = node.asOperand().getDef().getAst()
|
||||
)
|
||||
or
|
||||
result = pathNode.(EndpointPathNode).inner()
|
||||
}
|
||||
}
|
||||
|
||||
private class WrapPathNode extends PathNode, TWrapPathNode {
|
||||
DataFlow3::PathNode inner() { this = TWrapPathNode(result) }
|
||||
|
||||
override string toString() { result = this.inner().toString() }
|
||||
|
||||
override predicate hasLocationInfo(
|
||||
string filepath, int startline, int startcolumn, int endline, int endcolumn
|
||||
) {
|
||||
this.inner().hasLocationInfo(filepath, startline, startcolumn, endline, endcolumn)
|
||||
}
|
||||
}
|
||||
|
||||
private class EndpointPathNode extends PathNode, TEndpointPathNode {
|
||||
Expr inner() { this = TEndpointPathNode(result) }
|
||||
|
||||
override string toString() { result = this.inner().toString() }
|
||||
|
||||
override predicate hasLocationInfo(
|
||||
string filepath, int startline, int startcolumn, int endline, int endcolumn
|
||||
) {
|
||||
this.inner()
|
||||
.getLocation()
|
||||
.hasLocationInfo(filepath, startline, startcolumn, endline, endcolumn)
|
||||
}
|
||||
}
|
||||
|
||||
/** A PathNode whose `Element` is a source. It may also be a sink. */
|
||||
private class InitialPathNode extends EndpointPathNode {
|
||||
InitialPathNode() { exists(TaintTrackingConfiguration cfg | cfg.isSource(this.inner())) }
|
||||
}
|
||||
|
||||
/** A PathNode whose `Element` is a sink. It may also be a source. */
|
||||
private class FinalPathNode extends EndpointPathNode {
|
||||
FinalPathNode() { exists(TaintTrackingConfiguration cfg | cfg.isSink(this.inner())) }
|
||||
}
|
||||
|
||||
/** Holds if `(a,b)` is an edge in the graph of data flow path explanations. */
|
||||
query predicate edges(PathNode a, PathNode b) {
|
||||
DataFlow3::PathGraph::edges(a.(WrapPathNode).inner(), b.(WrapPathNode).inner())
|
||||
or
|
||||
// To avoid showing trivial-looking steps, we _replace_ the last node instead
|
||||
// of adding an edge out of it.
|
||||
exists(WrapPathNode sinkNode |
|
||||
DataFlow3::PathGraph::edges(a.(WrapPathNode).inner(), sinkNode.inner()) and
|
||||
b.(FinalPathNode).inner() = adjustedSink(sinkNode.inner().getNode())
|
||||
)
|
||||
or
|
||||
// Same for the first node
|
||||
exists(WrapPathNode sourceNode |
|
||||
DataFlow3::PathGraph::edges(sourceNode.inner(), b.(WrapPathNode).inner()) and
|
||||
sourceNode.inner().getNode() = getNodeForExpr(a.(InitialPathNode).inner())
|
||||
)
|
||||
or
|
||||
// Finally, handle the case where the path goes directly from a source to a
|
||||
// sink, meaning that they both need to be translated.
|
||||
exists(WrapPathNode sinkNode, WrapPathNode sourceNode |
|
||||
DataFlow3::PathGraph::edges(sourceNode.inner(), sinkNode.inner()) and
|
||||
sourceNode.inner().getNode() = getNodeForExpr(a.(InitialPathNode).inner()) and
|
||||
b.(FinalPathNode).inner() = adjustedSink(sinkNode.inner().getNode())
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if there is flow from `arg` to `out` across a call that can by summarized by the flow
|
||||
* from `par` to `ret` within it, in the graph of data flow path explanations.
|
||||
*/
|
||||
query predicate subpaths(PathNode arg, PathNode par, PathNode ret, PathNode out) {
|
||||
DataFlow3::PathGraph::subpaths(arg.(WrapPathNode).inner(), par.(WrapPathNode).inner(),
|
||||
ret.(WrapPathNode).inner(), out.(WrapPathNode).inner())
|
||||
or
|
||||
// To avoid showing trivial-looking steps, we _replace_ the last node instead
|
||||
// of adding an edge out of it.
|
||||
exists(WrapPathNode sinkNode |
|
||||
DataFlow3::PathGraph::subpaths(arg.(WrapPathNode).inner(), par.(WrapPathNode).inner(),
|
||||
ret.(WrapPathNode).inner(), sinkNode.inner()) and
|
||||
out.(FinalPathNode).inner() = adjustedSink(sinkNode.inner().getNode())
|
||||
)
|
||||
or
|
||||
// Same for the first node
|
||||
exists(WrapPathNode sourceNode |
|
||||
DataFlow3::PathGraph::subpaths(sourceNode.inner(), par.(WrapPathNode).inner(),
|
||||
ret.(WrapPathNode).inner(), out.(WrapPathNode).inner()) and
|
||||
sourceNode.inner().getNode() = getNodeForExpr(arg.(InitialPathNode).inner())
|
||||
)
|
||||
or
|
||||
// Finally, handle the case where the path goes directly from a source to a
|
||||
// sink, meaning that they both need to be translated.
|
||||
exists(WrapPathNode sinkNode, WrapPathNode sourceNode |
|
||||
DataFlow3::PathGraph::subpaths(sourceNode.inner(), par.(WrapPathNode).inner(),
|
||||
ret.(WrapPathNode).inner(), sinkNode.inner()) and
|
||||
sourceNode.inner().getNode() = getNodeForExpr(arg.(InitialPathNode).inner()) and
|
||||
out.(FinalPathNode).inner() = adjustedSink(sinkNode.inner().getNode())
|
||||
)
|
||||
}
|
||||
|
||||
/** Holds if `n` is a node in the graph of data flow path explanations. */
|
||||
query predicate nodes(PathNode n, string key, string val) {
|
||||
key = "semmle.label" and val = n.toString()
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `tainted` may contain taint from `source`, where `sourceNode` and
|
||||
* `sinkNode` are the corresponding `PathNode`s that can be used in a query
|
||||
* to provide path explanations. Extend `TaintTrackingConfiguration` to use
|
||||
* this predicate.
|
||||
*
|
||||
* A tainted expression is either directly user input, or is computed from
|
||||
* user input in a way that users can probably control the exact output of
|
||||
* the computation.
|
||||
*/
|
||||
predicate taintedWithPath(Expr source, Element tainted, PathNode sourceNode, PathNode sinkNode) {
|
||||
exists(AdjustedConfiguration cfg, DataFlow3::Node flowSource, DataFlow3::Node flowSink |
|
||||
source = sourceNode.(InitialPathNode).inner() and
|
||||
flowSource = getNodeForExpr(source) and
|
||||
cfg.hasFlow(flowSource, flowSink) and
|
||||
tainted = adjustedSink(flowSink) and
|
||||
tainted = sinkNode.(FinalPathNode).inner()
|
||||
)
|
||||
}
|
||||
|
||||
private predicate isGlobalVariablePathNode(WrapPathNode n) {
|
||||
n.inner().getNode().asVariable() instanceof GlobalOrNamespaceVariable
|
||||
}
|
||||
|
||||
private predicate edgesWithoutGlobals(PathNode a, PathNode b) {
|
||||
edges(a, b) and
|
||||
not isGlobalVariablePathNode(a) and
|
||||
not isGlobalVariablePathNode(b)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `tainted` can be reached from a taint source without passing
|
||||
* through a global variable.
|
||||
*/
|
||||
predicate taintedWithoutGlobals(Element tainted) {
|
||||
exists(AdjustedConfiguration cfg, PathNode sourceNode, FinalPathNode sinkNode |
|
||||
cfg.isSource(sourceNode.(WrapPathNode).inner().getNode()) and
|
||||
edgesWithoutGlobals+(sourceNode, sinkNode) and
|
||||
tainted = sinkNode.inner()
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -27,7 +27,7 @@ private import implementations.StdString
|
||||
private import implementations.Swap
|
||||
private import implementations.GetDelim
|
||||
private import implementations.SmartPointer
|
||||
private import implementations.Scanf
|
||||
private import implementations.Sscanf
|
||||
private import implementations.Send
|
||||
private import implementations.Recv
|
||||
private import implementations.Accept
|
||||
|
||||
@@ -15,6 +15,6 @@ private class Fread extends AliasFunction, RemoteFlowSourceFunction {
|
||||
|
||||
override predicate hasRemoteFlowSource(FunctionOutput output, string description) {
|
||||
output.isParameterDeref(0) and
|
||||
description = "string read by " + this.getName()
|
||||
description = "String read by " + this.getName()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -36,6 +36,6 @@ private class GetDelimFunction extends TaintFunction, AliasFunction, SideEffectF
|
||||
|
||||
override predicate hasRemoteFlowSource(FunctionOutput output, string description) {
|
||||
output.isParameterDeref(0) and
|
||||
description = "string read by " + this.getName()
|
||||
description = "String read by " + this.getName()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,19 +1,15 @@
|
||||
/**
|
||||
* Provides an implementation class modeling the POSIX function `getenv` and
|
||||
* various similar functions.
|
||||
* Provides an implementation class modeling the POSIX function `getenv`.
|
||||
*/
|
||||
|
||||
import cpp
|
||||
import semmle.code.cpp.models.interfaces.FlowSource
|
||||
|
||||
/**
|
||||
* The POSIX function `getenv`, the GNU function `secure_getenv`, and the
|
||||
* Windows function `_wgetenv`.
|
||||
* The POSIX function `getenv`.
|
||||
*/
|
||||
class Getenv extends LocalFlowSourceFunction {
|
||||
Getenv() {
|
||||
this.hasGlobalOrStdOrBslName("getenv") or this.hasGlobalName(["secure_getenv", "_wgetenv"])
|
||||
}
|
||||
Getenv() { this.hasGlobalOrStdOrBslName("getenv") }
|
||||
|
||||
override predicate hasLocalFlowSource(FunctionOutput output, string description) {
|
||||
(
|
||||
|
||||
@@ -49,10 +49,10 @@ private class FgetsFunction extends DataFlowFunction, TaintFunction, ArrayFuncti
|
||||
|
||||
override predicate hasRemoteFlowSource(FunctionOutput output, string description) {
|
||||
output.isParameterDeref(0) and
|
||||
description = "string read by " + this.getName()
|
||||
description = "String read by " + this.getName()
|
||||
or
|
||||
output.isReturnValue() and
|
||||
description = "string read by " + this.getName()
|
||||
description = "String read by " + this.getName()
|
||||
}
|
||||
|
||||
override predicate hasArrayWithVariableSize(int bufParam, int countParam) {
|
||||
@@ -98,10 +98,10 @@ private class GetsFunction extends DataFlowFunction, ArrayFunction, AliasFunctio
|
||||
|
||||
override predicate hasLocalFlowSource(FunctionOutput output, string description) {
|
||||
output.isParameterDeref(0) and
|
||||
description = "string read by " + this.getName()
|
||||
description = "String read by " + this.getName()
|
||||
or
|
||||
output.isReturnValue() and
|
||||
description = "string read by " + this.getName()
|
||||
description = "String read by " + this.getName()
|
||||
}
|
||||
|
||||
override predicate hasArrayWithUnknownSize(int bufParam) { bufParam = 0 }
|
||||
|
||||
@@ -1,10 +1,9 @@
|
||||
import semmle.code.cpp.models.interfaces.Taint
|
||||
import semmle.code.cpp.models.interfaces.Alias
|
||||
import semmle.code.cpp.models.interfaces.ArrayFunction
|
||||
import semmle.code.cpp.models.interfaces.FlowSource
|
||||
|
||||
private class InetNtoa extends TaintFunction {
|
||||
InetNtoa() { this.hasGlobalName("inet_ntoa") }
|
||||
InetNtoa() { hasGlobalName("inet_ntoa") }
|
||||
|
||||
override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) {
|
||||
input.isParameter(0) and
|
||||
@@ -13,7 +12,7 @@ private class InetNtoa extends TaintFunction {
|
||||
}
|
||||
|
||||
private class InetAton extends TaintFunction, ArrayFunction {
|
||||
InetAton() { this.hasGlobalName("inet_aton") }
|
||||
InetAton() { hasGlobalName("inet_aton") }
|
||||
|
||||
override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) {
|
||||
input.isParameterDeref(0) and
|
||||
@@ -33,7 +32,7 @@ private class InetAton extends TaintFunction, ArrayFunction {
|
||||
}
|
||||
|
||||
private class InetAddr extends TaintFunction, ArrayFunction, AliasFunction {
|
||||
InetAddr() { this.hasGlobalName("inet_addr") }
|
||||
InetAddr() { hasGlobalName("inet_addr") }
|
||||
|
||||
override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) {
|
||||
input.isParameterDeref(0) and
|
||||
@@ -52,7 +51,7 @@ private class InetAddr extends TaintFunction, ArrayFunction, AliasFunction {
|
||||
}
|
||||
|
||||
private class InetNetwork extends TaintFunction, ArrayFunction {
|
||||
InetNetwork() { this.hasGlobalName("inet_network") }
|
||||
InetNetwork() { hasGlobalName("inet_network") }
|
||||
|
||||
override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) {
|
||||
input.isParameterDeref(0) and
|
||||
@@ -65,7 +64,7 @@ private class InetNetwork extends TaintFunction, ArrayFunction {
|
||||
}
|
||||
|
||||
private class InetMakeaddr extends TaintFunction {
|
||||
InetMakeaddr() { this.hasGlobalName("inet_makeaddr") }
|
||||
InetMakeaddr() { hasGlobalName("inet_makeaddr") }
|
||||
|
||||
override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) {
|
||||
(
|
||||
@@ -77,7 +76,7 @@ private class InetMakeaddr extends TaintFunction {
|
||||
}
|
||||
|
||||
private class InetLnaof extends TaintFunction {
|
||||
InetLnaof() { this.hasGlobalName("inet_lnaof") }
|
||||
InetLnaof() { hasGlobalName("inet_lnaof") }
|
||||
|
||||
override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) {
|
||||
input.isParameter(0) and
|
||||
@@ -86,7 +85,7 @@ private class InetLnaof extends TaintFunction {
|
||||
}
|
||||
|
||||
private class InetNetof extends TaintFunction {
|
||||
InetNetof() { this.hasGlobalName("inet_netof") }
|
||||
InetNetof() { hasGlobalName("inet_netof") }
|
||||
|
||||
override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) {
|
||||
input.isParameter(0) and
|
||||
@@ -95,7 +94,7 @@ private class InetNetof extends TaintFunction {
|
||||
}
|
||||
|
||||
private class InetPton extends TaintFunction, ArrayFunction {
|
||||
InetPton() { this.hasGlobalName("inet_pton") }
|
||||
InetPton() { hasGlobalName("inet_pton") }
|
||||
|
||||
override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) {
|
||||
(
|
||||
@@ -115,7 +114,7 @@ private class InetPton extends TaintFunction, ArrayFunction {
|
||||
}
|
||||
|
||||
private class Gethostbyname extends TaintFunction, ArrayFunction {
|
||||
Gethostbyname() { this.hasGlobalName("gethostbyname") }
|
||||
Gethostbyname() { hasGlobalName("gethostbyname") }
|
||||
|
||||
override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) {
|
||||
input.isParameterDeref(0) and
|
||||
@@ -128,7 +127,7 @@ private class Gethostbyname extends TaintFunction, ArrayFunction {
|
||||
}
|
||||
|
||||
private class Gethostbyaddr extends TaintFunction, ArrayFunction {
|
||||
Gethostbyaddr() { this.hasGlobalName("gethostbyaddr") }
|
||||
Gethostbyaddr() { hasGlobalName("gethostbyaddr") }
|
||||
|
||||
override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) {
|
||||
(
|
||||
@@ -143,21 +142,3 @@ private class Gethostbyaddr extends TaintFunction, ArrayFunction {
|
||||
|
||||
override predicate hasArrayWithNullTerminator(int bufParam) { bufParam = 0 }
|
||||
}
|
||||
|
||||
private class Getaddrinfo extends TaintFunction, ArrayFunction, RemoteFlowSourceFunction {
|
||||
Getaddrinfo() { this.hasGlobalName("getaddrinfo") }
|
||||
|
||||
override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) {
|
||||
input.isParameterDeref([0 .. 2]) and
|
||||
output.isParameterDeref(3)
|
||||
}
|
||||
|
||||
override predicate hasArrayInput(int bufParam) { bufParam in [0, 1] }
|
||||
|
||||
override predicate hasArrayWithNullTerminator(int bufParam) { bufParam in [0, 1] }
|
||||
|
||||
override predicate hasRemoteFlowSource(FunctionOutput output, string description) {
|
||||
output.isParameterDeref(3) and
|
||||
description = "address returned by " + this.getName()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -83,7 +83,7 @@ private class Recv extends AliasFunction, ArrayFunction, SideEffectFunction,
|
||||
or
|
||||
this.hasGlobalName("recvfrom") and output.isParameterDeref([4, 5])
|
||||
) and
|
||||
description = "buffer read by " + this.getName()
|
||||
description = "Buffer read by " + this.getName()
|
||||
}
|
||||
|
||||
override predicate hasSocketInput(FunctionInput input) { input.isParameter(0) }
|
||||
|
||||
@@ -58,7 +58,7 @@ private class Send extends AliasFunction, ArrayFunction, SideEffectFunction, Rem
|
||||
override ParameterIndex getParameterSizeIndex(ParameterIndex i) { i = 1 and result = 2 }
|
||||
|
||||
override predicate hasRemoteFlowSink(FunctionInput input, string description) {
|
||||
input.isParameterDeref(1) and description = "buffer sent by " + this.getName()
|
||||
input.isParameterDeref(1) and description = "Buffer sent by " + this.getName()
|
||||
}
|
||||
|
||||
override predicate hasSocketInput(FunctionInput input) { input.isParameter(0) }
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/**
|
||||
* Provides implementation classes modeling the `scanf` family of functions.
|
||||
* See `semmle.code.cpp.models.Models` for usage information.
|
||||
* Provides implementation classes modeling `sscanf`, `fscanf` and various similar
|
||||
* functions. See `semmle.code.cpp.models.Models` for usage information.
|
||||
*/
|
||||
|
||||
import semmle.code.cpp.Function
|
||||
@@ -9,15 +9,18 @@ import semmle.code.cpp.models.interfaces.ArrayFunction
|
||||
import semmle.code.cpp.models.interfaces.Taint
|
||||
import semmle.code.cpp.models.interfaces.Alias
|
||||
import semmle.code.cpp.models.interfaces.SideEffect
|
||||
import semmle.code.cpp.models.interfaces.FlowSource
|
||||
|
||||
/**
|
||||
* The `scanf` family of functions.
|
||||
* The standard function `sscanf`, `fscanf` and its assorted variants
|
||||
*/
|
||||
abstract private class ScanfFunctionModel extends ArrayFunction, TaintFunction, AliasFunction,
|
||||
SideEffectFunction {
|
||||
private class SscanfModel extends ArrayFunction, TaintFunction, AliasFunction, SideEffectFunction {
|
||||
SscanfModel() { this instanceof Sscanf or this instanceof Fscanf or this instanceof Snscanf }
|
||||
|
||||
override predicate hasArrayWithNullTerminator(int bufParam) {
|
||||
bufParam = this.(ScanfFunction).getFormatParameterIndex()
|
||||
or
|
||||
not this instanceof Fscanf and
|
||||
bufParam = this.(ScanfFunction).getInputParameterIndex()
|
||||
}
|
||||
|
||||
override predicate hasArrayInput(int bufParam) { this.hasArrayWithNullTerminator(bufParam) }
|
||||
@@ -33,7 +36,7 @@ abstract private class ScanfFunctionModel extends ArrayFunction, TaintFunction,
|
||||
)
|
||||
}
|
||||
|
||||
int getArgsStartPosition() { result = this.getNumberOfParameters() }
|
||||
private int getArgsStartPosition() { result = this.getNumberOfParameters() }
|
||||
|
||||
override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) {
|
||||
input.isParameterDeref(this.(ScanfFunction).getInputParameterIndex()) and
|
||||
@@ -67,36 +70,3 @@ abstract private class ScanfFunctionModel extends ArrayFunction, TaintFunction,
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The standard function `scanf` and its assorted variants
|
||||
*/
|
||||
private class ScanfModel extends ScanfFunctionModel, LocalFlowSourceFunction instanceof Scanf {
|
||||
override predicate hasLocalFlowSource(FunctionOutput output, string description) {
|
||||
output.isParameterDeref(any(int i | i >= this.getArgsStartPosition())) and
|
||||
description = "value read by " + this.getName()
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The standard function `fscanf` and its assorted variants
|
||||
*/
|
||||
private class FscanfModel extends ScanfFunctionModel, RemoteFlowSourceFunction instanceof Fscanf {
|
||||
override predicate hasRemoteFlowSource(FunctionOutput output, string description) {
|
||||
output.isParameterDeref(any(int i | i >= this.getArgsStartPosition())) and
|
||||
description = "value read by " + this.getName()
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The standard function `sscanf` and its assorted variants
|
||||
*/
|
||||
private class SscanfModel extends ScanfFunctionModel {
|
||||
SscanfModel() { this instanceof Sscanf or this instanceof Snscanf }
|
||||
|
||||
override predicate hasArrayWithNullTerminator(int bufParam) {
|
||||
super.hasArrayWithNullTerminator(bufParam)
|
||||
or
|
||||
bufParam = this.(ScanfFunction).getInputParameterIndex()
|
||||
}
|
||||
}
|
||||
@@ -15,24 +15,6 @@ private class StdBasicString extends ClassTemplateInstantiation {
|
||||
StdBasicString() { this.hasQualifiedName(["std", "bsl"], "basic_string") }
|
||||
}
|
||||
|
||||
/**
|
||||
* The `std::basic_string::iterator` declaration.
|
||||
*
|
||||
* Intuitively, this class shouldn't be necessary as it's already captured
|
||||
* by the `StdIterator` class. However, this class ensures that the typedef inside the
|
||||
* body of the `std::string` class is also seen as an iterator.
|
||||
*
|
||||
* Eventually, we should be consistent about which of the following should be recognized as iterators:
|
||||
* 1. The typedef type.
|
||||
* 2. The template class of the resolved type.
|
||||
* 3. Any instantiation of the resolved type.
|
||||
*/
|
||||
private class StdBasicStringIterator extends Iterator, Type {
|
||||
StdBasicStringIterator() {
|
||||
this.getEnclosingElement() instanceof StdBasicString and this.hasName("iterator")
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A `std::string` function for which taint should be propagated.
|
||||
*/
|
||||
|
||||
@@ -89,9 +89,9 @@ private class LocalParameterSource extends LocalFlowSource {
|
||||
|
||||
private class ArgvSource extends LocalFlowSource {
|
||||
ArgvSource() {
|
||||
exists(Function main, Parameter argv |
|
||||
main.hasGlobalName("main") and
|
||||
main.getParameter(1) = argv and
|
||||
exists(Parameter argv |
|
||||
argv.hasName("argv") and
|
||||
argv.getFunction().hasGlobalName("main") and
|
||||
this.asExpr() = argv.getAnAccess()
|
||||
)
|
||||
}
|
||||
|
||||
@@ -121,9 +121,7 @@ private predicate moveToDependingOnSide(Expr src, Expr dest) {
|
||||
* (this is done to avoid false positives). Because of this we need to track if the tainted element came from an argument
|
||||
* or not, and for that we use destFromArg
|
||||
*/
|
||||
deprecated private predicate betweenFunctionsValueMoveTo(
|
||||
Element src, Element dest, boolean destFromArg
|
||||
) {
|
||||
private predicate betweenFunctionsValueMoveTo(Element src, Element dest, boolean destFromArg) {
|
||||
not unreachable(src) and
|
||||
not unreachable(dest) and
|
||||
(
|
||||
@@ -164,13 +162,13 @@ deprecated private predicate betweenFunctionsValueMoveTo(
|
||||
// predicate folding for proper join-order
|
||||
// bad magic: pushes down predicate that ruins join-order
|
||||
pragma[nomagic]
|
||||
deprecated private predicate resolveCallWithParam(Call call, Function called, int i, Parameter p) {
|
||||
private predicate resolveCallWithParam(Call call, Function called, int i, Parameter p) {
|
||||
called = resolveCall(call) and
|
||||
p = called.getParameter(i)
|
||||
}
|
||||
|
||||
/** A variable for which flow through is allowed. */
|
||||
deprecated library class FlowVariable extends Variable {
|
||||
library class FlowVariable extends Variable {
|
||||
FlowVariable() {
|
||||
(
|
||||
this instanceof LocalScopeVariable or
|
||||
@@ -181,11 +179,11 @@ deprecated library class FlowVariable extends Variable {
|
||||
}
|
||||
|
||||
/** A local scope variable for which flow through is allowed. */
|
||||
deprecated library class FlowLocalScopeVariable extends Variable {
|
||||
library class FlowLocalScopeVariable extends Variable {
|
||||
FlowLocalScopeVariable() { this instanceof LocalScopeVariable }
|
||||
}
|
||||
|
||||
deprecated private predicate insideFunctionValueMoveTo(Element src, Element dest) {
|
||||
private predicate insideFunctionValueMoveTo(Element src, Element dest) {
|
||||
not unreachable(src) and
|
||||
not unreachable(dest) and
|
||||
(
|
||||
@@ -326,7 +324,7 @@ private predicate unionAccess(Variable v, Field f, FieldAccess a) {
|
||||
a.getQualifier() = v.getAnAccess()
|
||||
}
|
||||
|
||||
deprecated GlobalOrNamespaceVariable globalVarFromId(string id) {
|
||||
GlobalOrNamespaceVariable globalVarFromId(string id) {
|
||||
if result instanceof NamespaceVariable
|
||||
then id = result.getNamespace() + "::" + result.getName()
|
||||
else id = result.getName()
|
||||
@@ -355,7 +353,7 @@ private predicate hasUpperBoundsCheck(Variable var) {
|
||||
}
|
||||
|
||||
cached
|
||||
deprecated private predicate taintedWithArgsAndGlobalVars(
|
||||
private predicate taintedWithArgsAndGlobalVars(
|
||||
Element src, Element dest, boolean destFromArg, string globalVar
|
||||
) {
|
||||
isUserInput(src, _) and
|
||||
@@ -397,7 +395,7 @@ deprecated private predicate taintedWithArgsAndGlobalVars(
|
||||
* This doesn't include data flow through global variables.
|
||||
* If you need that you must call taintedIncludingGlobalVars.
|
||||
*/
|
||||
deprecated predicate tainted(Expr source, Element tainted) {
|
||||
predicate tainted(Expr source, Element tainted) {
|
||||
taintedWithArgsAndGlobalVars(source, tainted, _, "")
|
||||
}
|
||||
|
||||
@@ -412,7 +410,7 @@ deprecated predicate tainted(Expr source, Element tainted) {
|
||||
* The parameter `globalVar` is the name of the last global variable used to move the
|
||||
* value from source to tainted.
|
||||
*/
|
||||
deprecated predicate taintedIncludingGlobalVars(Expr source, Element tainted, string globalVar) {
|
||||
predicate taintedIncludingGlobalVars(Expr source, Element tainted, string globalVar) {
|
||||
taintedWithArgsAndGlobalVars(source, tainted, _, globalVar)
|
||||
}
|
||||
|
||||
@@ -543,14 +541,14 @@ private predicate returnArgument(Function f, int sourceArg) {
|
||||
* targets a virtual method, simple data flow analysis is performed
|
||||
* in order to identify target(s).
|
||||
*/
|
||||
deprecated Function resolveCall(Call call) {
|
||||
Function resolveCall(Call call) {
|
||||
result = call.getTarget()
|
||||
or
|
||||
result = call.(DataSensitiveCallExpr).resolve()
|
||||
}
|
||||
|
||||
/** A data sensitive call expression. */
|
||||
abstract deprecated library class DataSensitiveCallExpr extends Expr {
|
||||
abstract library class DataSensitiveCallExpr extends Expr {
|
||||
DataSensitiveCallExpr() { not unreachable(this) }
|
||||
|
||||
abstract Expr getSrc();
|
||||
@@ -581,7 +579,7 @@ abstract deprecated library class DataSensitiveCallExpr extends Expr {
|
||||
}
|
||||
|
||||
/** Call through a function pointer. */
|
||||
deprecated library class DataSensitiveExprCall extends DataSensitiveCallExpr, ExprCall {
|
||||
library class DataSensitiveExprCall extends DataSensitiveCallExpr, ExprCall {
|
||||
override Expr getSrc() { result = getExpr() }
|
||||
|
||||
override Function resolve() {
|
||||
@@ -590,8 +588,7 @@ deprecated library class DataSensitiveExprCall extends DataSensitiveCallExpr, Ex
|
||||
}
|
||||
|
||||
/** Call to a virtual function. */
|
||||
deprecated library class DataSensitiveOverriddenFunctionCall extends DataSensitiveCallExpr,
|
||||
FunctionCall {
|
||||
library class DataSensitiveOverriddenFunctionCall extends DataSensitiveCallExpr, FunctionCall {
|
||||
DataSensitiveOverriddenFunctionCall() {
|
||||
exists(getTarget().(VirtualFunction).getAnOverridingFunction())
|
||||
}
|
||||
|
||||
@@ -1,7 +1,3 @@
|
||||
## 0.4.5
|
||||
|
||||
No user-facing changes.
|
||||
|
||||
## 0.4.4
|
||||
|
||||
No user-facing changes.
|
||||
|
||||
@@ -66,7 +66,9 @@ class ElseDirective extends Directive {
|
||||
override predicate mismatched() { depth() < 1 }
|
||||
}
|
||||
|
||||
class EndifDirective extends Directive instanceof PreprocessorEndif {
|
||||
class EndifDirective extends Directive {
|
||||
EndifDirective() { this instanceof PreprocessorEndif }
|
||||
|
||||
override int depthChange() { result = -1 }
|
||||
|
||||
override predicate mismatched() { depth() < 0 }
|
||||
|
||||
@@ -16,7 +16,7 @@
|
||||
|
||||
import cpp
|
||||
import semmle.code.cpp.security.FunctionWithWrappers
|
||||
import semmle.code.cpp.security.FlowSources
|
||||
import semmle.code.cpp.security.Security
|
||||
import semmle.code.cpp.ir.IR
|
||||
import semmle.code.cpp.ir.dataflow.TaintTracking
|
||||
import DataFlow::PathGraph
|
||||
@@ -47,6 +47,12 @@ class FileFunction extends FunctionWithWrappers {
|
||||
override predicate interestingArg(int arg) { arg = 0 }
|
||||
}
|
||||
|
||||
Expr asSourceExpr(DataFlow::Node node) {
|
||||
result = node.asConvertedExpr()
|
||||
or
|
||||
result = node.asDefiningArgument()
|
||||
}
|
||||
|
||||
Expr asSinkExpr(DataFlow::Node node) {
|
||||
result =
|
||||
node.asOperand()
|
||||
@@ -83,7 +89,7 @@ predicate hasUpperBoundsCheck(Variable var) {
|
||||
class TaintedPathConfiguration extends TaintTracking::Configuration {
|
||||
TaintedPathConfiguration() { this = "TaintedPathConfiguration" }
|
||||
|
||||
override predicate isSource(DataFlow::Node node) { node instanceof FlowSource }
|
||||
override predicate isSource(DataFlow::Node node) { isUserInput(asSourceExpr(node), _) }
|
||||
|
||||
override predicate isSink(DataFlow::Node node) {
|
||||
exists(FileFunction fileFunction |
|
||||
@@ -102,16 +108,31 @@ class TaintedPathConfiguration extends TaintTracking::Configuration {
|
||||
hasUpperBoundsCheck(checkedVar)
|
||||
)
|
||||
}
|
||||
|
||||
predicate hasFilteredFlowPath(DataFlow::PathNode source, DataFlow::PathNode sink) {
|
||||
this.hasFlowPath(source, sink) and
|
||||
// The use of `isUserInput` in `isSink` in combination with `asSourceExpr` causes
|
||||
// duplicate results. Filter these duplicates. The proper solution is to switch to
|
||||
// using `LocalFlowSource` and `RemoteFlowSource`, but this currently only supports
|
||||
// a subset of the cases supported by `isUserInput`.
|
||||
not exists(DataFlow::PathNode source2 |
|
||||
this.hasFlowPath(source2, sink) and
|
||||
asSourceExpr(source.getNode()) = asSourceExpr(source2.getNode())
|
||||
|
|
||||
not exists(source.getNode().asConvertedExpr()) and exists(source2.getNode().asConvertedExpr())
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
from
|
||||
FileFunction fileFunction, Expr taintedArg, FlowSource taintSource, TaintedPathConfiguration cfg,
|
||||
DataFlow::PathNode sourceNode, DataFlow::PathNode sinkNode, string callChain
|
||||
FileFunction fileFunction, Expr taintedArg, Expr taintSource, TaintedPathConfiguration cfg,
|
||||
DataFlow::PathNode sourceNode, DataFlow::PathNode sinkNode, string taintCause, string callChain
|
||||
where
|
||||
taintedArg = asSinkExpr(sinkNode.getNode()) and
|
||||
fileFunction.outermostWrapperFunctionCall(taintedArg, callChain) and
|
||||
cfg.hasFlowPath(sourceNode, sinkNode) and
|
||||
taintSource = sourceNode.getNode()
|
||||
cfg.hasFilteredFlowPath(sourceNode, sinkNode) and
|
||||
taintSource = asSourceExpr(sourceNode.getNode()) and
|
||||
isUserInput(taintSource, taintCause)
|
||||
select taintedArg, sourceNode, sinkNode,
|
||||
"This argument to a file access function is derived from $@ and then passed to " + callChain + ".",
|
||||
taintSource, "user input (" + taintSource.getSourceType() + ")"
|
||||
taintSource, "user input (" + taintCause + ")"
|
||||
|
||||
@@ -13,7 +13,7 @@
|
||||
|
||||
import cpp
|
||||
import semmle.code.cpp.commons.Environment
|
||||
import semmle.code.cpp.ir.dataflow.internal.DefaultTaintTrackingImpl
|
||||
import semmle.code.cpp.security.TaintTracking
|
||||
import TaintedWithPath
|
||||
|
||||
/** A call that prints its arguments to `stdout`. */
|
||||
|
||||
@@ -15,7 +15,7 @@
|
||||
import cpp
|
||||
import semmle.code.cpp.security.Security
|
||||
import semmle.code.cpp.security.FunctionWithWrappers
|
||||
import semmle.code.cpp.ir.dataflow.internal.DefaultTaintTrackingImpl
|
||||
import semmle.code.cpp.security.TaintTracking
|
||||
import TaintedWithPath
|
||||
|
||||
class SqlLikeFunction extends FunctionWithWrappers {
|
||||
|
||||
@@ -14,7 +14,7 @@
|
||||
|
||||
import cpp
|
||||
import semmle.code.cpp.security.Security
|
||||
import semmle.code.cpp.ir.dataflow.internal.DefaultTaintTrackingImpl
|
||||
import semmle.code.cpp.security.TaintTracking
|
||||
import TaintedWithPath
|
||||
|
||||
predicate isProcessOperationExplanation(Expr arg, string processOperation) {
|
||||
|
||||
@@ -16,7 +16,7 @@
|
||||
|
||||
import semmle.code.cpp.security.BufferWrite
|
||||
import semmle.code.cpp.security.Security
|
||||
import semmle.code.cpp.ir.dataflow.internal.DefaultTaintTrackingImpl
|
||||
import semmle.code.cpp.security.TaintTracking
|
||||
import TaintedWithPath
|
||||
|
||||
/*
|
||||
|
||||
@@ -116,6 +116,10 @@ class ImproperArrayIndexValidationConfig extends TaintTracking::Configuration {
|
||||
}
|
||||
}
|
||||
|
||||
/** Gets `str` where the first letter has been lowercased. */
|
||||
bindingset[str]
|
||||
string lowerFirst(string str) { result = str.prefix(1).toLowerCase() + str.suffix(1) }
|
||||
|
||||
from
|
||||
ImproperArrayIndexValidationConfig conf, DataFlow::PathNode source, DataFlow::PathNode sink,
|
||||
string sourceType
|
||||
@@ -124,4 +128,4 @@ where
|
||||
isFlowSource(source.getNode(), sourceType)
|
||||
select sink.getNode(), source, sink,
|
||||
"An array indexing expression depends on $@ that might be outside the bounds of the array.",
|
||||
source.getNode(), sourceType
|
||||
source.getNode(), lowerFirst(sourceType)
|
||||
|
||||
@@ -16,7 +16,7 @@
|
||||
import cpp
|
||||
import semmle.code.cpp.security.Security
|
||||
import semmle.code.cpp.security.FunctionWithWrappers
|
||||
import semmle.code.cpp.ir.dataflow.internal.DefaultTaintTrackingImpl
|
||||
import semmle.code.cpp.security.TaintTracking
|
||||
import TaintedWithPath
|
||||
|
||||
class Configuration extends TaintTrackingConfiguration {
|
||||
|
||||
@@ -16,7 +16,7 @@
|
||||
import cpp
|
||||
import semmle.code.cpp.security.FunctionWithWrappers
|
||||
import semmle.code.cpp.security.Security
|
||||
import semmle.code.cpp.ir.dataflow.internal.DefaultTaintTrackingImpl
|
||||
import semmle.code.cpp.security.TaintTracking
|
||||
import TaintedWithPath
|
||||
|
||||
class Configuration extends TaintTrackingConfiguration {
|
||||
|
||||
@@ -12,7 +12,7 @@
|
||||
|
||||
import cpp
|
||||
import semmle.code.cpp.commons.NullTermination
|
||||
import semmle.code.cpp.ir.dataflow.internal.DefaultTaintTrackingImpl
|
||||
import semmle.code.cpp.security.TaintTracking
|
||||
|
||||
/** A user-controlled expression that may not be null terminated. */
|
||||
class TaintSource extends VariableAccess {
|
||||
|
||||
@@ -15,7 +15,7 @@
|
||||
import cpp
|
||||
import semmle.code.cpp.security.Overflow
|
||||
import semmle.code.cpp.security.Security
|
||||
import semmle.code.cpp.ir.dataflow.internal.DefaultTaintTrackingImpl
|
||||
import semmle.code.cpp.security.TaintTracking
|
||||
import TaintedWithPath
|
||||
import Bounded
|
||||
|
||||
|
||||
@@ -17,7 +17,7 @@
|
||||
import cpp
|
||||
import semmle.code.cpp.security.Overflow
|
||||
import semmle.code.cpp.security.Security
|
||||
import semmle.code.cpp.ir.dataflow.internal.DefaultTaintTrackingImpl
|
||||
import semmle.code.cpp.security.TaintTracking
|
||||
|
||||
predicate isMaxValue(Expr mie) {
|
||||
exists(MacroInvocation mi |
|
||||
|
||||
@@ -15,7 +15,7 @@
|
||||
|
||||
import cpp
|
||||
import semmle.code.cpp.rangeanalysis.SimpleRangeAnalysis
|
||||
import semmle.code.cpp.ir.dataflow.internal.DefaultTaintTrackingImpl
|
||||
import semmle.code.cpp.security.TaintTracking
|
||||
|
||||
/** Holds if `expr` might overflow. */
|
||||
predicate outOfBoundsExpr(Expr expr, string kind) {
|
||||
|
||||
@@ -12,7 +12,7 @@
|
||||
* external/cwe/cwe-290
|
||||
*/
|
||||
|
||||
import semmle.code.cpp.ir.dataflow.internal.DefaultTaintTrackingImpl
|
||||
import semmle.code.cpp.security.TaintTracking
|
||||
import TaintedWithPath
|
||||
|
||||
predicate hardCodedAddressOrIP(StringLiteral txt) {
|
||||
|
||||
@@ -19,25 +19,7 @@ import semmle.code.cpp.ir.dataflow.TaintTracking
|
||||
import DataFlow::PathGraph
|
||||
|
||||
/**
|
||||
* A buffer write into a sensitive expression.
|
||||
*/
|
||||
class SensitiveBufferWrite extends Expr instanceof BufferWrite::BufferWrite {
|
||||
SensitiveBufferWrite() { super.getDest() instanceof SensitiveExpr }
|
||||
|
||||
/**
|
||||
* Gets a data source of this operation.
|
||||
*/
|
||||
Expr getASource() { result = super.getASource() }
|
||||
|
||||
/**
|
||||
* Gets the destination buffer of this operation.
|
||||
*/
|
||||
Expr getDest() { result = super.getDest() }
|
||||
}
|
||||
|
||||
/**
|
||||
* A taint flow configuration for flow from user input to a buffer write
|
||||
* into a sensitive expression.
|
||||
* A taint flow configuration for flow from user input to a buffer write.
|
||||
*/
|
||||
class ToBufferConfiguration extends TaintTracking::Configuration {
|
||||
ToBufferConfiguration() { this = "ToBufferConfiguration" }
|
||||
@@ -49,17 +31,18 @@ class ToBufferConfiguration extends TaintTracking::Configuration {
|
||||
}
|
||||
|
||||
override predicate isSink(DataFlow::Node sink) {
|
||||
exists(SensitiveBufferWrite w | w.getASource() = sink.asExpr())
|
||||
exists(BufferWrite::BufferWrite w | w.getASource() = sink.asExpr())
|
||||
}
|
||||
}
|
||||
|
||||
from
|
||||
ToBufferConfiguration config, SensitiveBufferWrite w, DataFlow::PathNode sourceNode,
|
||||
DataFlow::PathNode sinkNode, FlowSource source
|
||||
ToBufferConfiguration config, BufferWrite::BufferWrite w, DataFlow::PathNode sourceNode,
|
||||
DataFlow::PathNode sinkNode, FlowSource source, SensitiveExpr dest
|
||||
where
|
||||
config.hasFlowPath(sourceNode, sinkNode) and
|
||||
sourceNode.getNode() = source and
|
||||
w.getASource() = sinkNode.getNode().asExpr()
|
||||
w.getASource() = sinkNode.getNode().asExpr() and
|
||||
dest = w.getDest()
|
||||
select w, sourceNode, sinkNode,
|
||||
"This write into buffer '" + w.getDest().toString() + "' may contain unencrypted data from $@.",
|
||||
source, "user input (" + source.getSourceType() + ")"
|
||||
"This write into buffer '" + dest.toString() + "' may contain unencrypted data from $@.", source,
|
||||
"user input (" + source.getSourceType() + ")"
|
||||
|
||||
@@ -12,7 +12,7 @@
|
||||
* external/cwe/cwe-807
|
||||
*/
|
||||
|
||||
import semmle.code.cpp.ir.dataflow.internal.DefaultTaintTrackingImpl
|
||||
import semmle.code.cpp.security.TaintTracking
|
||||
import TaintedWithPath
|
||||
|
||||
predicate sensitiveCondition(Expr condition, Expr raise) {
|
||||
|
||||
@@ -1,3 +0,0 @@
|
||||
## 0.4.5
|
||||
|
||||
No user-facing changes.
|
||||
@@ -1,2 +1,2 @@
|
||||
---
|
||||
lastReleaseVersion: 0.4.5
|
||||
lastReleaseVersion: 0.4.4
|
||||
|
||||
@@ -9,7 +9,6 @@
|
||||
* @tags reliability
|
||||
* security
|
||||
* external/cwe/cwe-476
|
||||
* experimental
|
||||
*/
|
||||
|
||||
import cpp
|
||||
|
||||
@@ -10,7 +10,6 @@
|
||||
* @tags correctness
|
||||
* security
|
||||
* external/cwe/cwe-20
|
||||
* experimental
|
||||
*/
|
||||
|
||||
import cpp
|
||||
|
||||
@@ -12,7 +12,6 @@
|
||||
* @security-severity 7.5
|
||||
* @tags security
|
||||
* external/cwe/cwe-020
|
||||
* experimental
|
||||
*/
|
||||
|
||||
import cpp
|
||||
|
||||
@@ -9,7 +9,6 @@
|
||||
* maintainability
|
||||
* security
|
||||
* external/cwe/cwe-1041
|
||||
* experimental
|
||||
*/
|
||||
|
||||
import cpp
|
||||
|
||||
@@ -10,7 +10,6 @@
|
||||
* @tags correctness
|
||||
* security
|
||||
* external/cwe/cwe-1126
|
||||
* experimental
|
||||
*/
|
||||
|
||||
import cpp
|
||||
|
||||
@@ -7,7 +7,6 @@
|
||||
* @tags reliability
|
||||
* security
|
||||
* external/cwe/cwe-120
|
||||
* experimental
|
||||
*/
|
||||
|
||||
import cpp
|
||||
|
||||
@@ -8,7 +8,6 @@
|
||||
* @tags correctness
|
||||
* security
|
||||
* external/cwe/cwe-125
|
||||
* experimental
|
||||
*/
|
||||
|
||||
import cpp
|
||||
|
||||
@@ -8,7 +8,6 @@
|
||||
* correctness
|
||||
* external/cwe/cwe-190
|
||||
* external/cwe/cwe-128
|
||||
* experimental
|
||||
* @id cpp/multiplication-overflow-in-alloc
|
||||
*/
|
||||
|
||||
|
||||
@@ -8,7 +8,6 @@
|
||||
* @tags correctness
|
||||
* security
|
||||
* external/cwe/cwe-190
|
||||
* experimental
|
||||
*/
|
||||
|
||||
import cpp
|
||||
|
||||
@@ -10,7 +10,6 @@
|
||||
* security
|
||||
* external/cwe/cwe-200
|
||||
* external/cwe/cwe-264
|
||||
* experimental
|
||||
*/
|
||||
|
||||
import cpp
|
||||
|
||||
@@ -9,7 +9,6 @@
|
||||
* security
|
||||
* external/cwe/cwe-243
|
||||
* external/cwe/cwe-252
|
||||
* experimental
|
||||
*/
|
||||
|
||||
import cpp
|
||||
|
||||
@@ -13,7 +13,6 @@
|
||||
* external/cwe/cwe-200
|
||||
* external/cwe/cwe-560
|
||||
* external/cwe/cwe-687
|
||||
* experimental
|
||||
*/
|
||||
|
||||
import cpp
|
||||
|
||||
@@ -9,7 +9,6 @@
|
||||
* @id cpp/drop-linux-privileges-outoforder
|
||||
* @tags security
|
||||
* external/cwe/cwe-273
|
||||
* experimental
|
||||
* @precision medium
|
||||
*/
|
||||
|
||||
|
||||
@@ -6,7 +6,6 @@
|
||||
* @id cpp/pam-auth-bypass
|
||||
* @tags security
|
||||
* external/cwe/cwe-285
|
||||
* experimental
|
||||
*/
|
||||
|
||||
import cpp
|
||||
|
||||
@@ -7,7 +7,6 @@
|
||||
* @id cpp/private-cleartext-write
|
||||
* @tags security
|
||||
* external/cwe/cwe-359
|
||||
* experimental
|
||||
*/
|
||||
|
||||
import cpp
|
||||
|
||||
@@ -12,7 +12,6 @@
|
||||
* @security-severity 7.5
|
||||
* @tags security
|
||||
* external/cwe/cwe-362
|
||||
* experimental
|
||||
*/
|
||||
|
||||
import cpp
|
||||
|
||||
@@ -8,7 +8,6 @@
|
||||
* @tags correctness
|
||||
* security
|
||||
* external/cwe/cwe-377
|
||||
* experimental
|
||||
*/
|
||||
|
||||
import cpp
|
||||
|
||||
@@ -9,7 +9,6 @@
|
||||
* @tags correctness
|
||||
* security
|
||||
* external/cwe/cwe-401
|
||||
* experimental
|
||||
*/
|
||||
|
||||
import cpp
|
||||
|
||||
@@ -7,7 +7,6 @@
|
||||
* @precision medium
|
||||
* @tags security
|
||||
* external/cwe/cwe-415
|
||||
* experimental
|
||||
*/
|
||||
|
||||
import cpp
|
||||
|
||||
@@ -9,7 +9,6 @@
|
||||
* security
|
||||
* external/cwe/cwe-476
|
||||
* external/cwe/cwe-415
|
||||
* experimental
|
||||
*/
|
||||
|
||||
import cpp
|
||||
|
||||
@@ -11,7 +11,6 @@
|
||||
* external/cwe/cwe-561
|
||||
* external/cwe/cwe-691
|
||||
* external/cwe/cwe-478
|
||||
* experimental
|
||||
*/
|
||||
|
||||
import cpp
|
||||
|
||||
@@ -8,7 +8,6 @@
|
||||
* @tags correctness
|
||||
* security
|
||||
* external/cwe/cwe-670
|
||||
* experimental
|
||||
*/
|
||||
|
||||
import cpp
|
||||
|
||||
@@ -8,7 +8,6 @@
|
||||
* @tags security
|
||||
* external/cwe/cwe-675
|
||||
* external/cwe/cwe-666
|
||||
* experimental
|
||||
*/
|
||||
|
||||
import cpp
|
||||
|
||||
@@ -11,7 +11,6 @@
|
||||
* @tags correctness
|
||||
* security
|
||||
* external/cwe/cwe-691
|
||||
* experimental
|
||||
*/
|
||||
|
||||
import cpp
|
||||
|
||||
@@ -9,7 +9,6 @@
|
||||
* @tags correctness
|
||||
* security
|
||||
* external/cwe/cwe-691
|
||||
* experimental
|
||||
*/
|
||||
|
||||
import cpp
|
||||
|
||||
@@ -10,7 +10,6 @@
|
||||
* external/cwe/cwe-703
|
||||
* external/cwe/cwe-248
|
||||
* external/cwe/cwe-390
|
||||
* experimental
|
||||
*/
|
||||
|
||||
import cpp
|
||||
|
||||
@@ -9,7 +9,6 @@
|
||||
* security
|
||||
* external/cwe/cwe-754
|
||||
* external/cwe/cwe-908
|
||||
* experimental
|
||||
*/
|
||||
|
||||
import cpp
|
||||
|
||||
@@ -9,7 +9,6 @@
|
||||
* @precision medium
|
||||
* @tags security
|
||||
* external/cwe/cwe-758
|
||||
* experimental
|
||||
*/
|
||||
|
||||
import cpp
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user