mirror of
https://github.com/github/codeql.git
synced 2025-12-21 11:16:30 +01:00
Merge branch 'main' into henrymercer/mergeback-3.8
This commit is contained in:
6
cpp/ql/lib/change-notes/2022-11-14-deprecate-ast-gvn.md
Normal file
6
cpp/ql/lib/change-notes/2022-11-14-deprecate-ast-gvn.md
Normal file
@@ -0,0 +1,6 @@
|
||||
---
|
||||
category: deprecated
|
||||
---
|
||||
|
||||
|
||||
* Deprecated `semmle.code.cpp.valuenumbering.GlobalValueNumberingImpl`. Use `semmle.code.cpp.valuenumbering.GlobalValueNumbering`, which exposes the same API.
|
||||
4
cpp/ql/lib/change-notes/2022-11-16-must-flow.md
Normal file
4
cpp/ql/lib/change-notes/2022-11-16-must-flow.md
Normal file
@@ -0,0 +1,4 @@
|
||||
---
|
||||
category: breaking
|
||||
---
|
||||
The predicates in the `MustFlow::Configuration` class used by the `MustFlow` library (`semmle.code.cpp.ir.dataflow.MustFlow`) have changed to be defined directly in terms of the C++ IR instead of IR dataflow nodes.
|
||||
4
cpp/ql/lib/change-notes/2022-11-17-deleted-deps.md
Normal file
4
cpp/ql/lib/change-notes/2022-11-17-deleted-deps.md
Normal file
@@ -0,0 +1,4 @@
|
||||
---
|
||||
category: minorAnalysis
|
||||
---
|
||||
* Deleted the deprecated `getName` and `getShortName` predicates from the `Folder` class.
|
||||
@@ -0,0 +1,6 @@
|
||||
---
|
||||
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`.
|
||||
@@ -0,0 +1,4 @@
|
||||
---
|
||||
category: minorAnalysis
|
||||
---
|
||||
* The `getaddrinfo` function is now recognized as a flow source.
|
||||
@@ -0,0 +1,4 @@
|
||||
---
|
||||
category: minorAnalysis
|
||||
---
|
||||
* The `secure_getenv` and `_wgetenv` functions are now recognized as local flow sources.
|
||||
@@ -0,0 +1,4 @@
|
||||
---
|
||||
category: minorAnalysis
|
||||
---
|
||||
* The `scanf` and `fscanf` functions and their variants are now recognized as flow sources.
|
||||
@@ -0,0 +1,4 @@
|
||||
---
|
||||
category: minorAnalysis
|
||||
---
|
||||
* The `ArgvSource` flow source has been generalized to handle cases where the argument vector of `main` is not named `argv`.
|
||||
@@ -70,7 +70,7 @@ abstract class Configuration extends string {
|
||||
/**
|
||||
* Holds if `sink` is a relevant data flow sink accepting `state`.
|
||||
*/
|
||||
predicate isSink(Node source, FlowState state) { none() }
|
||||
predicate isSink(Node sink, FlowState state) { none() }
|
||||
|
||||
/**
|
||||
* Holds if data flow through `node` is prohibited. This completely removes
|
||||
@@ -319,8 +319,6 @@ private class ParamNodeEx extends NodeEx {
|
||||
}
|
||||
|
||||
ParameterPosition getPosition() { this.isParameterOf(_, result) }
|
||||
|
||||
predicate allowParameterReturnInSelf() { allowParameterReturnInSelfCached(this.asNode()) }
|
||||
}
|
||||
|
||||
private class RetNodeEx extends NodeEx {
|
||||
@@ -608,6 +606,38 @@ private predicate hasSinkCallCtx(Configuration config) {
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if flow from `p` to a return node of kind `kind` is allowed.
|
||||
*
|
||||
* We don't expect a parameter to return stored in itself, unless
|
||||
* explicitly allowed
|
||||
*/
|
||||
bindingset[p, kind]
|
||||
private predicate parameterFlowThroughAllowed(ParamNodeEx p, ReturnKindExt kind) {
|
||||
exists(ParameterPosition pos | p.isParameterOf(_, pos) |
|
||||
not kind.(ParamUpdateReturnKind).getPosition() = pos
|
||||
or
|
||||
allowParameterReturnInSelfCached(p.asNode())
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* 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;
|
||||
|
||||
@@ -981,21 +1011,22 @@ private module Stage1 implements StageSig {
|
||||
* candidate for the origin of a summary.
|
||||
*/
|
||||
pragma[nomagic]
|
||||
predicate parameterMayFlowThrough(ParamNodeEx p, DataFlowCallable c, Ap ap, Configuration config) {
|
||||
exists(ReturnKindExt kind |
|
||||
predicate parameterMayFlowThrough(ParamNodeEx p, Ap ap, Configuration config) {
|
||||
exists(DataFlowCallable c, ReturnKindExt kind |
|
||||
throughFlowNodeCand(p, config) and
|
||||
returnFlowCallableNodeCand(c, kind, config) and
|
||||
p.getEnclosingCallable() = c and
|
||||
exists(ap) and
|
||||
// we don't expect a parameter to return stored in itself, unless explicitly allowed
|
||||
(
|
||||
not kind.(ParamUpdateReturnKind).getPosition() = p.getPosition()
|
||||
or
|
||||
p.allowParameterReturnInSelf()
|
||||
)
|
||||
parameterFlowThroughAllowed(p, kind)
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
predicate returnMayFlowThrough(RetNodeEx ret, ReturnKindExt kind, Configuration config) {
|
||||
throughFlowNodeCand(ret, config) and
|
||||
kind = ret.getKind()
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
predicate callMayFlowThroughRev(DataFlowCall call, Configuration config) {
|
||||
exists(ArgNodeEx arg, boolean toReturn |
|
||||
@@ -1052,12 +1083,16 @@ private predicate viableReturnPosOutNodeCand1(
|
||||
*/
|
||||
pragma[nomagic]
|
||||
private predicate flowOutOfCallNodeCand1(
|
||||
DataFlowCall call, RetNodeEx ret, NodeEx out, Configuration config
|
||||
DataFlowCall call, RetNodeEx ret, ReturnKindExt kind, NodeEx out, Configuration config
|
||||
) {
|
||||
viableReturnPosOutNodeCand1(call, ret.getReturnPosition(), out, config) and
|
||||
Stage1::revFlow(ret, config) and
|
||||
not outBarrier(ret, config) and
|
||||
not inBarrier(out, config)
|
||||
exists(ReturnPosition pos |
|
||||
viableReturnPosOutNodeCand1(call, pos, out, config) and
|
||||
pos = ret.getReturnPosition() and
|
||||
kind = pos.getKind() and
|
||||
Stage1::revFlow(ret, config) and
|
||||
not outBarrier(ret, config) and
|
||||
not inBarrier(out, config)
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
@@ -1087,10 +1122,11 @@ private predicate flowIntoCallNodeCand1(
|
||||
* edge in the graph of paths between sources and sinks that ignores call
|
||||
* contexts.
|
||||
*/
|
||||
pragma[nomagic]
|
||||
private int branch(NodeEx n1, Configuration conf) {
|
||||
result =
|
||||
strictcount(NodeEx n |
|
||||
flowOutOfCallNodeCand1(_, n1, n, conf) or flowIntoCallNodeCand1(_, n1, n, conf)
|
||||
flowOutOfCallNodeCand1(_, n1, _, n, conf) or flowIntoCallNodeCand1(_, n1, n, conf)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1099,10 +1135,11 @@ private int branch(NodeEx n1, Configuration conf) {
|
||||
* edge in the graph of paths between sources and sinks that ignores call
|
||||
* contexts.
|
||||
*/
|
||||
pragma[nomagic]
|
||||
private int join(NodeEx n2, Configuration conf) {
|
||||
result =
|
||||
strictcount(NodeEx n |
|
||||
flowOutOfCallNodeCand1(_, n, n2, conf) or flowIntoCallNodeCand1(_, n, n2, conf)
|
||||
flowOutOfCallNodeCand1(_, n, _, n2, conf) or flowIntoCallNodeCand1(_, n, n2, conf)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1115,12 +1152,13 @@ private int join(NodeEx n2, Configuration conf) {
|
||||
*/
|
||||
pragma[nomagic]
|
||||
private predicate flowOutOfCallNodeCand1(
|
||||
DataFlowCall call, RetNodeEx ret, NodeEx out, boolean allowsFieldFlow, Configuration config
|
||||
DataFlowCall call, RetNodeEx ret, ReturnKindExt kind, NodeEx out, boolean allowsFieldFlow,
|
||||
Configuration config
|
||||
) {
|
||||
flowOutOfCallNodeCand1(call, ret, out, config) and
|
||||
flowOutOfCallNodeCand1(call, ret, kind, out, pragma[only_bind_into](config)) and
|
||||
exists(int b, int j |
|
||||
b = branch(ret, config) and
|
||||
j = join(out, config) and
|
||||
b = branch(ret, pragma[only_bind_into](config)) and
|
||||
j = join(out, pragma[only_bind_into](config)) and
|
||||
if b.minimum(j) <= config.fieldFlowBranchLimit()
|
||||
then allowsFieldFlow = true
|
||||
else allowsFieldFlow = false
|
||||
@@ -1136,10 +1174,10 @@ pragma[nomagic]
|
||||
private predicate flowIntoCallNodeCand1(
|
||||
DataFlowCall call, ArgNodeEx arg, ParamNodeEx p, boolean allowsFieldFlow, Configuration config
|
||||
) {
|
||||
flowIntoCallNodeCand1(call, arg, p, config) and
|
||||
flowIntoCallNodeCand1(call, arg, p, pragma[only_bind_into](config)) and
|
||||
exists(int b, int j |
|
||||
b = branch(arg, config) and
|
||||
j = join(p, config) and
|
||||
b = branch(arg, pragma[only_bind_into](config)) and
|
||||
j = join(p, pragma[only_bind_into](config)) and
|
||||
if b.minimum(j) <= config.fieldFlowBranchLimit()
|
||||
then allowsFieldFlow = true
|
||||
else allowsFieldFlow = false
|
||||
@@ -1156,7 +1194,9 @@ private signature module StageSig {
|
||||
|
||||
predicate callMayFlowThroughRev(DataFlowCall call, Configuration config);
|
||||
|
||||
predicate parameterMayFlowThrough(ParamNodeEx p, DataFlowCallable c, Ap ap, Configuration config);
|
||||
predicate parameterMayFlowThrough(ParamNodeEx p, Ap ap, Configuration config);
|
||||
|
||||
predicate returnMayFlowThrough(RetNodeEx ret, ReturnKindExt kind, Configuration config);
|
||||
|
||||
predicate storeStepCand(
|
||||
NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, DataFlowType contentType,
|
||||
@@ -1222,7 +1262,8 @@ private module MkStage<StageSig PrevStage> {
|
||||
);
|
||||
|
||||
predicate flowOutOfCall(
|
||||
DataFlowCall call, RetNodeEx ret, NodeEx out, boolean allowsFieldFlow, Configuration config
|
||||
DataFlowCall call, RetNodeEx ret, ReturnKindExt kind, NodeEx out, boolean allowsFieldFlow,
|
||||
Configuration config
|
||||
);
|
||||
|
||||
predicate flowIntoCall(
|
||||
@@ -1247,14 +1288,14 @@ private module MkStage<StageSig PrevStage> {
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate flowThroughOutOfCall(
|
||||
DataFlowCall call, CcCall ccc, RetNodeEx ret, NodeEx out, boolean allowsFieldFlow,
|
||||
Configuration config
|
||||
DataFlowCall call, DataFlowCallable c, CcCall ccc, RetNodeEx ret, ReturnKindExt kind,
|
||||
NodeEx out, boolean allowsFieldFlow, Configuration config
|
||||
) {
|
||||
flowOutOfCall(call, ret, out, allowsFieldFlow, pragma[only_bind_into](config)) and
|
||||
flowOutOfCall(call, ret, kind, out, allowsFieldFlow, pragma[only_bind_into](config)) and
|
||||
PrevStage::callMayFlowThroughRev(call, pragma[only_bind_into](config)) and
|
||||
PrevStage::parameterMayFlowThrough(_, ret.getEnclosingCallable(), _,
|
||||
pragma[only_bind_into](config)) and
|
||||
matchesCall(ccc, call)
|
||||
PrevStage::returnMayFlowThrough(ret, kind, pragma[only_bind_into](config)) and
|
||||
matchesCall(ccc, call) and
|
||||
c = ret.getEnclosingCallable()
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1262,29 +1303,32 @@ private module MkStage<StageSig PrevStage> {
|
||||
* configuration `config`.
|
||||
*
|
||||
* The call context `cc` records whether the node is reached through an
|
||||
* argument in a call, and if so, `argAp` records the access path of that
|
||||
* argument.
|
||||
* argument in a call, and if so, `summaryCtx` and `argAp` record the
|
||||
* corresponding parameter position and access path of that argument, respectively.
|
||||
*/
|
||||
pragma[nomagic]
|
||||
additional predicate fwdFlow(
|
||||
NodeEx node, FlowState state, Cc cc, ApOption argAp, Ap ap, Configuration config
|
||||
NodeEx node, FlowState state, Cc cc, ParameterPositionOption summaryCtx, ApOption argAp,
|
||||
Ap ap, Configuration config
|
||||
) {
|
||||
fwdFlow0(node, state, cc, argAp, ap, config) and
|
||||
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, ApOption argAp, Ap ap, Configuration config
|
||||
NodeEx node, FlowState state, Cc cc, ParameterPositionOption summaryCtx, ApOption argAp,
|
||||
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)
|
||||
or
|
||||
exists(NodeEx mid, FlowState state0, Ap ap0, LocalCc localCc |
|
||||
fwdFlow(mid, state0, cc, argAp, ap0, config) and
|
||||
fwdFlow(mid, state0, cc, summaryCtx, argAp, ap0, config) and
|
||||
localCc = getLocalCc(mid, cc)
|
||||
|
|
||||
localStep(mid, state0, node, state, true, _, config, localCc) and
|
||||
@@ -1295,65 +1339,82 @@ private module MkStage<StageSig PrevStage> {
|
||||
)
|
||||
or
|
||||
exists(NodeEx mid |
|
||||
fwdFlow(mid, pragma[only_bind_into](state), _, _, ap, 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
|
||||
argAp = apNone()
|
||||
)
|
||||
or
|
||||
exists(NodeEx mid, ApNil nil |
|
||||
fwdFlow(mid, state, _, _, nil, pragma[only_bind_into](config)) and
|
||||
fwdFlow(mid, state, _, _, _, nil, pragma[only_bind_into](config)) and
|
||||
additionalJumpStep(mid, node, config) and
|
||||
cc = ccNone() and
|
||||
summaryCtx = TParameterPositionNone() and
|
||||
argAp = apNone() and
|
||||
ap = getApNil(node)
|
||||
)
|
||||
or
|
||||
exists(NodeEx mid, FlowState state0, ApNil nil |
|
||||
fwdFlow(mid, state0, _, _, nil, pragma[only_bind_into](config)) and
|
||||
fwdFlow(mid, state0, _, _, _, nil, pragma[only_bind_into](config)) and
|
||||
additionalJumpStateStep(mid, state0, node, state, config) and
|
||||
cc = ccNone() and
|
||||
summaryCtx = TParameterPositionNone() and
|
||||
argAp = apNone() and
|
||||
ap = getApNil(node)
|
||||
)
|
||||
or
|
||||
// store
|
||||
exists(TypedContent tc, Ap ap0 |
|
||||
fwdFlowStore(_, ap0, tc, node, state, cc, argAp, config) and
|
||||
fwdFlowStore(_, ap0, tc, node, state, cc, summaryCtx, argAp, config) and
|
||||
ap = apCons(tc, ap0)
|
||||
)
|
||||
or
|
||||
// read
|
||||
exists(Ap ap0, Content c |
|
||||
fwdFlowRead(ap0, c, _, node, state, cc, argAp, config) and
|
||||
fwdFlowRead(ap0, c, _, node, state, cc, summaryCtx, argAp, config) and
|
||||
fwdFlowConsCand(ap0, c, ap, config)
|
||||
)
|
||||
or
|
||||
// flow into a callable
|
||||
exists(ApApprox apa |
|
||||
fwdFlowIn(_, node, state, _, cc, _, ap, config) and
|
||||
fwdFlowIn(_, node, state, _, cc, _, _, ap, config) and
|
||||
apa = getApprox(ap) and
|
||||
if PrevStage::parameterMayFlowThrough(node, _, apa, config)
|
||||
then argAp = apSome(ap)
|
||||
else argAp = apNone()
|
||||
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
|
||||
fwdFlowOutNotFromArg(node, state, cc, argAp, ap, config)
|
||||
exists(
|
||||
DataFlowCall call, RetNodeEx ret, boolean allowsFieldFlow, CcNoCall innercc,
|
||||
DataFlowCallable inner
|
||||
|
|
||||
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()
|
||||
)
|
||||
or
|
||||
exists(DataFlowCall call, Ap argAp0 |
|
||||
fwdFlowOutFromArg(call, node, state, argAp0, ap, config) and
|
||||
fwdFlowIsEntered(call, cc, argAp, argAp0, config)
|
||||
// flow through a callable
|
||||
exists(DataFlowCall call, ParameterPosition summaryCtx0, Ap argAp0 |
|
||||
fwdFlowOutFromArg(call, node, state, summaryCtx0, argAp0, ap, config) and
|
||||
fwdFlowIsEntered(call, cc, summaryCtx, argAp, summaryCtx0, argAp0, config)
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate fwdFlowStore(
|
||||
NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, FlowState state, Cc cc, ApOption argAp,
|
||||
Configuration config
|
||||
NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, FlowState state, Cc cc,
|
||||
ParameterPositionOption summaryCtx, ApOption argAp, Configuration config
|
||||
) {
|
||||
exists(DataFlowType contentType |
|
||||
fwdFlow(node1, state, cc, argAp, ap1, config) and
|
||||
fwdFlow(node1, state, cc, summaryCtx, argAp, ap1, config) and
|
||||
PrevStage::storeStepCand(node1, unbindApa(getApprox(ap1)), tc, node2, contentType, config) and
|
||||
typecheckStore(ap1, contentType)
|
||||
)
|
||||
@@ -1366,7 +1427,7 @@ private module MkStage<StageSig PrevStage> {
|
||||
pragma[nomagic]
|
||||
private predicate fwdFlowConsCand(Ap cons, Content c, Ap tail, Configuration config) {
|
||||
exists(TypedContent tc |
|
||||
fwdFlowStore(_, tail, tc, _, _, _, _, config) and
|
||||
fwdFlowStore(_, tail, tc, _, _, _, _, _, config) and
|
||||
tc.getContent() = c and
|
||||
cons = apCons(tc, tail)
|
||||
)
|
||||
@@ -1374,21 +1435,21 @@ private module MkStage<StageSig PrevStage> {
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate fwdFlowRead(
|
||||
Ap ap, Content c, NodeEx node1, NodeEx node2, FlowState state, Cc cc, ApOption argAp,
|
||||
Configuration config
|
||||
Ap ap, Content c, NodeEx node1, NodeEx node2, FlowState state, Cc cc,
|
||||
ParameterPositionOption summaryCtx, ApOption argAp, Configuration config
|
||||
) {
|
||||
fwdFlow(node1, state, cc, 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, Cc innercc, ApOption argAp,
|
||||
Ap ap, 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, argAp, ap, 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()
|
||||
@@ -1396,29 +1457,19 @@ private module MkStage<StageSig PrevStage> {
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate fwdFlowOutNotFromArg(
|
||||
NodeEx out, FlowState state, Cc ccOut, ApOption argAp, Ap ap, Configuration config
|
||||
private predicate fwdFlowOutFromArg(
|
||||
DataFlowCall call, NodeEx out, FlowState state, ParameterPosition summaryCtx, Ap argAp, Ap ap,
|
||||
Configuration config
|
||||
) {
|
||||
exists(
|
||||
DataFlowCall call, RetNodeEx ret, boolean allowsFieldFlow, CcNoCall innercc,
|
||||
DataFlowCallable inner
|
||||
DataFlowCallable c, RetNodeEx ret, ReturnKindExt kind, boolean allowsFieldFlow, CcCall ccc
|
||||
|
|
||||
fwdFlow(ret, state, innercc, argAp, ap, config) and
|
||||
flowOutOfCall(call, ret, out, allowsFieldFlow, config) and
|
||||
inner = ret.getEnclosingCallable() and
|
||||
ccOut = getCallContextReturn(inner, call, innercc) and
|
||||
if allowsFieldFlow = false then ap instanceof ApNil else any()
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate fwdFlowOutFromArg(
|
||||
DataFlowCall call, NodeEx out, FlowState state, Ap argAp, Ap ap, Configuration config
|
||||
) {
|
||||
exists(RetNodeEx ret, boolean allowsFieldFlow, CcCall ccc |
|
||||
fwdFlow(ret, state, ccc, apSome(argAp), ap, config) and
|
||||
flowThroughOutOfCall(call, ccc, ret, out, allowsFieldFlow, config) and
|
||||
if allowsFieldFlow = false then ap instanceof ApNil else any()
|
||||
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
|
||||
(if allowsFieldFlow = false then ap instanceof ApNil else any()) and
|
||||
parameterFlowThroughAllowed(c, pragma[only_bind_into](summaryCtx), kind)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1428,11 +1479,13 @@ private module MkStage<StageSig PrevStage> {
|
||||
*/
|
||||
pragma[nomagic]
|
||||
private predicate fwdFlowIsEntered(
|
||||
DataFlowCall call, Cc cc, ApOption argAp, Ap ap, Configuration config
|
||||
DataFlowCall call, Cc cc, ParameterPositionOption summaryCtx, ApOption argAp,
|
||||
ParameterPosition pos, Ap ap, Configuration config
|
||||
) {
|
||||
exists(ParamNodeEx p |
|
||||
fwdFlowIn(call, p, _, cc, _, argAp, ap, config) and
|
||||
PrevStage::parameterMayFlowThrough(p, _, unbindApa(getApprox(ap)), config)
|
||||
exists(ParamNodeEx param |
|
||||
fwdFlowIn(call, param, _, cc, _, summaryCtx, argAp, ap, config) and
|
||||
PrevStage::parameterMayFlowThrough(param, unbindApa(getApprox(ap)), config) and
|
||||
pos = param.getPosition()
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1440,27 +1493,40 @@ private module MkStage<StageSig PrevStage> {
|
||||
private predicate storeStepFwd(
|
||||
NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, Ap ap2, Configuration config
|
||||
) {
|
||||
fwdFlowStore(node1, ap1, tc, node2, _, _, _, config) and
|
||||
fwdFlowStore(node1, ap1, tc, node2, _, _, _, _, config) and
|
||||
ap2 = apCons(tc, ap1) and
|
||||
fwdFlowRead(ap2, tc.getContent(), _, _, _, _, _, config)
|
||||
fwdFlowRead(ap2, tc.getContent(), _, _, _, _, _, _, config)
|
||||
}
|
||||
|
||||
private predicate readStepFwd(
|
||||
NodeEx n1, Ap ap1, Content c, NodeEx n2, Ap ap2, Configuration config
|
||||
) {
|
||||
fwdFlowRead(ap1, c, n1, n2, _, _, _, config) and
|
||||
fwdFlowRead(ap1, c, n1, n2, _, _, _, _, config) and
|
||||
fwdFlowConsCand(ap1, c, ap2, config)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate callMayFlowThroughFwd(DataFlowCall call, Configuration config) {
|
||||
exists(Ap argAp0, NodeEx out, FlowState state, Cc cc, ApOption argAp, Ap ap |
|
||||
fwdFlow(out, state, pragma[only_bind_into](cc), pragma[only_bind_into](argAp), ap,
|
||||
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
|
||||
fwdFlowOutFromArg(call, out, state, argAp0, ap, config) and
|
||||
fwdFlowIsEntered(pragma[only_bind_into](call), pragma[only_bind_into](cc),
|
||||
pragma[only_bind_into](argAp), pragma[only_bind_into](argAp0),
|
||||
pragma[only_bind_into](config))
|
||||
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(DataFlowCallable c, ParameterPosition ppos |
|
||||
returnFlowsThrough0(ret, kind, state, ccc, c, ppos, argAp, ap, config) and
|
||||
p.isParameterOf(c, ppos) and
|
||||
parameterFlowThroughAllowed(p, kind)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1468,118 +1534,130 @@ private module MkStage<StageSig PrevStage> {
|
||||
private predicate flowThroughIntoCall(
|
||||
DataFlowCall call, ArgNodeEx arg, ParamNodeEx p, boolean allowsFieldFlow, Configuration config
|
||||
) {
|
||||
flowIntoCall(call, arg, p, allowsFieldFlow, config) and
|
||||
fwdFlow(arg, _, _, _, _, pragma[only_bind_into](config)) and
|
||||
PrevStage::parameterMayFlowThrough(p, _, _, pragma[only_bind_into](config)) and
|
||||
callMayFlowThroughFwd(call, pragma[only_bind_into](config))
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate returnNodeMayFlowThrough(
|
||||
RetNodeEx ret, FlowState state, Ap ap, Configuration config
|
||||
) {
|
||||
fwdFlow(ret, state, any(CcCall ccc), apSome(_), ap, config)
|
||||
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))
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `node` with access path `ap` is part of a path from a source to a
|
||||
* sink in the configuration `config`.
|
||||
*
|
||||
* The Boolean `toReturn` records whether the node must be returned from the
|
||||
* enclosing callable in order to reach a sink, and if so, `returnAp` records
|
||||
* the access path of the returned value.
|
||||
* The parameter `returnCtx` records whether (and how) the node must be returned
|
||||
* from the enclosing callable in order to reach a sink, and if so, `returnAp`
|
||||
* records the access path of the returned value.
|
||||
*/
|
||||
pragma[nomagic]
|
||||
additional predicate revFlow(
|
||||
NodeEx node, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, Configuration config
|
||||
NodeEx node, FlowState state, ReturnCtx returnCtx, ApOption returnAp, Ap ap,
|
||||
Configuration config
|
||||
) {
|
||||
revFlow0(node, state, toReturn, returnAp, ap, config) and
|
||||
fwdFlow(node, state, _, _, ap, config)
|
||||
revFlow0(node, state, returnCtx, returnAp, ap, config) and
|
||||
fwdFlow(node, state, _, _, _, ap, config)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate revFlow0(
|
||||
NodeEx node, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, Configuration config
|
||||
NodeEx node, FlowState state, ReturnCtx returnCtx, ApOption returnAp, Ap ap,
|
||||
Configuration config
|
||||
) {
|
||||
fwdFlow(node, state, _, _, ap, config) and
|
||||
fwdFlow(node, state, _, _, _, ap, config) and
|
||||
sinkNode(node, state, config) and
|
||||
(if hasSinkCallCtx(config) then toReturn = true else toReturn = false) and
|
||||
(
|
||||
if hasSinkCallCtx(config)
|
||||
then returnCtx = TReturnCtxNoFlowThrough()
|
||||
else returnCtx = TReturnCtxNone()
|
||||
) and
|
||||
returnAp = apNone() and
|
||||
ap instanceof ApNil
|
||||
or
|
||||
exists(NodeEx mid, FlowState state0 |
|
||||
localStep(node, state, mid, state0, true, _, config, _) and
|
||||
revFlow(mid, state0, toReturn, returnAp, ap, config)
|
||||
revFlow(mid, state0, returnCtx, returnAp, ap, config)
|
||||
)
|
||||
or
|
||||
exists(NodeEx mid, FlowState state0, ApNil nil |
|
||||
fwdFlow(node, pragma[only_bind_into](state), _, _, ap, pragma[only_bind_into](config)) and
|
||||
fwdFlow(node, pragma[only_bind_into](state), _, _, _, ap, pragma[only_bind_into](config)) and
|
||||
localStep(node, pragma[only_bind_into](state), mid, state0, false, _, config, _) and
|
||||
revFlow(mid, state0, toReturn, returnAp, nil, pragma[only_bind_into](config)) and
|
||||
revFlow(mid, state0, returnCtx, returnAp, nil, pragma[only_bind_into](config)) and
|
||||
ap instanceof ApNil
|
||||
)
|
||||
or
|
||||
exists(NodeEx mid |
|
||||
jumpStep(node, mid, config) and
|
||||
revFlow(mid, state, _, _, ap, config) and
|
||||
toReturn = false and
|
||||
returnCtx = TReturnCtxNone() and
|
||||
returnAp = apNone()
|
||||
)
|
||||
or
|
||||
exists(NodeEx mid, ApNil nil |
|
||||
fwdFlow(node, _, _, _, ap, pragma[only_bind_into](config)) and
|
||||
fwdFlow(node, _, _, _, _, ap, pragma[only_bind_into](config)) and
|
||||
additionalJumpStep(node, mid, config) and
|
||||
revFlow(pragma[only_bind_into](mid), state, _, _, nil, pragma[only_bind_into](config)) and
|
||||
toReturn = false and
|
||||
returnCtx = TReturnCtxNone() and
|
||||
returnAp = apNone() and
|
||||
ap instanceof ApNil
|
||||
)
|
||||
or
|
||||
exists(NodeEx mid, FlowState state0, ApNil nil |
|
||||
fwdFlow(node, _, _, _, ap, pragma[only_bind_into](config)) and
|
||||
fwdFlow(node, _, _, _, _, ap, pragma[only_bind_into](config)) and
|
||||
additionalJumpStateStep(node, state, mid, state0, config) and
|
||||
revFlow(pragma[only_bind_into](mid), pragma[only_bind_into](state0), _, _, nil,
|
||||
pragma[only_bind_into](config)) and
|
||||
toReturn = false and
|
||||
returnCtx = TReturnCtxNone() and
|
||||
returnAp = apNone() and
|
||||
ap instanceof ApNil
|
||||
)
|
||||
or
|
||||
// store
|
||||
exists(Ap ap0, Content c |
|
||||
revFlowStore(ap0, c, ap, node, state, _, _, toReturn, returnAp, config) and
|
||||
revFlowStore(ap0, c, ap, node, state, _, _, returnCtx, returnAp, config) and
|
||||
revFlowConsCand(ap0, c, ap, config)
|
||||
)
|
||||
or
|
||||
// read
|
||||
exists(NodeEx mid, Ap ap0 |
|
||||
revFlow(mid, state, toReturn, returnAp, ap0, config) and
|
||||
revFlow(mid, state, returnCtx, returnAp, ap0, config) and
|
||||
readStepFwd(node, ap, _, mid, ap0, config)
|
||||
)
|
||||
or
|
||||
// flow into a callable
|
||||
revFlowInNotToReturn(node, state, returnAp, ap, config) and
|
||||
toReturn = false
|
||||
exists(ParamNodeEx p, boolean allowsFieldFlow |
|
||||
revFlow(p, state, TReturnCtxNone(), returnAp, ap, config) and
|
||||
flowIntoCall(_, node, p, allowsFieldFlow, config) and
|
||||
(if allowsFieldFlow = false then ap instanceof ApNil else any()) and
|
||||
returnCtx = TReturnCtxNone()
|
||||
)
|
||||
or
|
||||
exists(DataFlowCall call, Ap returnAp0 |
|
||||
revFlowInToReturn(call, node, state, returnAp0, ap, config) and
|
||||
revFlowIsReturned(call, toReturn, returnAp, returnAp0, config)
|
||||
// flow through a callable
|
||||
exists(DataFlowCall call, ReturnKindExt returnKind0, Ap returnAp0 |
|
||||
revFlowInToReturn(call, node, state, returnKind0, returnAp0, ap, config) and
|
||||
revFlowIsReturned(call, returnCtx, returnAp, returnKind0, returnAp0, config)
|
||||
)
|
||||
or
|
||||
// flow out of a callable
|
||||
revFlowOut(_, node, state, _, _, ap, config) and
|
||||
toReturn = true and
|
||||
if returnNodeMayFlowThrough(node, state, ap, config)
|
||||
then returnAp = apSome(ap)
|
||||
else returnAp = apNone()
|
||||
exists(ReturnKindExt kind |
|
||||
revFlowOut(_, node, kind, state, _, _, ap, config) and
|
||||
if returnFlowsThrough(node, kind, state, _, _, _, ap, config)
|
||||
then (
|
||||
returnCtx = TReturnCtxMaybeFlowThrough(kind) and
|
||||
returnAp = apSome(ap)
|
||||
) else (
|
||||
returnCtx = TReturnCtxNoFlowThrough() and returnAp = apNone()
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate revFlowStore(
|
||||
Ap ap0, Content c, Ap ap, NodeEx node, FlowState state, TypedContent tc, NodeEx mid,
|
||||
boolean toReturn, ApOption returnAp, Configuration config
|
||||
ReturnCtx returnCtx, ApOption returnAp, Configuration config
|
||||
) {
|
||||
revFlow(mid, state, toReturn, returnAp, ap0, config) and
|
||||
revFlow(mid, state, returnCtx, returnAp, ap0, config) and
|
||||
storeStepFwd(node, ap, tc, mid, ap0, config) and
|
||||
tc.getContent() = c
|
||||
}
|
||||
@@ -1599,35 +1677,27 @@ private module MkStage<StageSig PrevStage> {
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate revFlowOut(
|
||||
DataFlowCall call, RetNodeEx ret, FlowState state, boolean toReturn, ApOption returnAp, Ap ap,
|
||||
Configuration config
|
||||
DataFlowCall call, RetNodeEx ret, ReturnKindExt kind, FlowState state, ReturnCtx returnCtx,
|
||||
ApOption returnAp, Ap ap, Configuration config
|
||||
) {
|
||||
exists(NodeEx out, boolean allowsFieldFlow |
|
||||
revFlow(out, state, toReturn, returnAp, ap, config) and
|
||||
flowOutOfCall(call, ret, out, allowsFieldFlow, config) and
|
||||
if allowsFieldFlow = false then ap instanceof ApNil else any()
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate revFlowInNotToReturn(
|
||||
ArgNodeEx arg, FlowState state, ApOption returnAp, Ap ap, Configuration config
|
||||
) {
|
||||
exists(ParamNodeEx p, boolean allowsFieldFlow |
|
||||
revFlow(p, state, false, returnAp, ap, config) and
|
||||
flowIntoCall(_, arg, p, allowsFieldFlow, config) and
|
||||
revFlow(out, state, returnCtx, returnAp, ap, config) and
|
||||
flowOutOfCall(call, ret, kind, out, allowsFieldFlow, config) and
|
||||
if allowsFieldFlow = false then ap instanceof ApNil else any()
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate revFlowInToReturn(
|
||||
DataFlowCall call, ArgNodeEx arg, FlowState state, Ap returnAp, Ap ap, Configuration config
|
||||
DataFlowCall call, ArgNodeEx arg, FlowState state, ReturnKindExt kind, Ap returnAp, Ap ap,
|
||||
Configuration config
|
||||
) {
|
||||
exists(ParamNodeEx p, boolean allowsFieldFlow |
|
||||
revFlow(p, state, true, apSome(returnAp), ap, config) and
|
||||
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()
|
||||
(if allowsFieldFlow = false then ap instanceof ApNil else any()) and
|
||||
parameterFlowThroughAllowed(p, kind)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1638,11 +1708,12 @@ private module MkStage<StageSig PrevStage> {
|
||||
*/
|
||||
pragma[nomagic]
|
||||
private predicate revFlowIsReturned(
|
||||
DataFlowCall call, boolean toReturn, ApOption returnAp, Ap ap, Configuration config
|
||||
DataFlowCall call, ReturnCtx returnCtx, ApOption returnAp, ReturnKindExt kind, Ap ap,
|
||||
Configuration config
|
||||
) {
|
||||
exists(RetNodeEx ret, FlowState state, CcCall ccc |
|
||||
revFlowOut(call, ret, state, toReturn, returnAp, ap, config) and
|
||||
fwdFlow(ret, state, ccc, apSome(_), ap, config) and
|
||||
revFlowOut(call, ret, kind, state, returnCtx, returnAp, ap, config) and
|
||||
returnFlowsThrough(ret, kind, state, ccc, _, _, ap, config) and
|
||||
matchesCall(ccc, call)
|
||||
)
|
||||
}
|
||||
@@ -1713,40 +1784,39 @@ private module MkStage<StageSig PrevStage> {
|
||||
validAp(ap, config)
|
||||
}
|
||||
|
||||
pragma[noinline]
|
||||
private predicate parameterFlow(
|
||||
ParamNodeEx p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config
|
||||
pragma[nomagic]
|
||||
private predicate parameterFlowsThroughRev(
|
||||
ParamNodeEx p, Ap ap, ReturnKindExt kind, Configuration config
|
||||
) {
|
||||
revFlow(p, _, true, apSome(ap0), ap, config) and
|
||||
c = p.getEnclosingCallable()
|
||||
revFlow(p, _, TReturnCtxMaybeFlowThrough(kind), apSome(_), ap, config) and
|
||||
parameterFlowThroughAllowed(p, kind)
|
||||
}
|
||||
|
||||
predicate parameterMayFlowThrough(ParamNodeEx p, DataFlowCallable c, Ap ap, Configuration config) {
|
||||
exists(RetNodeEx ret, FlowState state, Ap ap0, ReturnKindExt kind, ParameterPosition pos |
|
||||
parameterFlow(p, ap, ap0, c, config) and
|
||||
c = ret.getEnclosingCallable() and
|
||||
revFlow(pragma[only_bind_into](ret), pragma[only_bind_into](state), true, apSome(_),
|
||||
pragma[only_bind_into](ap0), pragma[only_bind_into](config)) and
|
||||
fwdFlow(ret, state, any(CcCall ccc), apSome(ap), ap0, config) and
|
||||
kind = ret.getKind() and
|
||||
p.getPosition() = pos and
|
||||
// we don't expect a parameter to return stored in itself, unless explicitly allowed
|
||||
(
|
||||
not kind.(ParamUpdateReturnKind).getPosition() = pos
|
||||
or
|
||||
p.allowParameterReturnInSelf()
|
||||
)
|
||||
pragma[nomagic]
|
||||
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)
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
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(
|
||||
Ap returnAp0, ArgNodeEx arg, FlowState state, boolean toReturn, ApOption returnAp, Ap ap
|
||||
ReturnKindExt returnKind0, Ap returnAp0, ArgNodeEx arg, FlowState state,
|
||||
ReturnCtx returnCtx, ApOption returnAp, Ap ap
|
||||
|
|
||||
revFlow(arg, state, toReturn, returnAp, ap, config) and
|
||||
revFlowInToReturn(call, arg, state, returnAp0, ap, config) and
|
||||
revFlowIsReturned(call, toReturn, returnAp, returnAp0, config)
|
||||
revFlow(arg, state, returnCtx, returnAp, ap, config) and
|
||||
revFlowInToReturn(call, arg, state, returnKind0, returnAp0, ap, config) and
|
||||
revFlowIsReturned(call, returnCtx, returnAp, returnKind0, returnAp0, config)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1754,14 +1824,13 @@ private module MkStage<StageSig PrevStage> {
|
||||
boolean fwd, int nodes, int fields, int conscand, int states, int tuples, Configuration config
|
||||
) {
|
||||
fwd = true and
|
||||
nodes = count(NodeEx node | fwdFlow(node, _, _, _, _, config)) and
|
||||
nodes = count(NodeEx node | fwdFlow(node, _, _, _, _, _, config)) and
|
||||
fields = count(TypedContent f0 | fwdConsCand(f0, _, config)) and
|
||||
conscand = count(TypedContent f0, Ap ap | fwdConsCand(f0, ap, config)) and
|
||||
states = count(FlowState state | fwdFlow(_, state, _, _, _, config)) and
|
||||
states = count(FlowState state | fwdFlow(_, state, _, _, _, _, config)) and
|
||||
tuples =
|
||||
count(NodeEx n, FlowState state, Cc cc, ApOption argAp, Ap ap |
|
||||
fwdFlow(n, state, cc, argAp, ap, config)
|
||||
)
|
||||
count(NodeEx n, FlowState state, Cc cc, ParameterPositionOption summaryCtx, ApOption argAp,
|
||||
Ap ap | fwdFlow(n, state, cc, summaryCtx, argAp, ap, config))
|
||||
or
|
||||
fwd = false and
|
||||
nodes = count(NodeEx node | revFlow(node, _, _, _, _, config)) and
|
||||
@@ -1769,8 +1838,8 @@ private module MkStage<StageSig PrevStage> {
|
||||
conscand = count(TypedContent f0, Ap ap | consCand(f0, ap, config)) and
|
||||
states = count(FlowState state | revFlow(_, state, _, _, _, config)) and
|
||||
tuples =
|
||||
count(NodeEx n, FlowState state, boolean b, ApOption retAp, Ap ap |
|
||||
revFlow(n, state, b, retAp, ap, config)
|
||||
count(NodeEx n, FlowState state, ReturnCtx returnCtx, ApOption retAp, Ap ap |
|
||||
revFlow(n, state, returnCtx, retAp, ap, config)
|
||||
)
|
||||
}
|
||||
/* End: Stage logic. */
|
||||
@@ -1915,7 +1984,7 @@ private module Stage2Param implements MkStage<Stage1>::StageParam {
|
||||
exists(lcc)
|
||||
}
|
||||
|
||||
predicate flowOutOfCall = flowOutOfCallNodeCand1/5;
|
||||
predicate flowOutOfCall = flowOutOfCallNodeCand1/6;
|
||||
|
||||
predicate flowIntoCall = flowIntoCallNodeCand1/5;
|
||||
|
||||
@@ -1951,9 +2020,10 @@ private module Stage2 implements StageSig {
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate flowOutOfCallNodeCand2(
|
||||
DataFlowCall call, RetNodeEx node1, NodeEx node2, boolean allowsFieldFlow, Configuration config
|
||||
DataFlowCall call, RetNodeEx node1, ReturnKindExt kind, NodeEx node2, boolean allowsFieldFlow,
|
||||
Configuration config
|
||||
) {
|
||||
flowOutOfCallNodeCand1(call, node1, node2, allowsFieldFlow, config) and
|
||||
flowOutOfCallNodeCand1(call, node1, kind, node2, allowsFieldFlow, config) and
|
||||
Stage2::revFlow(node2, pragma[only_bind_into](config)) and
|
||||
Stage2::revFlowAlias(node1, pragma[only_bind_into](config))
|
||||
}
|
||||
@@ -2021,8 +2091,8 @@ private module LocalFlowBigStep {
|
||||
exists(NodeEx next | Stage2::revFlow(next, state, config) |
|
||||
jumpStep(node, next, config) or
|
||||
additionalJumpStep(node, next, config) or
|
||||
flowIntoCallNodeCand1(_, node, next, config) or
|
||||
flowOutOfCallNodeCand1(_, node, next, config) or
|
||||
flowIntoCallNodeCand2(_, node, next, _, config) or
|
||||
flowOutOfCallNodeCand2(_, node, _, next, _, config) or
|
||||
Stage2::storeStepCand(node, _, _, next, _, config) or
|
||||
Stage2::readStepCand(node, _, next, config)
|
||||
)
|
||||
@@ -2163,7 +2233,7 @@ private module Stage3Param implements MkStage<Stage2>::StageParam {
|
||||
localFlowBigStep(node1, state1, node2, state2, preservesValue, ap, config, _) and exists(lcc)
|
||||
}
|
||||
|
||||
predicate flowOutOfCall = flowOutOfCallNodeCand2/5;
|
||||
predicate flowOutOfCall = flowOutOfCallNodeCand2/6;
|
||||
|
||||
predicate flowIntoCall = flowIntoCallNodeCand2/5;
|
||||
|
||||
@@ -2233,8 +2303,9 @@ private predicate flowCandSummaryCtx(
|
||||
NodeEx node, FlowState state, AccessPathFront argApf, Configuration config
|
||||
) {
|
||||
exists(AccessPathFront apf |
|
||||
Stage3::revFlow(node, state, true, _, apf, config) and
|
||||
Stage3::fwdFlow(node, state, any(Stage3::CcCall ccc), TAccessPathFrontSome(argApf), apf, config)
|
||||
Stage3::revFlow(node, state, TReturnCtxMaybeFlowThrough(_), _, apf, config) and
|
||||
Stage3::fwdFlow(node, state, any(Stage3::CcCall ccc), _, TAccessPathFrontSome(argApf), apf,
|
||||
config)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -2468,10 +2539,11 @@ private module Stage4Param implements MkStage<Stage3>::StageParam {
|
||||
|
||||
pragma[nomagic]
|
||||
predicate flowOutOfCall(
|
||||
DataFlowCall call, RetNodeEx node1, NodeEx node2, boolean allowsFieldFlow, Configuration config
|
||||
DataFlowCall call, RetNodeEx node1, ReturnKindExt kind, NodeEx node2, boolean allowsFieldFlow,
|
||||
Configuration config
|
||||
) {
|
||||
exists(FlowState state |
|
||||
flowOutOfCallNodeCand2(call, node1, node2, allowsFieldFlow, config) and
|
||||
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))
|
||||
@@ -2508,13 +2580,14 @@ private Configuration unbindConf(Configuration conf) {
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate nodeMayUseSummary0(
|
||||
NodeEx n, DataFlowCallable c, FlowState state, AccessPathApprox apa, Configuration config
|
||||
NodeEx n, DataFlowCallable c, ParameterPosition pos, FlowState state, AccessPathApprox apa,
|
||||
Configuration config
|
||||
) {
|
||||
exists(AccessPathApprox apa0 |
|
||||
Stage4::parameterMayFlowThrough(_, c, _, _) and
|
||||
Stage4::revFlow(n, state, true, _, apa0, config) and
|
||||
Stage4::fwdFlow(n, state, any(CallContextCall ccc), TAccessPathApproxSome(apa), apa0, config) and
|
||||
n.getEnclosingCallable() = c
|
||||
c = n.getEnclosingCallable() and
|
||||
Stage4::revFlow(n, state, TReturnCtxMaybeFlowThrough(_), _, apa0, config) and
|
||||
Stage4::fwdFlow(n, state, any(CallContextCall ccc), TParameterPositionSome(pos),
|
||||
TAccessPathApproxSome(apa), apa0, config)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -2522,9 +2595,10 @@ pragma[nomagic]
|
||||
private predicate nodeMayUseSummary(
|
||||
NodeEx n, FlowState state, AccessPathApprox apa, Configuration config
|
||||
) {
|
||||
exists(DataFlowCallable c |
|
||||
Stage4::parameterMayFlowThrough(_, c, apa, config) and
|
||||
nodeMayUseSummary0(n, c, state, apa, config)
|
||||
exists(DataFlowCallable c, ParameterPosition pos, ParamNodeEx p |
|
||||
Stage4::parameterMayFlowThrough(p, apa, config) and
|
||||
nodeMayUseSummary0(n, c, pos, state, apa, config) and
|
||||
p.isParameterOf(c, pos)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -2532,7 +2606,7 @@ private newtype TSummaryCtx =
|
||||
TSummaryCtxNone() or
|
||||
TSummaryCtxSome(ParamNodeEx p, FlowState state, AccessPath ap) {
|
||||
exists(Configuration config |
|
||||
Stage4::parameterMayFlowThrough(p, _, ap.getApprox(), config) and
|
||||
Stage4::parameterMayFlowThrough(p, ap.getApprox(), config) and
|
||||
Stage4::revFlow(p, state, _, config)
|
||||
)
|
||||
}
|
||||
@@ -3453,17 +3527,11 @@ private predicate paramFlowsThrough(
|
||||
ReturnKindExt kind, FlowState state, CallContextCall cc, SummaryCtxSome sc, AccessPath ap,
|
||||
AccessPathApprox apa, Configuration config
|
||||
) {
|
||||
exists(PathNodeMid mid, RetNodeEx ret, ParameterPosition pos |
|
||||
exists(PathNodeMid mid, RetNodeEx ret |
|
||||
pathNode(mid, ret, state, cc, sc, ap, config, _) and
|
||||
kind = ret.getKind() and
|
||||
apa = ap.getApprox() and
|
||||
pos = sc.getParameterPos() and
|
||||
// we don't expect a parameter to return stored in itself, unless explicitly allowed
|
||||
(
|
||||
not kind.(ParamUpdateReturnKind).getPosition() = pos
|
||||
or
|
||||
sc.getParamNode().allowParameterReturnInSelf()
|
||||
)
|
||||
parameterFlowThroughAllowed(sc.getParamNode(), kind)
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@@ -70,7 +70,7 @@ abstract class Configuration extends string {
|
||||
/**
|
||||
* Holds if `sink` is a relevant data flow sink accepting `state`.
|
||||
*/
|
||||
predicate isSink(Node source, FlowState state) { none() }
|
||||
predicate isSink(Node sink, FlowState state) { none() }
|
||||
|
||||
/**
|
||||
* Holds if data flow through `node` is prohibited. This completely removes
|
||||
@@ -319,8 +319,6 @@ private class ParamNodeEx extends NodeEx {
|
||||
}
|
||||
|
||||
ParameterPosition getPosition() { this.isParameterOf(_, result) }
|
||||
|
||||
predicate allowParameterReturnInSelf() { allowParameterReturnInSelfCached(this.asNode()) }
|
||||
}
|
||||
|
||||
private class RetNodeEx extends NodeEx {
|
||||
@@ -608,6 +606,38 @@ private predicate hasSinkCallCtx(Configuration config) {
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if flow from `p` to a return node of kind `kind` is allowed.
|
||||
*
|
||||
* We don't expect a parameter to return stored in itself, unless
|
||||
* explicitly allowed
|
||||
*/
|
||||
bindingset[p, kind]
|
||||
private predicate parameterFlowThroughAllowed(ParamNodeEx p, ReturnKindExt kind) {
|
||||
exists(ParameterPosition pos | p.isParameterOf(_, pos) |
|
||||
not kind.(ParamUpdateReturnKind).getPosition() = pos
|
||||
or
|
||||
allowParameterReturnInSelfCached(p.asNode())
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* 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;
|
||||
|
||||
@@ -981,21 +1011,22 @@ private module Stage1 implements StageSig {
|
||||
* candidate for the origin of a summary.
|
||||
*/
|
||||
pragma[nomagic]
|
||||
predicate parameterMayFlowThrough(ParamNodeEx p, DataFlowCallable c, Ap ap, Configuration config) {
|
||||
exists(ReturnKindExt kind |
|
||||
predicate parameterMayFlowThrough(ParamNodeEx p, Ap ap, Configuration config) {
|
||||
exists(DataFlowCallable c, ReturnKindExt kind |
|
||||
throughFlowNodeCand(p, config) and
|
||||
returnFlowCallableNodeCand(c, kind, config) and
|
||||
p.getEnclosingCallable() = c and
|
||||
exists(ap) and
|
||||
// we don't expect a parameter to return stored in itself, unless explicitly allowed
|
||||
(
|
||||
not kind.(ParamUpdateReturnKind).getPosition() = p.getPosition()
|
||||
or
|
||||
p.allowParameterReturnInSelf()
|
||||
)
|
||||
parameterFlowThroughAllowed(p, kind)
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
predicate returnMayFlowThrough(RetNodeEx ret, ReturnKindExt kind, Configuration config) {
|
||||
throughFlowNodeCand(ret, config) and
|
||||
kind = ret.getKind()
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
predicate callMayFlowThroughRev(DataFlowCall call, Configuration config) {
|
||||
exists(ArgNodeEx arg, boolean toReturn |
|
||||
@@ -1052,12 +1083,16 @@ private predicate viableReturnPosOutNodeCand1(
|
||||
*/
|
||||
pragma[nomagic]
|
||||
private predicate flowOutOfCallNodeCand1(
|
||||
DataFlowCall call, RetNodeEx ret, NodeEx out, Configuration config
|
||||
DataFlowCall call, RetNodeEx ret, ReturnKindExt kind, NodeEx out, Configuration config
|
||||
) {
|
||||
viableReturnPosOutNodeCand1(call, ret.getReturnPosition(), out, config) and
|
||||
Stage1::revFlow(ret, config) and
|
||||
not outBarrier(ret, config) and
|
||||
not inBarrier(out, config)
|
||||
exists(ReturnPosition pos |
|
||||
viableReturnPosOutNodeCand1(call, pos, out, config) and
|
||||
pos = ret.getReturnPosition() and
|
||||
kind = pos.getKind() and
|
||||
Stage1::revFlow(ret, config) and
|
||||
not outBarrier(ret, config) and
|
||||
not inBarrier(out, config)
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
@@ -1087,10 +1122,11 @@ private predicate flowIntoCallNodeCand1(
|
||||
* edge in the graph of paths between sources and sinks that ignores call
|
||||
* contexts.
|
||||
*/
|
||||
pragma[nomagic]
|
||||
private int branch(NodeEx n1, Configuration conf) {
|
||||
result =
|
||||
strictcount(NodeEx n |
|
||||
flowOutOfCallNodeCand1(_, n1, n, conf) or flowIntoCallNodeCand1(_, n1, n, conf)
|
||||
flowOutOfCallNodeCand1(_, n1, _, n, conf) or flowIntoCallNodeCand1(_, n1, n, conf)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1099,10 +1135,11 @@ private int branch(NodeEx n1, Configuration conf) {
|
||||
* edge in the graph of paths between sources and sinks that ignores call
|
||||
* contexts.
|
||||
*/
|
||||
pragma[nomagic]
|
||||
private int join(NodeEx n2, Configuration conf) {
|
||||
result =
|
||||
strictcount(NodeEx n |
|
||||
flowOutOfCallNodeCand1(_, n, n2, conf) or flowIntoCallNodeCand1(_, n, n2, conf)
|
||||
flowOutOfCallNodeCand1(_, n, _, n2, conf) or flowIntoCallNodeCand1(_, n, n2, conf)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1115,12 +1152,13 @@ private int join(NodeEx n2, Configuration conf) {
|
||||
*/
|
||||
pragma[nomagic]
|
||||
private predicate flowOutOfCallNodeCand1(
|
||||
DataFlowCall call, RetNodeEx ret, NodeEx out, boolean allowsFieldFlow, Configuration config
|
||||
DataFlowCall call, RetNodeEx ret, ReturnKindExt kind, NodeEx out, boolean allowsFieldFlow,
|
||||
Configuration config
|
||||
) {
|
||||
flowOutOfCallNodeCand1(call, ret, out, config) and
|
||||
flowOutOfCallNodeCand1(call, ret, kind, out, pragma[only_bind_into](config)) and
|
||||
exists(int b, int j |
|
||||
b = branch(ret, config) and
|
||||
j = join(out, config) and
|
||||
b = branch(ret, pragma[only_bind_into](config)) and
|
||||
j = join(out, pragma[only_bind_into](config)) and
|
||||
if b.minimum(j) <= config.fieldFlowBranchLimit()
|
||||
then allowsFieldFlow = true
|
||||
else allowsFieldFlow = false
|
||||
@@ -1136,10 +1174,10 @@ pragma[nomagic]
|
||||
private predicate flowIntoCallNodeCand1(
|
||||
DataFlowCall call, ArgNodeEx arg, ParamNodeEx p, boolean allowsFieldFlow, Configuration config
|
||||
) {
|
||||
flowIntoCallNodeCand1(call, arg, p, config) and
|
||||
flowIntoCallNodeCand1(call, arg, p, pragma[only_bind_into](config)) and
|
||||
exists(int b, int j |
|
||||
b = branch(arg, config) and
|
||||
j = join(p, config) and
|
||||
b = branch(arg, pragma[only_bind_into](config)) and
|
||||
j = join(p, pragma[only_bind_into](config)) and
|
||||
if b.minimum(j) <= config.fieldFlowBranchLimit()
|
||||
then allowsFieldFlow = true
|
||||
else allowsFieldFlow = false
|
||||
@@ -1156,7 +1194,9 @@ private signature module StageSig {
|
||||
|
||||
predicate callMayFlowThroughRev(DataFlowCall call, Configuration config);
|
||||
|
||||
predicate parameterMayFlowThrough(ParamNodeEx p, DataFlowCallable c, Ap ap, Configuration config);
|
||||
predicate parameterMayFlowThrough(ParamNodeEx p, Ap ap, Configuration config);
|
||||
|
||||
predicate returnMayFlowThrough(RetNodeEx ret, ReturnKindExt kind, Configuration config);
|
||||
|
||||
predicate storeStepCand(
|
||||
NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, DataFlowType contentType,
|
||||
@@ -1222,7 +1262,8 @@ private module MkStage<StageSig PrevStage> {
|
||||
);
|
||||
|
||||
predicate flowOutOfCall(
|
||||
DataFlowCall call, RetNodeEx ret, NodeEx out, boolean allowsFieldFlow, Configuration config
|
||||
DataFlowCall call, RetNodeEx ret, ReturnKindExt kind, NodeEx out, boolean allowsFieldFlow,
|
||||
Configuration config
|
||||
);
|
||||
|
||||
predicate flowIntoCall(
|
||||
@@ -1247,14 +1288,14 @@ private module MkStage<StageSig PrevStage> {
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate flowThroughOutOfCall(
|
||||
DataFlowCall call, CcCall ccc, RetNodeEx ret, NodeEx out, boolean allowsFieldFlow,
|
||||
Configuration config
|
||||
DataFlowCall call, DataFlowCallable c, CcCall ccc, RetNodeEx ret, ReturnKindExt kind,
|
||||
NodeEx out, boolean allowsFieldFlow, Configuration config
|
||||
) {
|
||||
flowOutOfCall(call, ret, out, allowsFieldFlow, pragma[only_bind_into](config)) and
|
||||
flowOutOfCall(call, ret, kind, out, allowsFieldFlow, pragma[only_bind_into](config)) and
|
||||
PrevStage::callMayFlowThroughRev(call, pragma[only_bind_into](config)) and
|
||||
PrevStage::parameterMayFlowThrough(_, ret.getEnclosingCallable(), _,
|
||||
pragma[only_bind_into](config)) and
|
||||
matchesCall(ccc, call)
|
||||
PrevStage::returnMayFlowThrough(ret, kind, pragma[only_bind_into](config)) and
|
||||
matchesCall(ccc, call) and
|
||||
c = ret.getEnclosingCallable()
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1262,29 +1303,32 @@ private module MkStage<StageSig PrevStage> {
|
||||
* configuration `config`.
|
||||
*
|
||||
* The call context `cc` records whether the node is reached through an
|
||||
* argument in a call, and if so, `argAp` records the access path of that
|
||||
* argument.
|
||||
* argument in a call, and if so, `summaryCtx` and `argAp` record the
|
||||
* corresponding parameter position and access path of that argument, respectively.
|
||||
*/
|
||||
pragma[nomagic]
|
||||
additional predicate fwdFlow(
|
||||
NodeEx node, FlowState state, Cc cc, ApOption argAp, Ap ap, Configuration config
|
||||
NodeEx node, FlowState state, Cc cc, ParameterPositionOption summaryCtx, ApOption argAp,
|
||||
Ap ap, Configuration config
|
||||
) {
|
||||
fwdFlow0(node, state, cc, argAp, ap, config) and
|
||||
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, ApOption argAp, Ap ap, Configuration config
|
||||
NodeEx node, FlowState state, Cc cc, ParameterPositionOption summaryCtx, ApOption argAp,
|
||||
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)
|
||||
or
|
||||
exists(NodeEx mid, FlowState state0, Ap ap0, LocalCc localCc |
|
||||
fwdFlow(mid, state0, cc, argAp, ap0, config) and
|
||||
fwdFlow(mid, state0, cc, summaryCtx, argAp, ap0, config) and
|
||||
localCc = getLocalCc(mid, cc)
|
||||
|
|
||||
localStep(mid, state0, node, state, true, _, config, localCc) and
|
||||
@@ -1295,65 +1339,82 @@ private module MkStage<StageSig PrevStage> {
|
||||
)
|
||||
or
|
||||
exists(NodeEx mid |
|
||||
fwdFlow(mid, pragma[only_bind_into](state), _, _, ap, 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
|
||||
argAp = apNone()
|
||||
)
|
||||
or
|
||||
exists(NodeEx mid, ApNil nil |
|
||||
fwdFlow(mid, state, _, _, nil, pragma[only_bind_into](config)) and
|
||||
fwdFlow(mid, state, _, _, _, nil, pragma[only_bind_into](config)) and
|
||||
additionalJumpStep(mid, node, config) and
|
||||
cc = ccNone() and
|
||||
summaryCtx = TParameterPositionNone() and
|
||||
argAp = apNone() and
|
||||
ap = getApNil(node)
|
||||
)
|
||||
or
|
||||
exists(NodeEx mid, FlowState state0, ApNil nil |
|
||||
fwdFlow(mid, state0, _, _, nil, pragma[only_bind_into](config)) and
|
||||
fwdFlow(mid, state0, _, _, _, nil, pragma[only_bind_into](config)) and
|
||||
additionalJumpStateStep(mid, state0, node, state, config) and
|
||||
cc = ccNone() and
|
||||
summaryCtx = TParameterPositionNone() and
|
||||
argAp = apNone() and
|
||||
ap = getApNil(node)
|
||||
)
|
||||
or
|
||||
// store
|
||||
exists(TypedContent tc, Ap ap0 |
|
||||
fwdFlowStore(_, ap0, tc, node, state, cc, argAp, config) and
|
||||
fwdFlowStore(_, ap0, tc, node, state, cc, summaryCtx, argAp, config) and
|
||||
ap = apCons(tc, ap0)
|
||||
)
|
||||
or
|
||||
// read
|
||||
exists(Ap ap0, Content c |
|
||||
fwdFlowRead(ap0, c, _, node, state, cc, argAp, config) and
|
||||
fwdFlowRead(ap0, c, _, node, state, cc, summaryCtx, argAp, config) and
|
||||
fwdFlowConsCand(ap0, c, ap, config)
|
||||
)
|
||||
or
|
||||
// flow into a callable
|
||||
exists(ApApprox apa |
|
||||
fwdFlowIn(_, node, state, _, cc, _, ap, config) and
|
||||
fwdFlowIn(_, node, state, _, cc, _, _, ap, config) and
|
||||
apa = getApprox(ap) and
|
||||
if PrevStage::parameterMayFlowThrough(node, _, apa, config)
|
||||
then argAp = apSome(ap)
|
||||
else argAp = apNone()
|
||||
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
|
||||
fwdFlowOutNotFromArg(node, state, cc, argAp, ap, config)
|
||||
exists(
|
||||
DataFlowCall call, RetNodeEx ret, boolean allowsFieldFlow, CcNoCall innercc,
|
||||
DataFlowCallable inner
|
||||
|
|
||||
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()
|
||||
)
|
||||
or
|
||||
exists(DataFlowCall call, Ap argAp0 |
|
||||
fwdFlowOutFromArg(call, node, state, argAp0, ap, config) and
|
||||
fwdFlowIsEntered(call, cc, argAp, argAp0, config)
|
||||
// flow through a callable
|
||||
exists(DataFlowCall call, ParameterPosition summaryCtx0, Ap argAp0 |
|
||||
fwdFlowOutFromArg(call, node, state, summaryCtx0, argAp0, ap, config) and
|
||||
fwdFlowIsEntered(call, cc, summaryCtx, argAp, summaryCtx0, argAp0, config)
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate fwdFlowStore(
|
||||
NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, FlowState state, Cc cc, ApOption argAp,
|
||||
Configuration config
|
||||
NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, FlowState state, Cc cc,
|
||||
ParameterPositionOption summaryCtx, ApOption argAp, Configuration config
|
||||
) {
|
||||
exists(DataFlowType contentType |
|
||||
fwdFlow(node1, state, cc, argAp, ap1, config) and
|
||||
fwdFlow(node1, state, cc, summaryCtx, argAp, ap1, config) and
|
||||
PrevStage::storeStepCand(node1, unbindApa(getApprox(ap1)), tc, node2, contentType, config) and
|
||||
typecheckStore(ap1, contentType)
|
||||
)
|
||||
@@ -1366,7 +1427,7 @@ private module MkStage<StageSig PrevStage> {
|
||||
pragma[nomagic]
|
||||
private predicate fwdFlowConsCand(Ap cons, Content c, Ap tail, Configuration config) {
|
||||
exists(TypedContent tc |
|
||||
fwdFlowStore(_, tail, tc, _, _, _, _, config) and
|
||||
fwdFlowStore(_, tail, tc, _, _, _, _, _, config) and
|
||||
tc.getContent() = c and
|
||||
cons = apCons(tc, tail)
|
||||
)
|
||||
@@ -1374,21 +1435,21 @@ private module MkStage<StageSig PrevStage> {
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate fwdFlowRead(
|
||||
Ap ap, Content c, NodeEx node1, NodeEx node2, FlowState state, Cc cc, ApOption argAp,
|
||||
Configuration config
|
||||
Ap ap, Content c, NodeEx node1, NodeEx node2, FlowState state, Cc cc,
|
||||
ParameterPositionOption summaryCtx, ApOption argAp, Configuration config
|
||||
) {
|
||||
fwdFlow(node1, state, cc, 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, Cc innercc, ApOption argAp,
|
||||
Ap ap, 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, argAp, ap, 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()
|
||||
@@ -1396,29 +1457,19 @@ private module MkStage<StageSig PrevStage> {
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate fwdFlowOutNotFromArg(
|
||||
NodeEx out, FlowState state, Cc ccOut, ApOption argAp, Ap ap, Configuration config
|
||||
private predicate fwdFlowOutFromArg(
|
||||
DataFlowCall call, NodeEx out, FlowState state, ParameterPosition summaryCtx, Ap argAp, Ap ap,
|
||||
Configuration config
|
||||
) {
|
||||
exists(
|
||||
DataFlowCall call, RetNodeEx ret, boolean allowsFieldFlow, CcNoCall innercc,
|
||||
DataFlowCallable inner
|
||||
DataFlowCallable c, RetNodeEx ret, ReturnKindExt kind, boolean allowsFieldFlow, CcCall ccc
|
||||
|
|
||||
fwdFlow(ret, state, innercc, argAp, ap, config) and
|
||||
flowOutOfCall(call, ret, out, allowsFieldFlow, config) and
|
||||
inner = ret.getEnclosingCallable() and
|
||||
ccOut = getCallContextReturn(inner, call, innercc) and
|
||||
if allowsFieldFlow = false then ap instanceof ApNil else any()
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate fwdFlowOutFromArg(
|
||||
DataFlowCall call, NodeEx out, FlowState state, Ap argAp, Ap ap, Configuration config
|
||||
) {
|
||||
exists(RetNodeEx ret, boolean allowsFieldFlow, CcCall ccc |
|
||||
fwdFlow(ret, state, ccc, apSome(argAp), ap, config) and
|
||||
flowThroughOutOfCall(call, ccc, ret, out, allowsFieldFlow, config) and
|
||||
if allowsFieldFlow = false then ap instanceof ApNil else any()
|
||||
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
|
||||
(if allowsFieldFlow = false then ap instanceof ApNil else any()) and
|
||||
parameterFlowThroughAllowed(c, pragma[only_bind_into](summaryCtx), kind)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1428,11 +1479,13 @@ private module MkStage<StageSig PrevStage> {
|
||||
*/
|
||||
pragma[nomagic]
|
||||
private predicate fwdFlowIsEntered(
|
||||
DataFlowCall call, Cc cc, ApOption argAp, Ap ap, Configuration config
|
||||
DataFlowCall call, Cc cc, ParameterPositionOption summaryCtx, ApOption argAp,
|
||||
ParameterPosition pos, Ap ap, Configuration config
|
||||
) {
|
||||
exists(ParamNodeEx p |
|
||||
fwdFlowIn(call, p, _, cc, _, argAp, ap, config) and
|
||||
PrevStage::parameterMayFlowThrough(p, _, unbindApa(getApprox(ap)), config)
|
||||
exists(ParamNodeEx param |
|
||||
fwdFlowIn(call, param, _, cc, _, summaryCtx, argAp, ap, config) and
|
||||
PrevStage::parameterMayFlowThrough(param, unbindApa(getApprox(ap)), config) and
|
||||
pos = param.getPosition()
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1440,27 +1493,40 @@ private module MkStage<StageSig PrevStage> {
|
||||
private predicate storeStepFwd(
|
||||
NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, Ap ap2, Configuration config
|
||||
) {
|
||||
fwdFlowStore(node1, ap1, tc, node2, _, _, _, config) and
|
||||
fwdFlowStore(node1, ap1, tc, node2, _, _, _, _, config) and
|
||||
ap2 = apCons(tc, ap1) and
|
||||
fwdFlowRead(ap2, tc.getContent(), _, _, _, _, _, config)
|
||||
fwdFlowRead(ap2, tc.getContent(), _, _, _, _, _, _, config)
|
||||
}
|
||||
|
||||
private predicate readStepFwd(
|
||||
NodeEx n1, Ap ap1, Content c, NodeEx n2, Ap ap2, Configuration config
|
||||
) {
|
||||
fwdFlowRead(ap1, c, n1, n2, _, _, _, config) and
|
||||
fwdFlowRead(ap1, c, n1, n2, _, _, _, _, config) and
|
||||
fwdFlowConsCand(ap1, c, ap2, config)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate callMayFlowThroughFwd(DataFlowCall call, Configuration config) {
|
||||
exists(Ap argAp0, NodeEx out, FlowState state, Cc cc, ApOption argAp, Ap ap |
|
||||
fwdFlow(out, state, pragma[only_bind_into](cc), pragma[only_bind_into](argAp), ap,
|
||||
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
|
||||
fwdFlowOutFromArg(call, out, state, argAp0, ap, config) and
|
||||
fwdFlowIsEntered(pragma[only_bind_into](call), pragma[only_bind_into](cc),
|
||||
pragma[only_bind_into](argAp), pragma[only_bind_into](argAp0),
|
||||
pragma[only_bind_into](config))
|
||||
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(DataFlowCallable c, ParameterPosition ppos |
|
||||
returnFlowsThrough0(ret, kind, state, ccc, c, ppos, argAp, ap, config) and
|
||||
p.isParameterOf(c, ppos) and
|
||||
parameterFlowThroughAllowed(p, kind)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1468,118 +1534,130 @@ private module MkStage<StageSig PrevStage> {
|
||||
private predicate flowThroughIntoCall(
|
||||
DataFlowCall call, ArgNodeEx arg, ParamNodeEx p, boolean allowsFieldFlow, Configuration config
|
||||
) {
|
||||
flowIntoCall(call, arg, p, allowsFieldFlow, config) and
|
||||
fwdFlow(arg, _, _, _, _, pragma[only_bind_into](config)) and
|
||||
PrevStage::parameterMayFlowThrough(p, _, _, pragma[only_bind_into](config)) and
|
||||
callMayFlowThroughFwd(call, pragma[only_bind_into](config))
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate returnNodeMayFlowThrough(
|
||||
RetNodeEx ret, FlowState state, Ap ap, Configuration config
|
||||
) {
|
||||
fwdFlow(ret, state, any(CcCall ccc), apSome(_), ap, config)
|
||||
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))
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `node` with access path `ap` is part of a path from a source to a
|
||||
* sink in the configuration `config`.
|
||||
*
|
||||
* The Boolean `toReturn` records whether the node must be returned from the
|
||||
* enclosing callable in order to reach a sink, and if so, `returnAp` records
|
||||
* the access path of the returned value.
|
||||
* The parameter `returnCtx` records whether (and how) the node must be returned
|
||||
* from the enclosing callable in order to reach a sink, and if so, `returnAp`
|
||||
* records the access path of the returned value.
|
||||
*/
|
||||
pragma[nomagic]
|
||||
additional predicate revFlow(
|
||||
NodeEx node, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, Configuration config
|
||||
NodeEx node, FlowState state, ReturnCtx returnCtx, ApOption returnAp, Ap ap,
|
||||
Configuration config
|
||||
) {
|
||||
revFlow0(node, state, toReturn, returnAp, ap, config) and
|
||||
fwdFlow(node, state, _, _, ap, config)
|
||||
revFlow0(node, state, returnCtx, returnAp, ap, config) and
|
||||
fwdFlow(node, state, _, _, _, ap, config)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate revFlow0(
|
||||
NodeEx node, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, Configuration config
|
||||
NodeEx node, FlowState state, ReturnCtx returnCtx, ApOption returnAp, Ap ap,
|
||||
Configuration config
|
||||
) {
|
||||
fwdFlow(node, state, _, _, ap, config) and
|
||||
fwdFlow(node, state, _, _, _, ap, config) and
|
||||
sinkNode(node, state, config) and
|
||||
(if hasSinkCallCtx(config) then toReturn = true else toReturn = false) and
|
||||
(
|
||||
if hasSinkCallCtx(config)
|
||||
then returnCtx = TReturnCtxNoFlowThrough()
|
||||
else returnCtx = TReturnCtxNone()
|
||||
) and
|
||||
returnAp = apNone() and
|
||||
ap instanceof ApNil
|
||||
or
|
||||
exists(NodeEx mid, FlowState state0 |
|
||||
localStep(node, state, mid, state0, true, _, config, _) and
|
||||
revFlow(mid, state0, toReturn, returnAp, ap, config)
|
||||
revFlow(mid, state0, returnCtx, returnAp, ap, config)
|
||||
)
|
||||
or
|
||||
exists(NodeEx mid, FlowState state0, ApNil nil |
|
||||
fwdFlow(node, pragma[only_bind_into](state), _, _, ap, pragma[only_bind_into](config)) and
|
||||
fwdFlow(node, pragma[only_bind_into](state), _, _, _, ap, pragma[only_bind_into](config)) and
|
||||
localStep(node, pragma[only_bind_into](state), mid, state0, false, _, config, _) and
|
||||
revFlow(mid, state0, toReturn, returnAp, nil, pragma[only_bind_into](config)) and
|
||||
revFlow(mid, state0, returnCtx, returnAp, nil, pragma[only_bind_into](config)) and
|
||||
ap instanceof ApNil
|
||||
)
|
||||
or
|
||||
exists(NodeEx mid |
|
||||
jumpStep(node, mid, config) and
|
||||
revFlow(mid, state, _, _, ap, config) and
|
||||
toReturn = false and
|
||||
returnCtx = TReturnCtxNone() and
|
||||
returnAp = apNone()
|
||||
)
|
||||
or
|
||||
exists(NodeEx mid, ApNil nil |
|
||||
fwdFlow(node, _, _, _, ap, pragma[only_bind_into](config)) and
|
||||
fwdFlow(node, _, _, _, _, ap, pragma[only_bind_into](config)) and
|
||||
additionalJumpStep(node, mid, config) and
|
||||
revFlow(pragma[only_bind_into](mid), state, _, _, nil, pragma[only_bind_into](config)) and
|
||||
toReturn = false and
|
||||
returnCtx = TReturnCtxNone() and
|
||||
returnAp = apNone() and
|
||||
ap instanceof ApNil
|
||||
)
|
||||
or
|
||||
exists(NodeEx mid, FlowState state0, ApNil nil |
|
||||
fwdFlow(node, _, _, _, ap, pragma[only_bind_into](config)) and
|
||||
fwdFlow(node, _, _, _, _, ap, pragma[only_bind_into](config)) and
|
||||
additionalJumpStateStep(node, state, mid, state0, config) and
|
||||
revFlow(pragma[only_bind_into](mid), pragma[only_bind_into](state0), _, _, nil,
|
||||
pragma[only_bind_into](config)) and
|
||||
toReturn = false and
|
||||
returnCtx = TReturnCtxNone() and
|
||||
returnAp = apNone() and
|
||||
ap instanceof ApNil
|
||||
)
|
||||
or
|
||||
// store
|
||||
exists(Ap ap0, Content c |
|
||||
revFlowStore(ap0, c, ap, node, state, _, _, toReturn, returnAp, config) and
|
||||
revFlowStore(ap0, c, ap, node, state, _, _, returnCtx, returnAp, config) and
|
||||
revFlowConsCand(ap0, c, ap, config)
|
||||
)
|
||||
or
|
||||
// read
|
||||
exists(NodeEx mid, Ap ap0 |
|
||||
revFlow(mid, state, toReturn, returnAp, ap0, config) and
|
||||
revFlow(mid, state, returnCtx, returnAp, ap0, config) and
|
||||
readStepFwd(node, ap, _, mid, ap0, config)
|
||||
)
|
||||
or
|
||||
// flow into a callable
|
||||
revFlowInNotToReturn(node, state, returnAp, ap, config) and
|
||||
toReturn = false
|
||||
exists(ParamNodeEx p, boolean allowsFieldFlow |
|
||||
revFlow(p, state, TReturnCtxNone(), returnAp, ap, config) and
|
||||
flowIntoCall(_, node, p, allowsFieldFlow, config) and
|
||||
(if allowsFieldFlow = false then ap instanceof ApNil else any()) and
|
||||
returnCtx = TReturnCtxNone()
|
||||
)
|
||||
or
|
||||
exists(DataFlowCall call, Ap returnAp0 |
|
||||
revFlowInToReturn(call, node, state, returnAp0, ap, config) and
|
||||
revFlowIsReturned(call, toReturn, returnAp, returnAp0, config)
|
||||
// flow through a callable
|
||||
exists(DataFlowCall call, ReturnKindExt returnKind0, Ap returnAp0 |
|
||||
revFlowInToReturn(call, node, state, returnKind0, returnAp0, ap, config) and
|
||||
revFlowIsReturned(call, returnCtx, returnAp, returnKind0, returnAp0, config)
|
||||
)
|
||||
or
|
||||
// flow out of a callable
|
||||
revFlowOut(_, node, state, _, _, ap, config) and
|
||||
toReturn = true and
|
||||
if returnNodeMayFlowThrough(node, state, ap, config)
|
||||
then returnAp = apSome(ap)
|
||||
else returnAp = apNone()
|
||||
exists(ReturnKindExt kind |
|
||||
revFlowOut(_, node, kind, state, _, _, ap, config) and
|
||||
if returnFlowsThrough(node, kind, state, _, _, _, ap, config)
|
||||
then (
|
||||
returnCtx = TReturnCtxMaybeFlowThrough(kind) and
|
||||
returnAp = apSome(ap)
|
||||
) else (
|
||||
returnCtx = TReturnCtxNoFlowThrough() and returnAp = apNone()
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate revFlowStore(
|
||||
Ap ap0, Content c, Ap ap, NodeEx node, FlowState state, TypedContent tc, NodeEx mid,
|
||||
boolean toReturn, ApOption returnAp, Configuration config
|
||||
ReturnCtx returnCtx, ApOption returnAp, Configuration config
|
||||
) {
|
||||
revFlow(mid, state, toReturn, returnAp, ap0, config) and
|
||||
revFlow(mid, state, returnCtx, returnAp, ap0, config) and
|
||||
storeStepFwd(node, ap, tc, mid, ap0, config) and
|
||||
tc.getContent() = c
|
||||
}
|
||||
@@ -1599,35 +1677,27 @@ private module MkStage<StageSig PrevStage> {
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate revFlowOut(
|
||||
DataFlowCall call, RetNodeEx ret, FlowState state, boolean toReturn, ApOption returnAp, Ap ap,
|
||||
Configuration config
|
||||
DataFlowCall call, RetNodeEx ret, ReturnKindExt kind, FlowState state, ReturnCtx returnCtx,
|
||||
ApOption returnAp, Ap ap, Configuration config
|
||||
) {
|
||||
exists(NodeEx out, boolean allowsFieldFlow |
|
||||
revFlow(out, state, toReturn, returnAp, ap, config) and
|
||||
flowOutOfCall(call, ret, out, allowsFieldFlow, config) and
|
||||
if allowsFieldFlow = false then ap instanceof ApNil else any()
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate revFlowInNotToReturn(
|
||||
ArgNodeEx arg, FlowState state, ApOption returnAp, Ap ap, Configuration config
|
||||
) {
|
||||
exists(ParamNodeEx p, boolean allowsFieldFlow |
|
||||
revFlow(p, state, false, returnAp, ap, config) and
|
||||
flowIntoCall(_, arg, p, allowsFieldFlow, config) and
|
||||
revFlow(out, state, returnCtx, returnAp, ap, config) and
|
||||
flowOutOfCall(call, ret, kind, out, allowsFieldFlow, config) and
|
||||
if allowsFieldFlow = false then ap instanceof ApNil else any()
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate revFlowInToReturn(
|
||||
DataFlowCall call, ArgNodeEx arg, FlowState state, Ap returnAp, Ap ap, Configuration config
|
||||
DataFlowCall call, ArgNodeEx arg, FlowState state, ReturnKindExt kind, Ap returnAp, Ap ap,
|
||||
Configuration config
|
||||
) {
|
||||
exists(ParamNodeEx p, boolean allowsFieldFlow |
|
||||
revFlow(p, state, true, apSome(returnAp), ap, config) and
|
||||
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()
|
||||
(if allowsFieldFlow = false then ap instanceof ApNil else any()) and
|
||||
parameterFlowThroughAllowed(p, kind)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1638,11 +1708,12 @@ private module MkStage<StageSig PrevStage> {
|
||||
*/
|
||||
pragma[nomagic]
|
||||
private predicate revFlowIsReturned(
|
||||
DataFlowCall call, boolean toReturn, ApOption returnAp, Ap ap, Configuration config
|
||||
DataFlowCall call, ReturnCtx returnCtx, ApOption returnAp, ReturnKindExt kind, Ap ap,
|
||||
Configuration config
|
||||
) {
|
||||
exists(RetNodeEx ret, FlowState state, CcCall ccc |
|
||||
revFlowOut(call, ret, state, toReturn, returnAp, ap, config) and
|
||||
fwdFlow(ret, state, ccc, apSome(_), ap, config) and
|
||||
revFlowOut(call, ret, kind, state, returnCtx, returnAp, ap, config) and
|
||||
returnFlowsThrough(ret, kind, state, ccc, _, _, ap, config) and
|
||||
matchesCall(ccc, call)
|
||||
)
|
||||
}
|
||||
@@ -1713,40 +1784,39 @@ private module MkStage<StageSig PrevStage> {
|
||||
validAp(ap, config)
|
||||
}
|
||||
|
||||
pragma[noinline]
|
||||
private predicate parameterFlow(
|
||||
ParamNodeEx p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config
|
||||
pragma[nomagic]
|
||||
private predicate parameterFlowsThroughRev(
|
||||
ParamNodeEx p, Ap ap, ReturnKindExt kind, Configuration config
|
||||
) {
|
||||
revFlow(p, _, true, apSome(ap0), ap, config) and
|
||||
c = p.getEnclosingCallable()
|
||||
revFlow(p, _, TReturnCtxMaybeFlowThrough(kind), apSome(_), ap, config) and
|
||||
parameterFlowThroughAllowed(p, kind)
|
||||
}
|
||||
|
||||
predicate parameterMayFlowThrough(ParamNodeEx p, DataFlowCallable c, Ap ap, Configuration config) {
|
||||
exists(RetNodeEx ret, FlowState state, Ap ap0, ReturnKindExt kind, ParameterPosition pos |
|
||||
parameterFlow(p, ap, ap0, c, config) and
|
||||
c = ret.getEnclosingCallable() and
|
||||
revFlow(pragma[only_bind_into](ret), pragma[only_bind_into](state), true, apSome(_),
|
||||
pragma[only_bind_into](ap0), pragma[only_bind_into](config)) and
|
||||
fwdFlow(ret, state, any(CcCall ccc), apSome(ap), ap0, config) and
|
||||
kind = ret.getKind() and
|
||||
p.getPosition() = pos and
|
||||
// we don't expect a parameter to return stored in itself, unless explicitly allowed
|
||||
(
|
||||
not kind.(ParamUpdateReturnKind).getPosition() = pos
|
||||
or
|
||||
p.allowParameterReturnInSelf()
|
||||
)
|
||||
pragma[nomagic]
|
||||
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)
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
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(
|
||||
Ap returnAp0, ArgNodeEx arg, FlowState state, boolean toReturn, ApOption returnAp, Ap ap
|
||||
ReturnKindExt returnKind0, Ap returnAp0, ArgNodeEx arg, FlowState state,
|
||||
ReturnCtx returnCtx, ApOption returnAp, Ap ap
|
||||
|
|
||||
revFlow(arg, state, toReturn, returnAp, ap, config) and
|
||||
revFlowInToReturn(call, arg, state, returnAp0, ap, config) and
|
||||
revFlowIsReturned(call, toReturn, returnAp, returnAp0, config)
|
||||
revFlow(arg, state, returnCtx, returnAp, ap, config) and
|
||||
revFlowInToReturn(call, arg, state, returnKind0, returnAp0, ap, config) and
|
||||
revFlowIsReturned(call, returnCtx, returnAp, returnKind0, returnAp0, config)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1754,14 +1824,13 @@ private module MkStage<StageSig PrevStage> {
|
||||
boolean fwd, int nodes, int fields, int conscand, int states, int tuples, Configuration config
|
||||
) {
|
||||
fwd = true and
|
||||
nodes = count(NodeEx node | fwdFlow(node, _, _, _, _, config)) and
|
||||
nodes = count(NodeEx node | fwdFlow(node, _, _, _, _, _, config)) and
|
||||
fields = count(TypedContent f0 | fwdConsCand(f0, _, config)) and
|
||||
conscand = count(TypedContent f0, Ap ap | fwdConsCand(f0, ap, config)) and
|
||||
states = count(FlowState state | fwdFlow(_, state, _, _, _, config)) and
|
||||
states = count(FlowState state | fwdFlow(_, state, _, _, _, _, config)) and
|
||||
tuples =
|
||||
count(NodeEx n, FlowState state, Cc cc, ApOption argAp, Ap ap |
|
||||
fwdFlow(n, state, cc, argAp, ap, config)
|
||||
)
|
||||
count(NodeEx n, FlowState state, Cc cc, ParameterPositionOption summaryCtx, ApOption argAp,
|
||||
Ap ap | fwdFlow(n, state, cc, summaryCtx, argAp, ap, config))
|
||||
or
|
||||
fwd = false and
|
||||
nodes = count(NodeEx node | revFlow(node, _, _, _, _, config)) and
|
||||
@@ -1769,8 +1838,8 @@ private module MkStage<StageSig PrevStage> {
|
||||
conscand = count(TypedContent f0, Ap ap | consCand(f0, ap, config)) and
|
||||
states = count(FlowState state | revFlow(_, state, _, _, _, config)) and
|
||||
tuples =
|
||||
count(NodeEx n, FlowState state, boolean b, ApOption retAp, Ap ap |
|
||||
revFlow(n, state, b, retAp, ap, config)
|
||||
count(NodeEx n, FlowState state, ReturnCtx returnCtx, ApOption retAp, Ap ap |
|
||||
revFlow(n, state, returnCtx, retAp, ap, config)
|
||||
)
|
||||
}
|
||||
/* End: Stage logic. */
|
||||
@@ -1915,7 +1984,7 @@ private module Stage2Param implements MkStage<Stage1>::StageParam {
|
||||
exists(lcc)
|
||||
}
|
||||
|
||||
predicate flowOutOfCall = flowOutOfCallNodeCand1/5;
|
||||
predicate flowOutOfCall = flowOutOfCallNodeCand1/6;
|
||||
|
||||
predicate flowIntoCall = flowIntoCallNodeCand1/5;
|
||||
|
||||
@@ -1951,9 +2020,10 @@ private module Stage2 implements StageSig {
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate flowOutOfCallNodeCand2(
|
||||
DataFlowCall call, RetNodeEx node1, NodeEx node2, boolean allowsFieldFlow, Configuration config
|
||||
DataFlowCall call, RetNodeEx node1, ReturnKindExt kind, NodeEx node2, boolean allowsFieldFlow,
|
||||
Configuration config
|
||||
) {
|
||||
flowOutOfCallNodeCand1(call, node1, node2, allowsFieldFlow, config) and
|
||||
flowOutOfCallNodeCand1(call, node1, kind, node2, allowsFieldFlow, config) and
|
||||
Stage2::revFlow(node2, pragma[only_bind_into](config)) and
|
||||
Stage2::revFlowAlias(node1, pragma[only_bind_into](config))
|
||||
}
|
||||
@@ -2021,8 +2091,8 @@ private module LocalFlowBigStep {
|
||||
exists(NodeEx next | Stage2::revFlow(next, state, config) |
|
||||
jumpStep(node, next, config) or
|
||||
additionalJumpStep(node, next, config) or
|
||||
flowIntoCallNodeCand1(_, node, next, config) or
|
||||
flowOutOfCallNodeCand1(_, node, next, config) or
|
||||
flowIntoCallNodeCand2(_, node, next, _, config) or
|
||||
flowOutOfCallNodeCand2(_, node, _, next, _, config) or
|
||||
Stage2::storeStepCand(node, _, _, next, _, config) or
|
||||
Stage2::readStepCand(node, _, next, config)
|
||||
)
|
||||
@@ -2163,7 +2233,7 @@ private module Stage3Param implements MkStage<Stage2>::StageParam {
|
||||
localFlowBigStep(node1, state1, node2, state2, preservesValue, ap, config, _) and exists(lcc)
|
||||
}
|
||||
|
||||
predicate flowOutOfCall = flowOutOfCallNodeCand2/5;
|
||||
predicate flowOutOfCall = flowOutOfCallNodeCand2/6;
|
||||
|
||||
predicate flowIntoCall = flowIntoCallNodeCand2/5;
|
||||
|
||||
@@ -2233,8 +2303,9 @@ private predicate flowCandSummaryCtx(
|
||||
NodeEx node, FlowState state, AccessPathFront argApf, Configuration config
|
||||
) {
|
||||
exists(AccessPathFront apf |
|
||||
Stage3::revFlow(node, state, true, _, apf, config) and
|
||||
Stage3::fwdFlow(node, state, any(Stage3::CcCall ccc), TAccessPathFrontSome(argApf), apf, config)
|
||||
Stage3::revFlow(node, state, TReturnCtxMaybeFlowThrough(_), _, apf, config) and
|
||||
Stage3::fwdFlow(node, state, any(Stage3::CcCall ccc), _, TAccessPathFrontSome(argApf), apf,
|
||||
config)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -2468,10 +2539,11 @@ private module Stage4Param implements MkStage<Stage3>::StageParam {
|
||||
|
||||
pragma[nomagic]
|
||||
predicate flowOutOfCall(
|
||||
DataFlowCall call, RetNodeEx node1, NodeEx node2, boolean allowsFieldFlow, Configuration config
|
||||
DataFlowCall call, RetNodeEx node1, ReturnKindExt kind, NodeEx node2, boolean allowsFieldFlow,
|
||||
Configuration config
|
||||
) {
|
||||
exists(FlowState state |
|
||||
flowOutOfCallNodeCand2(call, node1, node2, allowsFieldFlow, config) and
|
||||
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))
|
||||
@@ -2508,13 +2580,14 @@ private Configuration unbindConf(Configuration conf) {
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate nodeMayUseSummary0(
|
||||
NodeEx n, DataFlowCallable c, FlowState state, AccessPathApprox apa, Configuration config
|
||||
NodeEx n, DataFlowCallable c, ParameterPosition pos, FlowState state, AccessPathApprox apa,
|
||||
Configuration config
|
||||
) {
|
||||
exists(AccessPathApprox apa0 |
|
||||
Stage4::parameterMayFlowThrough(_, c, _, _) and
|
||||
Stage4::revFlow(n, state, true, _, apa0, config) and
|
||||
Stage4::fwdFlow(n, state, any(CallContextCall ccc), TAccessPathApproxSome(apa), apa0, config) and
|
||||
n.getEnclosingCallable() = c
|
||||
c = n.getEnclosingCallable() and
|
||||
Stage4::revFlow(n, state, TReturnCtxMaybeFlowThrough(_), _, apa0, config) and
|
||||
Stage4::fwdFlow(n, state, any(CallContextCall ccc), TParameterPositionSome(pos),
|
||||
TAccessPathApproxSome(apa), apa0, config)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -2522,9 +2595,10 @@ pragma[nomagic]
|
||||
private predicate nodeMayUseSummary(
|
||||
NodeEx n, FlowState state, AccessPathApprox apa, Configuration config
|
||||
) {
|
||||
exists(DataFlowCallable c |
|
||||
Stage4::parameterMayFlowThrough(_, c, apa, config) and
|
||||
nodeMayUseSummary0(n, c, state, apa, config)
|
||||
exists(DataFlowCallable c, ParameterPosition pos, ParamNodeEx p |
|
||||
Stage4::parameterMayFlowThrough(p, apa, config) and
|
||||
nodeMayUseSummary0(n, c, pos, state, apa, config) and
|
||||
p.isParameterOf(c, pos)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -2532,7 +2606,7 @@ private newtype TSummaryCtx =
|
||||
TSummaryCtxNone() or
|
||||
TSummaryCtxSome(ParamNodeEx p, FlowState state, AccessPath ap) {
|
||||
exists(Configuration config |
|
||||
Stage4::parameterMayFlowThrough(p, _, ap.getApprox(), config) and
|
||||
Stage4::parameterMayFlowThrough(p, ap.getApprox(), config) and
|
||||
Stage4::revFlow(p, state, _, config)
|
||||
)
|
||||
}
|
||||
@@ -3453,17 +3527,11 @@ private predicate paramFlowsThrough(
|
||||
ReturnKindExt kind, FlowState state, CallContextCall cc, SummaryCtxSome sc, AccessPath ap,
|
||||
AccessPathApprox apa, Configuration config
|
||||
) {
|
||||
exists(PathNodeMid mid, RetNodeEx ret, ParameterPosition pos |
|
||||
exists(PathNodeMid mid, RetNodeEx ret |
|
||||
pathNode(mid, ret, state, cc, sc, ap, config, _) and
|
||||
kind = ret.getKind() and
|
||||
apa = ap.getApprox() and
|
||||
pos = sc.getParameterPos() and
|
||||
// we don't expect a parameter to return stored in itself, unless explicitly allowed
|
||||
(
|
||||
not kind.(ParamUpdateReturnKind).getPosition() = pos
|
||||
or
|
||||
sc.getParamNode().allowParameterReturnInSelf()
|
||||
)
|
||||
parameterFlowThroughAllowed(sc.getParamNode(), kind)
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@@ -70,7 +70,7 @@ abstract class Configuration extends string {
|
||||
/**
|
||||
* Holds if `sink` is a relevant data flow sink accepting `state`.
|
||||
*/
|
||||
predicate isSink(Node source, FlowState state) { none() }
|
||||
predicate isSink(Node sink, FlowState state) { none() }
|
||||
|
||||
/**
|
||||
* Holds if data flow through `node` is prohibited. This completely removes
|
||||
@@ -319,8 +319,6 @@ private class ParamNodeEx extends NodeEx {
|
||||
}
|
||||
|
||||
ParameterPosition getPosition() { this.isParameterOf(_, result) }
|
||||
|
||||
predicate allowParameterReturnInSelf() { allowParameterReturnInSelfCached(this.asNode()) }
|
||||
}
|
||||
|
||||
private class RetNodeEx extends NodeEx {
|
||||
@@ -608,6 +606,38 @@ private predicate hasSinkCallCtx(Configuration config) {
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if flow from `p` to a return node of kind `kind` is allowed.
|
||||
*
|
||||
* We don't expect a parameter to return stored in itself, unless
|
||||
* explicitly allowed
|
||||
*/
|
||||
bindingset[p, kind]
|
||||
private predicate parameterFlowThroughAllowed(ParamNodeEx p, ReturnKindExt kind) {
|
||||
exists(ParameterPosition pos | p.isParameterOf(_, pos) |
|
||||
not kind.(ParamUpdateReturnKind).getPosition() = pos
|
||||
or
|
||||
allowParameterReturnInSelfCached(p.asNode())
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* 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;
|
||||
|
||||
@@ -981,21 +1011,22 @@ private module Stage1 implements StageSig {
|
||||
* candidate for the origin of a summary.
|
||||
*/
|
||||
pragma[nomagic]
|
||||
predicate parameterMayFlowThrough(ParamNodeEx p, DataFlowCallable c, Ap ap, Configuration config) {
|
||||
exists(ReturnKindExt kind |
|
||||
predicate parameterMayFlowThrough(ParamNodeEx p, Ap ap, Configuration config) {
|
||||
exists(DataFlowCallable c, ReturnKindExt kind |
|
||||
throughFlowNodeCand(p, config) and
|
||||
returnFlowCallableNodeCand(c, kind, config) and
|
||||
p.getEnclosingCallable() = c and
|
||||
exists(ap) and
|
||||
// we don't expect a parameter to return stored in itself, unless explicitly allowed
|
||||
(
|
||||
not kind.(ParamUpdateReturnKind).getPosition() = p.getPosition()
|
||||
or
|
||||
p.allowParameterReturnInSelf()
|
||||
)
|
||||
parameterFlowThroughAllowed(p, kind)
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
predicate returnMayFlowThrough(RetNodeEx ret, ReturnKindExt kind, Configuration config) {
|
||||
throughFlowNodeCand(ret, config) and
|
||||
kind = ret.getKind()
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
predicate callMayFlowThroughRev(DataFlowCall call, Configuration config) {
|
||||
exists(ArgNodeEx arg, boolean toReturn |
|
||||
@@ -1052,12 +1083,16 @@ private predicate viableReturnPosOutNodeCand1(
|
||||
*/
|
||||
pragma[nomagic]
|
||||
private predicate flowOutOfCallNodeCand1(
|
||||
DataFlowCall call, RetNodeEx ret, NodeEx out, Configuration config
|
||||
DataFlowCall call, RetNodeEx ret, ReturnKindExt kind, NodeEx out, Configuration config
|
||||
) {
|
||||
viableReturnPosOutNodeCand1(call, ret.getReturnPosition(), out, config) and
|
||||
Stage1::revFlow(ret, config) and
|
||||
not outBarrier(ret, config) and
|
||||
not inBarrier(out, config)
|
||||
exists(ReturnPosition pos |
|
||||
viableReturnPosOutNodeCand1(call, pos, out, config) and
|
||||
pos = ret.getReturnPosition() and
|
||||
kind = pos.getKind() and
|
||||
Stage1::revFlow(ret, config) and
|
||||
not outBarrier(ret, config) and
|
||||
not inBarrier(out, config)
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
@@ -1087,10 +1122,11 @@ private predicate flowIntoCallNodeCand1(
|
||||
* edge in the graph of paths between sources and sinks that ignores call
|
||||
* contexts.
|
||||
*/
|
||||
pragma[nomagic]
|
||||
private int branch(NodeEx n1, Configuration conf) {
|
||||
result =
|
||||
strictcount(NodeEx n |
|
||||
flowOutOfCallNodeCand1(_, n1, n, conf) or flowIntoCallNodeCand1(_, n1, n, conf)
|
||||
flowOutOfCallNodeCand1(_, n1, _, n, conf) or flowIntoCallNodeCand1(_, n1, n, conf)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1099,10 +1135,11 @@ private int branch(NodeEx n1, Configuration conf) {
|
||||
* edge in the graph of paths between sources and sinks that ignores call
|
||||
* contexts.
|
||||
*/
|
||||
pragma[nomagic]
|
||||
private int join(NodeEx n2, Configuration conf) {
|
||||
result =
|
||||
strictcount(NodeEx n |
|
||||
flowOutOfCallNodeCand1(_, n, n2, conf) or flowIntoCallNodeCand1(_, n, n2, conf)
|
||||
flowOutOfCallNodeCand1(_, n, _, n2, conf) or flowIntoCallNodeCand1(_, n, n2, conf)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1115,12 +1152,13 @@ private int join(NodeEx n2, Configuration conf) {
|
||||
*/
|
||||
pragma[nomagic]
|
||||
private predicate flowOutOfCallNodeCand1(
|
||||
DataFlowCall call, RetNodeEx ret, NodeEx out, boolean allowsFieldFlow, Configuration config
|
||||
DataFlowCall call, RetNodeEx ret, ReturnKindExt kind, NodeEx out, boolean allowsFieldFlow,
|
||||
Configuration config
|
||||
) {
|
||||
flowOutOfCallNodeCand1(call, ret, out, config) and
|
||||
flowOutOfCallNodeCand1(call, ret, kind, out, pragma[only_bind_into](config)) and
|
||||
exists(int b, int j |
|
||||
b = branch(ret, config) and
|
||||
j = join(out, config) and
|
||||
b = branch(ret, pragma[only_bind_into](config)) and
|
||||
j = join(out, pragma[only_bind_into](config)) and
|
||||
if b.minimum(j) <= config.fieldFlowBranchLimit()
|
||||
then allowsFieldFlow = true
|
||||
else allowsFieldFlow = false
|
||||
@@ -1136,10 +1174,10 @@ pragma[nomagic]
|
||||
private predicate flowIntoCallNodeCand1(
|
||||
DataFlowCall call, ArgNodeEx arg, ParamNodeEx p, boolean allowsFieldFlow, Configuration config
|
||||
) {
|
||||
flowIntoCallNodeCand1(call, arg, p, config) and
|
||||
flowIntoCallNodeCand1(call, arg, p, pragma[only_bind_into](config)) and
|
||||
exists(int b, int j |
|
||||
b = branch(arg, config) and
|
||||
j = join(p, config) and
|
||||
b = branch(arg, pragma[only_bind_into](config)) and
|
||||
j = join(p, pragma[only_bind_into](config)) and
|
||||
if b.minimum(j) <= config.fieldFlowBranchLimit()
|
||||
then allowsFieldFlow = true
|
||||
else allowsFieldFlow = false
|
||||
@@ -1156,7 +1194,9 @@ private signature module StageSig {
|
||||
|
||||
predicate callMayFlowThroughRev(DataFlowCall call, Configuration config);
|
||||
|
||||
predicate parameterMayFlowThrough(ParamNodeEx p, DataFlowCallable c, Ap ap, Configuration config);
|
||||
predicate parameterMayFlowThrough(ParamNodeEx p, Ap ap, Configuration config);
|
||||
|
||||
predicate returnMayFlowThrough(RetNodeEx ret, ReturnKindExt kind, Configuration config);
|
||||
|
||||
predicate storeStepCand(
|
||||
NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, DataFlowType contentType,
|
||||
@@ -1222,7 +1262,8 @@ private module MkStage<StageSig PrevStage> {
|
||||
);
|
||||
|
||||
predicate flowOutOfCall(
|
||||
DataFlowCall call, RetNodeEx ret, NodeEx out, boolean allowsFieldFlow, Configuration config
|
||||
DataFlowCall call, RetNodeEx ret, ReturnKindExt kind, NodeEx out, boolean allowsFieldFlow,
|
||||
Configuration config
|
||||
);
|
||||
|
||||
predicate flowIntoCall(
|
||||
@@ -1247,14 +1288,14 @@ private module MkStage<StageSig PrevStage> {
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate flowThroughOutOfCall(
|
||||
DataFlowCall call, CcCall ccc, RetNodeEx ret, NodeEx out, boolean allowsFieldFlow,
|
||||
Configuration config
|
||||
DataFlowCall call, DataFlowCallable c, CcCall ccc, RetNodeEx ret, ReturnKindExt kind,
|
||||
NodeEx out, boolean allowsFieldFlow, Configuration config
|
||||
) {
|
||||
flowOutOfCall(call, ret, out, allowsFieldFlow, pragma[only_bind_into](config)) and
|
||||
flowOutOfCall(call, ret, kind, out, allowsFieldFlow, pragma[only_bind_into](config)) and
|
||||
PrevStage::callMayFlowThroughRev(call, pragma[only_bind_into](config)) and
|
||||
PrevStage::parameterMayFlowThrough(_, ret.getEnclosingCallable(), _,
|
||||
pragma[only_bind_into](config)) and
|
||||
matchesCall(ccc, call)
|
||||
PrevStage::returnMayFlowThrough(ret, kind, pragma[only_bind_into](config)) and
|
||||
matchesCall(ccc, call) and
|
||||
c = ret.getEnclosingCallable()
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1262,29 +1303,32 @@ private module MkStage<StageSig PrevStage> {
|
||||
* configuration `config`.
|
||||
*
|
||||
* The call context `cc` records whether the node is reached through an
|
||||
* argument in a call, and if so, `argAp` records the access path of that
|
||||
* argument.
|
||||
* argument in a call, and if so, `summaryCtx` and `argAp` record the
|
||||
* corresponding parameter position and access path of that argument, respectively.
|
||||
*/
|
||||
pragma[nomagic]
|
||||
additional predicate fwdFlow(
|
||||
NodeEx node, FlowState state, Cc cc, ApOption argAp, Ap ap, Configuration config
|
||||
NodeEx node, FlowState state, Cc cc, ParameterPositionOption summaryCtx, ApOption argAp,
|
||||
Ap ap, Configuration config
|
||||
) {
|
||||
fwdFlow0(node, state, cc, argAp, ap, config) and
|
||||
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, ApOption argAp, Ap ap, Configuration config
|
||||
NodeEx node, FlowState state, Cc cc, ParameterPositionOption summaryCtx, ApOption argAp,
|
||||
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)
|
||||
or
|
||||
exists(NodeEx mid, FlowState state0, Ap ap0, LocalCc localCc |
|
||||
fwdFlow(mid, state0, cc, argAp, ap0, config) and
|
||||
fwdFlow(mid, state0, cc, summaryCtx, argAp, ap0, config) and
|
||||
localCc = getLocalCc(mid, cc)
|
||||
|
|
||||
localStep(mid, state0, node, state, true, _, config, localCc) and
|
||||
@@ -1295,65 +1339,82 @@ private module MkStage<StageSig PrevStage> {
|
||||
)
|
||||
or
|
||||
exists(NodeEx mid |
|
||||
fwdFlow(mid, pragma[only_bind_into](state), _, _, ap, 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
|
||||
argAp = apNone()
|
||||
)
|
||||
or
|
||||
exists(NodeEx mid, ApNil nil |
|
||||
fwdFlow(mid, state, _, _, nil, pragma[only_bind_into](config)) and
|
||||
fwdFlow(mid, state, _, _, _, nil, pragma[only_bind_into](config)) and
|
||||
additionalJumpStep(mid, node, config) and
|
||||
cc = ccNone() and
|
||||
summaryCtx = TParameterPositionNone() and
|
||||
argAp = apNone() and
|
||||
ap = getApNil(node)
|
||||
)
|
||||
or
|
||||
exists(NodeEx mid, FlowState state0, ApNil nil |
|
||||
fwdFlow(mid, state0, _, _, nil, pragma[only_bind_into](config)) and
|
||||
fwdFlow(mid, state0, _, _, _, nil, pragma[only_bind_into](config)) and
|
||||
additionalJumpStateStep(mid, state0, node, state, config) and
|
||||
cc = ccNone() and
|
||||
summaryCtx = TParameterPositionNone() and
|
||||
argAp = apNone() and
|
||||
ap = getApNil(node)
|
||||
)
|
||||
or
|
||||
// store
|
||||
exists(TypedContent tc, Ap ap0 |
|
||||
fwdFlowStore(_, ap0, tc, node, state, cc, argAp, config) and
|
||||
fwdFlowStore(_, ap0, tc, node, state, cc, summaryCtx, argAp, config) and
|
||||
ap = apCons(tc, ap0)
|
||||
)
|
||||
or
|
||||
// read
|
||||
exists(Ap ap0, Content c |
|
||||
fwdFlowRead(ap0, c, _, node, state, cc, argAp, config) and
|
||||
fwdFlowRead(ap0, c, _, node, state, cc, summaryCtx, argAp, config) and
|
||||
fwdFlowConsCand(ap0, c, ap, config)
|
||||
)
|
||||
or
|
||||
// flow into a callable
|
||||
exists(ApApprox apa |
|
||||
fwdFlowIn(_, node, state, _, cc, _, ap, config) and
|
||||
fwdFlowIn(_, node, state, _, cc, _, _, ap, config) and
|
||||
apa = getApprox(ap) and
|
||||
if PrevStage::parameterMayFlowThrough(node, _, apa, config)
|
||||
then argAp = apSome(ap)
|
||||
else argAp = apNone()
|
||||
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
|
||||
fwdFlowOutNotFromArg(node, state, cc, argAp, ap, config)
|
||||
exists(
|
||||
DataFlowCall call, RetNodeEx ret, boolean allowsFieldFlow, CcNoCall innercc,
|
||||
DataFlowCallable inner
|
||||
|
|
||||
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()
|
||||
)
|
||||
or
|
||||
exists(DataFlowCall call, Ap argAp0 |
|
||||
fwdFlowOutFromArg(call, node, state, argAp0, ap, config) and
|
||||
fwdFlowIsEntered(call, cc, argAp, argAp0, config)
|
||||
// flow through a callable
|
||||
exists(DataFlowCall call, ParameterPosition summaryCtx0, Ap argAp0 |
|
||||
fwdFlowOutFromArg(call, node, state, summaryCtx0, argAp0, ap, config) and
|
||||
fwdFlowIsEntered(call, cc, summaryCtx, argAp, summaryCtx0, argAp0, config)
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate fwdFlowStore(
|
||||
NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, FlowState state, Cc cc, ApOption argAp,
|
||||
Configuration config
|
||||
NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, FlowState state, Cc cc,
|
||||
ParameterPositionOption summaryCtx, ApOption argAp, Configuration config
|
||||
) {
|
||||
exists(DataFlowType contentType |
|
||||
fwdFlow(node1, state, cc, argAp, ap1, config) and
|
||||
fwdFlow(node1, state, cc, summaryCtx, argAp, ap1, config) and
|
||||
PrevStage::storeStepCand(node1, unbindApa(getApprox(ap1)), tc, node2, contentType, config) and
|
||||
typecheckStore(ap1, contentType)
|
||||
)
|
||||
@@ -1366,7 +1427,7 @@ private module MkStage<StageSig PrevStage> {
|
||||
pragma[nomagic]
|
||||
private predicate fwdFlowConsCand(Ap cons, Content c, Ap tail, Configuration config) {
|
||||
exists(TypedContent tc |
|
||||
fwdFlowStore(_, tail, tc, _, _, _, _, config) and
|
||||
fwdFlowStore(_, tail, tc, _, _, _, _, _, config) and
|
||||
tc.getContent() = c and
|
||||
cons = apCons(tc, tail)
|
||||
)
|
||||
@@ -1374,21 +1435,21 @@ private module MkStage<StageSig PrevStage> {
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate fwdFlowRead(
|
||||
Ap ap, Content c, NodeEx node1, NodeEx node2, FlowState state, Cc cc, ApOption argAp,
|
||||
Configuration config
|
||||
Ap ap, Content c, NodeEx node1, NodeEx node2, FlowState state, Cc cc,
|
||||
ParameterPositionOption summaryCtx, ApOption argAp, Configuration config
|
||||
) {
|
||||
fwdFlow(node1, state, cc, 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, Cc innercc, ApOption argAp,
|
||||
Ap ap, 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, argAp, ap, 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()
|
||||
@@ -1396,29 +1457,19 @@ private module MkStage<StageSig PrevStage> {
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate fwdFlowOutNotFromArg(
|
||||
NodeEx out, FlowState state, Cc ccOut, ApOption argAp, Ap ap, Configuration config
|
||||
private predicate fwdFlowOutFromArg(
|
||||
DataFlowCall call, NodeEx out, FlowState state, ParameterPosition summaryCtx, Ap argAp, Ap ap,
|
||||
Configuration config
|
||||
) {
|
||||
exists(
|
||||
DataFlowCall call, RetNodeEx ret, boolean allowsFieldFlow, CcNoCall innercc,
|
||||
DataFlowCallable inner
|
||||
DataFlowCallable c, RetNodeEx ret, ReturnKindExt kind, boolean allowsFieldFlow, CcCall ccc
|
||||
|
|
||||
fwdFlow(ret, state, innercc, argAp, ap, config) and
|
||||
flowOutOfCall(call, ret, out, allowsFieldFlow, config) and
|
||||
inner = ret.getEnclosingCallable() and
|
||||
ccOut = getCallContextReturn(inner, call, innercc) and
|
||||
if allowsFieldFlow = false then ap instanceof ApNil else any()
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate fwdFlowOutFromArg(
|
||||
DataFlowCall call, NodeEx out, FlowState state, Ap argAp, Ap ap, Configuration config
|
||||
) {
|
||||
exists(RetNodeEx ret, boolean allowsFieldFlow, CcCall ccc |
|
||||
fwdFlow(ret, state, ccc, apSome(argAp), ap, config) and
|
||||
flowThroughOutOfCall(call, ccc, ret, out, allowsFieldFlow, config) and
|
||||
if allowsFieldFlow = false then ap instanceof ApNil else any()
|
||||
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
|
||||
(if allowsFieldFlow = false then ap instanceof ApNil else any()) and
|
||||
parameterFlowThroughAllowed(c, pragma[only_bind_into](summaryCtx), kind)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1428,11 +1479,13 @@ private module MkStage<StageSig PrevStage> {
|
||||
*/
|
||||
pragma[nomagic]
|
||||
private predicate fwdFlowIsEntered(
|
||||
DataFlowCall call, Cc cc, ApOption argAp, Ap ap, Configuration config
|
||||
DataFlowCall call, Cc cc, ParameterPositionOption summaryCtx, ApOption argAp,
|
||||
ParameterPosition pos, Ap ap, Configuration config
|
||||
) {
|
||||
exists(ParamNodeEx p |
|
||||
fwdFlowIn(call, p, _, cc, _, argAp, ap, config) and
|
||||
PrevStage::parameterMayFlowThrough(p, _, unbindApa(getApprox(ap)), config)
|
||||
exists(ParamNodeEx param |
|
||||
fwdFlowIn(call, param, _, cc, _, summaryCtx, argAp, ap, config) and
|
||||
PrevStage::parameterMayFlowThrough(param, unbindApa(getApprox(ap)), config) and
|
||||
pos = param.getPosition()
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1440,27 +1493,40 @@ private module MkStage<StageSig PrevStage> {
|
||||
private predicate storeStepFwd(
|
||||
NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, Ap ap2, Configuration config
|
||||
) {
|
||||
fwdFlowStore(node1, ap1, tc, node2, _, _, _, config) and
|
||||
fwdFlowStore(node1, ap1, tc, node2, _, _, _, _, config) and
|
||||
ap2 = apCons(tc, ap1) and
|
||||
fwdFlowRead(ap2, tc.getContent(), _, _, _, _, _, config)
|
||||
fwdFlowRead(ap2, tc.getContent(), _, _, _, _, _, _, config)
|
||||
}
|
||||
|
||||
private predicate readStepFwd(
|
||||
NodeEx n1, Ap ap1, Content c, NodeEx n2, Ap ap2, Configuration config
|
||||
) {
|
||||
fwdFlowRead(ap1, c, n1, n2, _, _, _, config) and
|
||||
fwdFlowRead(ap1, c, n1, n2, _, _, _, _, config) and
|
||||
fwdFlowConsCand(ap1, c, ap2, config)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate callMayFlowThroughFwd(DataFlowCall call, Configuration config) {
|
||||
exists(Ap argAp0, NodeEx out, FlowState state, Cc cc, ApOption argAp, Ap ap |
|
||||
fwdFlow(out, state, pragma[only_bind_into](cc), pragma[only_bind_into](argAp), ap,
|
||||
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
|
||||
fwdFlowOutFromArg(call, out, state, argAp0, ap, config) and
|
||||
fwdFlowIsEntered(pragma[only_bind_into](call), pragma[only_bind_into](cc),
|
||||
pragma[only_bind_into](argAp), pragma[only_bind_into](argAp0),
|
||||
pragma[only_bind_into](config))
|
||||
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(DataFlowCallable c, ParameterPosition ppos |
|
||||
returnFlowsThrough0(ret, kind, state, ccc, c, ppos, argAp, ap, config) and
|
||||
p.isParameterOf(c, ppos) and
|
||||
parameterFlowThroughAllowed(p, kind)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1468,118 +1534,130 @@ private module MkStage<StageSig PrevStage> {
|
||||
private predicate flowThroughIntoCall(
|
||||
DataFlowCall call, ArgNodeEx arg, ParamNodeEx p, boolean allowsFieldFlow, Configuration config
|
||||
) {
|
||||
flowIntoCall(call, arg, p, allowsFieldFlow, config) and
|
||||
fwdFlow(arg, _, _, _, _, pragma[only_bind_into](config)) and
|
||||
PrevStage::parameterMayFlowThrough(p, _, _, pragma[only_bind_into](config)) and
|
||||
callMayFlowThroughFwd(call, pragma[only_bind_into](config))
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate returnNodeMayFlowThrough(
|
||||
RetNodeEx ret, FlowState state, Ap ap, Configuration config
|
||||
) {
|
||||
fwdFlow(ret, state, any(CcCall ccc), apSome(_), ap, config)
|
||||
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))
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `node` with access path `ap` is part of a path from a source to a
|
||||
* sink in the configuration `config`.
|
||||
*
|
||||
* The Boolean `toReturn` records whether the node must be returned from the
|
||||
* enclosing callable in order to reach a sink, and if so, `returnAp` records
|
||||
* the access path of the returned value.
|
||||
* The parameter `returnCtx` records whether (and how) the node must be returned
|
||||
* from the enclosing callable in order to reach a sink, and if so, `returnAp`
|
||||
* records the access path of the returned value.
|
||||
*/
|
||||
pragma[nomagic]
|
||||
additional predicate revFlow(
|
||||
NodeEx node, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, Configuration config
|
||||
NodeEx node, FlowState state, ReturnCtx returnCtx, ApOption returnAp, Ap ap,
|
||||
Configuration config
|
||||
) {
|
||||
revFlow0(node, state, toReturn, returnAp, ap, config) and
|
||||
fwdFlow(node, state, _, _, ap, config)
|
||||
revFlow0(node, state, returnCtx, returnAp, ap, config) and
|
||||
fwdFlow(node, state, _, _, _, ap, config)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate revFlow0(
|
||||
NodeEx node, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, Configuration config
|
||||
NodeEx node, FlowState state, ReturnCtx returnCtx, ApOption returnAp, Ap ap,
|
||||
Configuration config
|
||||
) {
|
||||
fwdFlow(node, state, _, _, ap, config) and
|
||||
fwdFlow(node, state, _, _, _, ap, config) and
|
||||
sinkNode(node, state, config) and
|
||||
(if hasSinkCallCtx(config) then toReturn = true else toReturn = false) and
|
||||
(
|
||||
if hasSinkCallCtx(config)
|
||||
then returnCtx = TReturnCtxNoFlowThrough()
|
||||
else returnCtx = TReturnCtxNone()
|
||||
) and
|
||||
returnAp = apNone() and
|
||||
ap instanceof ApNil
|
||||
or
|
||||
exists(NodeEx mid, FlowState state0 |
|
||||
localStep(node, state, mid, state0, true, _, config, _) and
|
||||
revFlow(mid, state0, toReturn, returnAp, ap, config)
|
||||
revFlow(mid, state0, returnCtx, returnAp, ap, config)
|
||||
)
|
||||
or
|
||||
exists(NodeEx mid, FlowState state0, ApNil nil |
|
||||
fwdFlow(node, pragma[only_bind_into](state), _, _, ap, pragma[only_bind_into](config)) and
|
||||
fwdFlow(node, pragma[only_bind_into](state), _, _, _, ap, pragma[only_bind_into](config)) and
|
||||
localStep(node, pragma[only_bind_into](state), mid, state0, false, _, config, _) and
|
||||
revFlow(mid, state0, toReturn, returnAp, nil, pragma[only_bind_into](config)) and
|
||||
revFlow(mid, state0, returnCtx, returnAp, nil, pragma[only_bind_into](config)) and
|
||||
ap instanceof ApNil
|
||||
)
|
||||
or
|
||||
exists(NodeEx mid |
|
||||
jumpStep(node, mid, config) and
|
||||
revFlow(mid, state, _, _, ap, config) and
|
||||
toReturn = false and
|
||||
returnCtx = TReturnCtxNone() and
|
||||
returnAp = apNone()
|
||||
)
|
||||
or
|
||||
exists(NodeEx mid, ApNil nil |
|
||||
fwdFlow(node, _, _, _, ap, pragma[only_bind_into](config)) and
|
||||
fwdFlow(node, _, _, _, _, ap, pragma[only_bind_into](config)) and
|
||||
additionalJumpStep(node, mid, config) and
|
||||
revFlow(pragma[only_bind_into](mid), state, _, _, nil, pragma[only_bind_into](config)) and
|
||||
toReturn = false and
|
||||
returnCtx = TReturnCtxNone() and
|
||||
returnAp = apNone() and
|
||||
ap instanceof ApNil
|
||||
)
|
||||
or
|
||||
exists(NodeEx mid, FlowState state0, ApNil nil |
|
||||
fwdFlow(node, _, _, _, ap, pragma[only_bind_into](config)) and
|
||||
fwdFlow(node, _, _, _, _, ap, pragma[only_bind_into](config)) and
|
||||
additionalJumpStateStep(node, state, mid, state0, config) and
|
||||
revFlow(pragma[only_bind_into](mid), pragma[only_bind_into](state0), _, _, nil,
|
||||
pragma[only_bind_into](config)) and
|
||||
toReturn = false and
|
||||
returnCtx = TReturnCtxNone() and
|
||||
returnAp = apNone() and
|
||||
ap instanceof ApNil
|
||||
)
|
||||
or
|
||||
// store
|
||||
exists(Ap ap0, Content c |
|
||||
revFlowStore(ap0, c, ap, node, state, _, _, toReturn, returnAp, config) and
|
||||
revFlowStore(ap0, c, ap, node, state, _, _, returnCtx, returnAp, config) and
|
||||
revFlowConsCand(ap0, c, ap, config)
|
||||
)
|
||||
or
|
||||
// read
|
||||
exists(NodeEx mid, Ap ap0 |
|
||||
revFlow(mid, state, toReturn, returnAp, ap0, config) and
|
||||
revFlow(mid, state, returnCtx, returnAp, ap0, config) and
|
||||
readStepFwd(node, ap, _, mid, ap0, config)
|
||||
)
|
||||
or
|
||||
// flow into a callable
|
||||
revFlowInNotToReturn(node, state, returnAp, ap, config) and
|
||||
toReturn = false
|
||||
exists(ParamNodeEx p, boolean allowsFieldFlow |
|
||||
revFlow(p, state, TReturnCtxNone(), returnAp, ap, config) and
|
||||
flowIntoCall(_, node, p, allowsFieldFlow, config) and
|
||||
(if allowsFieldFlow = false then ap instanceof ApNil else any()) and
|
||||
returnCtx = TReturnCtxNone()
|
||||
)
|
||||
or
|
||||
exists(DataFlowCall call, Ap returnAp0 |
|
||||
revFlowInToReturn(call, node, state, returnAp0, ap, config) and
|
||||
revFlowIsReturned(call, toReturn, returnAp, returnAp0, config)
|
||||
// flow through a callable
|
||||
exists(DataFlowCall call, ReturnKindExt returnKind0, Ap returnAp0 |
|
||||
revFlowInToReturn(call, node, state, returnKind0, returnAp0, ap, config) and
|
||||
revFlowIsReturned(call, returnCtx, returnAp, returnKind0, returnAp0, config)
|
||||
)
|
||||
or
|
||||
// flow out of a callable
|
||||
revFlowOut(_, node, state, _, _, ap, config) and
|
||||
toReturn = true and
|
||||
if returnNodeMayFlowThrough(node, state, ap, config)
|
||||
then returnAp = apSome(ap)
|
||||
else returnAp = apNone()
|
||||
exists(ReturnKindExt kind |
|
||||
revFlowOut(_, node, kind, state, _, _, ap, config) and
|
||||
if returnFlowsThrough(node, kind, state, _, _, _, ap, config)
|
||||
then (
|
||||
returnCtx = TReturnCtxMaybeFlowThrough(kind) and
|
||||
returnAp = apSome(ap)
|
||||
) else (
|
||||
returnCtx = TReturnCtxNoFlowThrough() and returnAp = apNone()
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate revFlowStore(
|
||||
Ap ap0, Content c, Ap ap, NodeEx node, FlowState state, TypedContent tc, NodeEx mid,
|
||||
boolean toReturn, ApOption returnAp, Configuration config
|
||||
ReturnCtx returnCtx, ApOption returnAp, Configuration config
|
||||
) {
|
||||
revFlow(mid, state, toReturn, returnAp, ap0, config) and
|
||||
revFlow(mid, state, returnCtx, returnAp, ap0, config) and
|
||||
storeStepFwd(node, ap, tc, mid, ap0, config) and
|
||||
tc.getContent() = c
|
||||
}
|
||||
@@ -1599,35 +1677,27 @@ private module MkStage<StageSig PrevStage> {
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate revFlowOut(
|
||||
DataFlowCall call, RetNodeEx ret, FlowState state, boolean toReturn, ApOption returnAp, Ap ap,
|
||||
Configuration config
|
||||
DataFlowCall call, RetNodeEx ret, ReturnKindExt kind, FlowState state, ReturnCtx returnCtx,
|
||||
ApOption returnAp, Ap ap, Configuration config
|
||||
) {
|
||||
exists(NodeEx out, boolean allowsFieldFlow |
|
||||
revFlow(out, state, toReturn, returnAp, ap, config) and
|
||||
flowOutOfCall(call, ret, out, allowsFieldFlow, config) and
|
||||
if allowsFieldFlow = false then ap instanceof ApNil else any()
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate revFlowInNotToReturn(
|
||||
ArgNodeEx arg, FlowState state, ApOption returnAp, Ap ap, Configuration config
|
||||
) {
|
||||
exists(ParamNodeEx p, boolean allowsFieldFlow |
|
||||
revFlow(p, state, false, returnAp, ap, config) and
|
||||
flowIntoCall(_, arg, p, allowsFieldFlow, config) and
|
||||
revFlow(out, state, returnCtx, returnAp, ap, config) and
|
||||
flowOutOfCall(call, ret, kind, out, allowsFieldFlow, config) and
|
||||
if allowsFieldFlow = false then ap instanceof ApNil else any()
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate revFlowInToReturn(
|
||||
DataFlowCall call, ArgNodeEx arg, FlowState state, Ap returnAp, Ap ap, Configuration config
|
||||
DataFlowCall call, ArgNodeEx arg, FlowState state, ReturnKindExt kind, Ap returnAp, Ap ap,
|
||||
Configuration config
|
||||
) {
|
||||
exists(ParamNodeEx p, boolean allowsFieldFlow |
|
||||
revFlow(p, state, true, apSome(returnAp), ap, config) and
|
||||
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()
|
||||
(if allowsFieldFlow = false then ap instanceof ApNil else any()) and
|
||||
parameterFlowThroughAllowed(p, kind)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1638,11 +1708,12 @@ private module MkStage<StageSig PrevStage> {
|
||||
*/
|
||||
pragma[nomagic]
|
||||
private predicate revFlowIsReturned(
|
||||
DataFlowCall call, boolean toReturn, ApOption returnAp, Ap ap, Configuration config
|
||||
DataFlowCall call, ReturnCtx returnCtx, ApOption returnAp, ReturnKindExt kind, Ap ap,
|
||||
Configuration config
|
||||
) {
|
||||
exists(RetNodeEx ret, FlowState state, CcCall ccc |
|
||||
revFlowOut(call, ret, state, toReturn, returnAp, ap, config) and
|
||||
fwdFlow(ret, state, ccc, apSome(_), ap, config) and
|
||||
revFlowOut(call, ret, kind, state, returnCtx, returnAp, ap, config) and
|
||||
returnFlowsThrough(ret, kind, state, ccc, _, _, ap, config) and
|
||||
matchesCall(ccc, call)
|
||||
)
|
||||
}
|
||||
@@ -1713,40 +1784,39 @@ private module MkStage<StageSig PrevStage> {
|
||||
validAp(ap, config)
|
||||
}
|
||||
|
||||
pragma[noinline]
|
||||
private predicate parameterFlow(
|
||||
ParamNodeEx p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config
|
||||
pragma[nomagic]
|
||||
private predicate parameterFlowsThroughRev(
|
||||
ParamNodeEx p, Ap ap, ReturnKindExt kind, Configuration config
|
||||
) {
|
||||
revFlow(p, _, true, apSome(ap0), ap, config) and
|
||||
c = p.getEnclosingCallable()
|
||||
revFlow(p, _, TReturnCtxMaybeFlowThrough(kind), apSome(_), ap, config) and
|
||||
parameterFlowThroughAllowed(p, kind)
|
||||
}
|
||||
|
||||
predicate parameterMayFlowThrough(ParamNodeEx p, DataFlowCallable c, Ap ap, Configuration config) {
|
||||
exists(RetNodeEx ret, FlowState state, Ap ap0, ReturnKindExt kind, ParameterPosition pos |
|
||||
parameterFlow(p, ap, ap0, c, config) and
|
||||
c = ret.getEnclosingCallable() and
|
||||
revFlow(pragma[only_bind_into](ret), pragma[only_bind_into](state), true, apSome(_),
|
||||
pragma[only_bind_into](ap0), pragma[only_bind_into](config)) and
|
||||
fwdFlow(ret, state, any(CcCall ccc), apSome(ap), ap0, config) and
|
||||
kind = ret.getKind() and
|
||||
p.getPosition() = pos and
|
||||
// we don't expect a parameter to return stored in itself, unless explicitly allowed
|
||||
(
|
||||
not kind.(ParamUpdateReturnKind).getPosition() = pos
|
||||
or
|
||||
p.allowParameterReturnInSelf()
|
||||
)
|
||||
pragma[nomagic]
|
||||
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)
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
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(
|
||||
Ap returnAp0, ArgNodeEx arg, FlowState state, boolean toReturn, ApOption returnAp, Ap ap
|
||||
ReturnKindExt returnKind0, Ap returnAp0, ArgNodeEx arg, FlowState state,
|
||||
ReturnCtx returnCtx, ApOption returnAp, Ap ap
|
||||
|
|
||||
revFlow(arg, state, toReturn, returnAp, ap, config) and
|
||||
revFlowInToReturn(call, arg, state, returnAp0, ap, config) and
|
||||
revFlowIsReturned(call, toReturn, returnAp, returnAp0, config)
|
||||
revFlow(arg, state, returnCtx, returnAp, ap, config) and
|
||||
revFlowInToReturn(call, arg, state, returnKind0, returnAp0, ap, config) and
|
||||
revFlowIsReturned(call, returnCtx, returnAp, returnKind0, returnAp0, config)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1754,14 +1824,13 @@ private module MkStage<StageSig PrevStage> {
|
||||
boolean fwd, int nodes, int fields, int conscand, int states, int tuples, Configuration config
|
||||
) {
|
||||
fwd = true and
|
||||
nodes = count(NodeEx node | fwdFlow(node, _, _, _, _, config)) and
|
||||
nodes = count(NodeEx node | fwdFlow(node, _, _, _, _, _, config)) and
|
||||
fields = count(TypedContent f0 | fwdConsCand(f0, _, config)) and
|
||||
conscand = count(TypedContent f0, Ap ap | fwdConsCand(f0, ap, config)) and
|
||||
states = count(FlowState state | fwdFlow(_, state, _, _, _, config)) and
|
||||
states = count(FlowState state | fwdFlow(_, state, _, _, _, _, config)) and
|
||||
tuples =
|
||||
count(NodeEx n, FlowState state, Cc cc, ApOption argAp, Ap ap |
|
||||
fwdFlow(n, state, cc, argAp, ap, config)
|
||||
)
|
||||
count(NodeEx n, FlowState state, Cc cc, ParameterPositionOption summaryCtx, ApOption argAp,
|
||||
Ap ap | fwdFlow(n, state, cc, summaryCtx, argAp, ap, config))
|
||||
or
|
||||
fwd = false and
|
||||
nodes = count(NodeEx node | revFlow(node, _, _, _, _, config)) and
|
||||
@@ -1769,8 +1838,8 @@ private module MkStage<StageSig PrevStage> {
|
||||
conscand = count(TypedContent f0, Ap ap | consCand(f0, ap, config)) and
|
||||
states = count(FlowState state | revFlow(_, state, _, _, _, config)) and
|
||||
tuples =
|
||||
count(NodeEx n, FlowState state, boolean b, ApOption retAp, Ap ap |
|
||||
revFlow(n, state, b, retAp, ap, config)
|
||||
count(NodeEx n, FlowState state, ReturnCtx returnCtx, ApOption retAp, Ap ap |
|
||||
revFlow(n, state, returnCtx, retAp, ap, config)
|
||||
)
|
||||
}
|
||||
/* End: Stage logic. */
|
||||
@@ -1915,7 +1984,7 @@ private module Stage2Param implements MkStage<Stage1>::StageParam {
|
||||
exists(lcc)
|
||||
}
|
||||
|
||||
predicate flowOutOfCall = flowOutOfCallNodeCand1/5;
|
||||
predicate flowOutOfCall = flowOutOfCallNodeCand1/6;
|
||||
|
||||
predicate flowIntoCall = flowIntoCallNodeCand1/5;
|
||||
|
||||
@@ -1951,9 +2020,10 @@ private module Stage2 implements StageSig {
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate flowOutOfCallNodeCand2(
|
||||
DataFlowCall call, RetNodeEx node1, NodeEx node2, boolean allowsFieldFlow, Configuration config
|
||||
DataFlowCall call, RetNodeEx node1, ReturnKindExt kind, NodeEx node2, boolean allowsFieldFlow,
|
||||
Configuration config
|
||||
) {
|
||||
flowOutOfCallNodeCand1(call, node1, node2, allowsFieldFlow, config) and
|
||||
flowOutOfCallNodeCand1(call, node1, kind, node2, allowsFieldFlow, config) and
|
||||
Stage2::revFlow(node2, pragma[only_bind_into](config)) and
|
||||
Stage2::revFlowAlias(node1, pragma[only_bind_into](config))
|
||||
}
|
||||
@@ -2021,8 +2091,8 @@ private module LocalFlowBigStep {
|
||||
exists(NodeEx next | Stage2::revFlow(next, state, config) |
|
||||
jumpStep(node, next, config) or
|
||||
additionalJumpStep(node, next, config) or
|
||||
flowIntoCallNodeCand1(_, node, next, config) or
|
||||
flowOutOfCallNodeCand1(_, node, next, config) or
|
||||
flowIntoCallNodeCand2(_, node, next, _, config) or
|
||||
flowOutOfCallNodeCand2(_, node, _, next, _, config) or
|
||||
Stage2::storeStepCand(node, _, _, next, _, config) or
|
||||
Stage2::readStepCand(node, _, next, config)
|
||||
)
|
||||
@@ -2163,7 +2233,7 @@ private module Stage3Param implements MkStage<Stage2>::StageParam {
|
||||
localFlowBigStep(node1, state1, node2, state2, preservesValue, ap, config, _) and exists(lcc)
|
||||
}
|
||||
|
||||
predicate flowOutOfCall = flowOutOfCallNodeCand2/5;
|
||||
predicate flowOutOfCall = flowOutOfCallNodeCand2/6;
|
||||
|
||||
predicate flowIntoCall = flowIntoCallNodeCand2/5;
|
||||
|
||||
@@ -2233,8 +2303,9 @@ private predicate flowCandSummaryCtx(
|
||||
NodeEx node, FlowState state, AccessPathFront argApf, Configuration config
|
||||
) {
|
||||
exists(AccessPathFront apf |
|
||||
Stage3::revFlow(node, state, true, _, apf, config) and
|
||||
Stage3::fwdFlow(node, state, any(Stage3::CcCall ccc), TAccessPathFrontSome(argApf), apf, config)
|
||||
Stage3::revFlow(node, state, TReturnCtxMaybeFlowThrough(_), _, apf, config) and
|
||||
Stage3::fwdFlow(node, state, any(Stage3::CcCall ccc), _, TAccessPathFrontSome(argApf), apf,
|
||||
config)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -2468,10 +2539,11 @@ private module Stage4Param implements MkStage<Stage3>::StageParam {
|
||||
|
||||
pragma[nomagic]
|
||||
predicate flowOutOfCall(
|
||||
DataFlowCall call, RetNodeEx node1, NodeEx node2, boolean allowsFieldFlow, Configuration config
|
||||
DataFlowCall call, RetNodeEx node1, ReturnKindExt kind, NodeEx node2, boolean allowsFieldFlow,
|
||||
Configuration config
|
||||
) {
|
||||
exists(FlowState state |
|
||||
flowOutOfCallNodeCand2(call, node1, node2, allowsFieldFlow, config) and
|
||||
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))
|
||||
@@ -2508,13 +2580,14 @@ private Configuration unbindConf(Configuration conf) {
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate nodeMayUseSummary0(
|
||||
NodeEx n, DataFlowCallable c, FlowState state, AccessPathApprox apa, Configuration config
|
||||
NodeEx n, DataFlowCallable c, ParameterPosition pos, FlowState state, AccessPathApprox apa,
|
||||
Configuration config
|
||||
) {
|
||||
exists(AccessPathApprox apa0 |
|
||||
Stage4::parameterMayFlowThrough(_, c, _, _) and
|
||||
Stage4::revFlow(n, state, true, _, apa0, config) and
|
||||
Stage4::fwdFlow(n, state, any(CallContextCall ccc), TAccessPathApproxSome(apa), apa0, config) and
|
||||
n.getEnclosingCallable() = c
|
||||
c = n.getEnclosingCallable() and
|
||||
Stage4::revFlow(n, state, TReturnCtxMaybeFlowThrough(_), _, apa0, config) and
|
||||
Stage4::fwdFlow(n, state, any(CallContextCall ccc), TParameterPositionSome(pos),
|
||||
TAccessPathApproxSome(apa), apa0, config)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -2522,9 +2595,10 @@ pragma[nomagic]
|
||||
private predicate nodeMayUseSummary(
|
||||
NodeEx n, FlowState state, AccessPathApprox apa, Configuration config
|
||||
) {
|
||||
exists(DataFlowCallable c |
|
||||
Stage4::parameterMayFlowThrough(_, c, apa, config) and
|
||||
nodeMayUseSummary0(n, c, state, apa, config)
|
||||
exists(DataFlowCallable c, ParameterPosition pos, ParamNodeEx p |
|
||||
Stage4::parameterMayFlowThrough(p, apa, config) and
|
||||
nodeMayUseSummary0(n, c, pos, state, apa, config) and
|
||||
p.isParameterOf(c, pos)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -2532,7 +2606,7 @@ private newtype TSummaryCtx =
|
||||
TSummaryCtxNone() or
|
||||
TSummaryCtxSome(ParamNodeEx p, FlowState state, AccessPath ap) {
|
||||
exists(Configuration config |
|
||||
Stage4::parameterMayFlowThrough(p, _, ap.getApprox(), config) and
|
||||
Stage4::parameterMayFlowThrough(p, ap.getApprox(), config) and
|
||||
Stage4::revFlow(p, state, _, config)
|
||||
)
|
||||
}
|
||||
@@ -3453,17 +3527,11 @@ private predicate paramFlowsThrough(
|
||||
ReturnKindExt kind, FlowState state, CallContextCall cc, SummaryCtxSome sc, AccessPath ap,
|
||||
AccessPathApprox apa, Configuration config
|
||||
) {
|
||||
exists(PathNodeMid mid, RetNodeEx ret, ParameterPosition pos |
|
||||
exists(PathNodeMid mid, RetNodeEx ret |
|
||||
pathNode(mid, ret, state, cc, sc, ap, config, _) and
|
||||
kind = ret.getKind() and
|
||||
apa = ap.getApprox() and
|
||||
pos = sc.getParameterPos() and
|
||||
// we don't expect a parameter to return stored in itself, unless explicitly allowed
|
||||
(
|
||||
not kind.(ParamUpdateReturnKind).getPosition() = pos
|
||||
or
|
||||
sc.getParamNode().allowParameterReturnInSelf()
|
||||
)
|
||||
parameterFlowThroughAllowed(sc.getParamNode(), kind)
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@@ -70,7 +70,7 @@ abstract class Configuration extends string {
|
||||
/**
|
||||
* Holds if `sink` is a relevant data flow sink accepting `state`.
|
||||
*/
|
||||
predicate isSink(Node source, FlowState state) { none() }
|
||||
predicate isSink(Node sink, FlowState state) { none() }
|
||||
|
||||
/**
|
||||
* Holds if data flow through `node` is prohibited. This completely removes
|
||||
@@ -319,8 +319,6 @@ private class ParamNodeEx extends NodeEx {
|
||||
}
|
||||
|
||||
ParameterPosition getPosition() { this.isParameterOf(_, result) }
|
||||
|
||||
predicate allowParameterReturnInSelf() { allowParameterReturnInSelfCached(this.asNode()) }
|
||||
}
|
||||
|
||||
private class RetNodeEx extends NodeEx {
|
||||
@@ -608,6 +606,38 @@ private predicate hasSinkCallCtx(Configuration config) {
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if flow from `p` to a return node of kind `kind` is allowed.
|
||||
*
|
||||
* We don't expect a parameter to return stored in itself, unless
|
||||
* explicitly allowed
|
||||
*/
|
||||
bindingset[p, kind]
|
||||
private predicate parameterFlowThroughAllowed(ParamNodeEx p, ReturnKindExt kind) {
|
||||
exists(ParameterPosition pos | p.isParameterOf(_, pos) |
|
||||
not kind.(ParamUpdateReturnKind).getPosition() = pos
|
||||
or
|
||||
allowParameterReturnInSelfCached(p.asNode())
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* 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;
|
||||
|
||||
@@ -981,21 +1011,22 @@ private module Stage1 implements StageSig {
|
||||
* candidate for the origin of a summary.
|
||||
*/
|
||||
pragma[nomagic]
|
||||
predicate parameterMayFlowThrough(ParamNodeEx p, DataFlowCallable c, Ap ap, Configuration config) {
|
||||
exists(ReturnKindExt kind |
|
||||
predicate parameterMayFlowThrough(ParamNodeEx p, Ap ap, Configuration config) {
|
||||
exists(DataFlowCallable c, ReturnKindExt kind |
|
||||
throughFlowNodeCand(p, config) and
|
||||
returnFlowCallableNodeCand(c, kind, config) and
|
||||
p.getEnclosingCallable() = c and
|
||||
exists(ap) and
|
||||
// we don't expect a parameter to return stored in itself, unless explicitly allowed
|
||||
(
|
||||
not kind.(ParamUpdateReturnKind).getPosition() = p.getPosition()
|
||||
or
|
||||
p.allowParameterReturnInSelf()
|
||||
)
|
||||
parameterFlowThroughAllowed(p, kind)
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
predicate returnMayFlowThrough(RetNodeEx ret, ReturnKindExt kind, Configuration config) {
|
||||
throughFlowNodeCand(ret, config) and
|
||||
kind = ret.getKind()
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
predicate callMayFlowThroughRev(DataFlowCall call, Configuration config) {
|
||||
exists(ArgNodeEx arg, boolean toReturn |
|
||||
@@ -1052,12 +1083,16 @@ private predicate viableReturnPosOutNodeCand1(
|
||||
*/
|
||||
pragma[nomagic]
|
||||
private predicate flowOutOfCallNodeCand1(
|
||||
DataFlowCall call, RetNodeEx ret, NodeEx out, Configuration config
|
||||
DataFlowCall call, RetNodeEx ret, ReturnKindExt kind, NodeEx out, Configuration config
|
||||
) {
|
||||
viableReturnPosOutNodeCand1(call, ret.getReturnPosition(), out, config) and
|
||||
Stage1::revFlow(ret, config) and
|
||||
not outBarrier(ret, config) and
|
||||
not inBarrier(out, config)
|
||||
exists(ReturnPosition pos |
|
||||
viableReturnPosOutNodeCand1(call, pos, out, config) and
|
||||
pos = ret.getReturnPosition() and
|
||||
kind = pos.getKind() and
|
||||
Stage1::revFlow(ret, config) and
|
||||
not outBarrier(ret, config) and
|
||||
not inBarrier(out, config)
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
@@ -1087,10 +1122,11 @@ private predicate flowIntoCallNodeCand1(
|
||||
* edge in the graph of paths between sources and sinks that ignores call
|
||||
* contexts.
|
||||
*/
|
||||
pragma[nomagic]
|
||||
private int branch(NodeEx n1, Configuration conf) {
|
||||
result =
|
||||
strictcount(NodeEx n |
|
||||
flowOutOfCallNodeCand1(_, n1, n, conf) or flowIntoCallNodeCand1(_, n1, n, conf)
|
||||
flowOutOfCallNodeCand1(_, n1, _, n, conf) or flowIntoCallNodeCand1(_, n1, n, conf)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1099,10 +1135,11 @@ private int branch(NodeEx n1, Configuration conf) {
|
||||
* edge in the graph of paths between sources and sinks that ignores call
|
||||
* contexts.
|
||||
*/
|
||||
pragma[nomagic]
|
||||
private int join(NodeEx n2, Configuration conf) {
|
||||
result =
|
||||
strictcount(NodeEx n |
|
||||
flowOutOfCallNodeCand1(_, n, n2, conf) or flowIntoCallNodeCand1(_, n, n2, conf)
|
||||
flowOutOfCallNodeCand1(_, n, _, n2, conf) or flowIntoCallNodeCand1(_, n, n2, conf)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1115,12 +1152,13 @@ private int join(NodeEx n2, Configuration conf) {
|
||||
*/
|
||||
pragma[nomagic]
|
||||
private predicate flowOutOfCallNodeCand1(
|
||||
DataFlowCall call, RetNodeEx ret, NodeEx out, boolean allowsFieldFlow, Configuration config
|
||||
DataFlowCall call, RetNodeEx ret, ReturnKindExt kind, NodeEx out, boolean allowsFieldFlow,
|
||||
Configuration config
|
||||
) {
|
||||
flowOutOfCallNodeCand1(call, ret, out, config) and
|
||||
flowOutOfCallNodeCand1(call, ret, kind, out, pragma[only_bind_into](config)) and
|
||||
exists(int b, int j |
|
||||
b = branch(ret, config) and
|
||||
j = join(out, config) and
|
||||
b = branch(ret, pragma[only_bind_into](config)) and
|
||||
j = join(out, pragma[only_bind_into](config)) and
|
||||
if b.minimum(j) <= config.fieldFlowBranchLimit()
|
||||
then allowsFieldFlow = true
|
||||
else allowsFieldFlow = false
|
||||
@@ -1136,10 +1174,10 @@ pragma[nomagic]
|
||||
private predicate flowIntoCallNodeCand1(
|
||||
DataFlowCall call, ArgNodeEx arg, ParamNodeEx p, boolean allowsFieldFlow, Configuration config
|
||||
) {
|
||||
flowIntoCallNodeCand1(call, arg, p, config) and
|
||||
flowIntoCallNodeCand1(call, arg, p, pragma[only_bind_into](config)) and
|
||||
exists(int b, int j |
|
||||
b = branch(arg, config) and
|
||||
j = join(p, config) and
|
||||
b = branch(arg, pragma[only_bind_into](config)) and
|
||||
j = join(p, pragma[only_bind_into](config)) and
|
||||
if b.minimum(j) <= config.fieldFlowBranchLimit()
|
||||
then allowsFieldFlow = true
|
||||
else allowsFieldFlow = false
|
||||
@@ -1156,7 +1194,9 @@ private signature module StageSig {
|
||||
|
||||
predicate callMayFlowThroughRev(DataFlowCall call, Configuration config);
|
||||
|
||||
predicate parameterMayFlowThrough(ParamNodeEx p, DataFlowCallable c, Ap ap, Configuration config);
|
||||
predicate parameterMayFlowThrough(ParamNodeEx p, Ap ap, Configuration config);
|
||||
|
||||
predicate returnMayFlowThrough(RetNodeEx ret, ReturnKindExt kind, Configuration config);
|
||||
|
||||
predicate storeStepCand(
|
||||
NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, DataFlowType contentType,
|
||||
@@ -1222,7 +1262,8 @@ private module MkStage<StageSig PrevStage> {
|
||||
);
|
||||
|
||||
predicate flowOutOfCall(
|
||||
DataFlowCall call, RetNodeEx ret, NodeEx out, boolean allowsFieldFlow, Configuration config
|
||||
DataFlowCall call, RetNodeEx ret, ReturnKindExt kind, NodeEx out, boolean allowsFieldFlow,
|
||||
Configuration config
|
||||
);
|
||||
|
||||
predicate flowIntoCall(
|
||||
@@ -1247,14 +1288,14 @@ private module MkStage<StageSig PrevStage> {
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate flowThroughOutOfCall(
|
||||
DataFlowCall call, CcCall ccc, RetNodeEx ret, NodeEx out, boolean allowsFieldFlow,
|
||||
Configuration config
|
||||
DataFlowCall call, DataFlowCallable c, CcCall ccc, RetNodeEx ret, ReturnKindExt kind,
|
||||
NodeEx out, boolean allowsFieldFlow, Configuration config
|
||||
) {
|
||||
flowOutOfCall(call, ret, out, allowsFieldFlow, pragma[only_bind_into](config)) and
|
||||
flowOutOfCall(call, ret, kind, out, allowsFieldFlow, pragma[only_bind_into](config)) and
|
||||
PrevStage::callMayFlowThroughRev(call, pragma[only_bind_into](config)) and
|
||||
PrevStage::parameterMayFlowThrough(_, ret.getEnclosingCallable(), _,
|
||||
pragma[only_bind_into](config)) and
|
||||
matchesCall(ccc, call)
|
||||
PrevStage::returnMayFlowThrough(ret, kind, pragma[only_bind_into](config)) and
|
||||
matchesCall(ccc, call) and
|
||||
c = ret.getEnclosingCallable()
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1262,29 +1303,32 @@ private module MkStage<StageSig PrevStage> {
|
||||
* configuration `config`.
|
||||
*
|
||||
* The call context `cc` records whether the node is reached through an
|
||||
* argument in a call, and if so, `argAp` records the access path of that
|
||||
* argument.
|
||||
* argument in a call, and if so, `summaryCtx` and `argAp` record the
|
||||
* corresponding parameter position and access path of that argument, respectively.
|
||||
*/
|
||||
pragma[nomagic]
|
||||
additional predicate fwdFlow(
|
||||
NodeEx node, FlowState state, Cc cc, ApOption argAp, Ap ap, Configuration config
|
||||
NodeEx node, FlowState state, Cc cc, ParameterPositionOption summaryCtx, ApOption argAp,
|
||||
Ap ap, Configuration config
|
||||
) {
|
||||
fwdFlow0(node, state, cc, argAp, ap, config) and
|
||||
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, ApOption argAp, Ap ap, Configuration config
|
||||
NodeEx node, FlowState state, Cc cc, ParameterPositionOption summaryCtx, ApOption argAp,
|
||||
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)
|
||||
or
|
||||
exists(NodeEx mid, FlowState state0, Ap ap0, LocalCc localCc |
|
||||
fwdFlow(mid, state0, cc, argAp, ap0, config) and
|
||||
fwdFlow(mid, state0, cc, summaryCtx, argAp, ap0, config) and
|
||||
localCc = getLocalCc(mid, cc)
|
||||
|
|
||||
localStep(mid, state0, node, state, true, _, config, localCc) and
|
||||
@@ -1295,65 +1339,82 @@ private module MkStage<StageSig PrevStage> {
|
||||
)
|
||||
or
|
||||
exists(NodeEx mid |
|
||||
fwdFlow(mid, pragma[only_bind_into](state), _, _, ap, 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
|
||||
argAp = apNone()
|
||||
)
|
||||
or
|
||||
exists(NodeEx mid, ApNil nil |
|
||||
fwdFlow(mid, state, _, _, nil, pragma[only_bind_into](config)) and
|
||||
fwdFlow(mid, state, _, _, _, nil, pragma[only_bind_into](config)) and
|
||||
additionalJumpStep(mid, node, config) and
|
||||
cc = ccNone() and
|
||||
summaryCtx = TParameterPositionNone() and
|
||||
argAp = apNone() and
|
||||
ap = getApNil(node)
|
||||
)
|
||||
or
|
||||
exists(NodeEx mid, FlowState state0, ApNil nil |
|
||||
fwdFlow(mid, state0, _, _, nil, pragma[only_bind_into](config)) and
|
||||
fwdFlow(mid, state0, _, _, _, nil, pragma[only_bind_into](config)) and
|
||||
additionalJumpStateStep(mid, state0, node, state, config) and
|
||||
cc = ccNone() and
|
||||
summaryCtx = TParameterPositionNone() and
|
||||
argAp = apNone() and
|
||||
ap = getApNil(node)
|
||||
)
|
||||
or
|
||||
// store
|
||||
exists(TypedContent tc, Ap ap0 |
|
||||
fwdFlowStore(_, ap0, tc, node, state, cc, argAp, config) and
|
||||
fwdFlowStore(_, ap0, tc, node, state, cc, summaryCtx, argAp, config) and
|
||||
ap = apCons(tc, ap0)
|
||||
)
|
||||
or
|
||||
// read
|
||||
exists(Ap ap0, Content c |
|
||||
fwdFlowRead(ap0, c, _, node, state, cc, argAp, config) and
|
||||
fwdFlowRead(ap0, c, _, node, state, cc, summaryCtx, argAp, config) and
|
||||
fwdFlowConsCand(ap0, c, ap, config)
|
||||
)
|
||||
or
|
||||
// flow into a callable
|
||||
exists(ApApprox apa |
|
||||
fwdFlowIn(_, node, state, _, cc, _, ap, config) and
|
||||
fwdFlowIn(_, node, state, _, cc, _, _, ap, config) and
|
||||
apa = getApprox(ap) and
|
||||
if PrevStage::parameterMayFlowThrough(node, _, apa, config)
|
||||
then argAp = apSome(ap)
|
||||
else argAp = apNone()
|
||||
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
|
||||
fwdFlowOutNotFromArg(node, state, cc, argAp, ap, config)
|
||||
exists(
|
||||
DataFlowCall call, RetNodeEx ret, boolean allowsFieldFlow, CcNoCall innercc,
|
||||
DataFlowCallable inner
|
||||
|
|
||||
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()
|
||||
)
|
||||
or
|
||||
exists(DataFlowCall call, Ap argAp0 |
|
||||
fwdFlowOutFromArg(call, node, state, argAp0, ap, config) and
|
||||
fwdFlowIsEntered(call, cc, argAp, argAp0, config)
|
||||
// flow through a callable
|
||||
exists(DataFlowCall call, ParameterPosition summaryCtx0, Ap argAp0 |
|
||||
fwdFlowOutFromArg(call, node, state, summaryCtx0, argAp0, ap, config) and
|
||||
fwdFlowIsEntered(call, cc, summaryCtx, argAp, summaryCtx0, argAp0, config)
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate fwdFlowStore(
|
||||
NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, FlowState state, Cc cc, ApOption argAp,
|
||||
Configuration config
|
||||
NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, FlowState state, Cc cc,
|
||||
ParameterPositionOption summaryCtx, ApOption argAp, Configuration config
|
||||
) {
|
||||
exists(DataFlowType contentType |
|
||||
fwdFlow(node1, state, cc, argAp, ap1, config) and
|
||||
fwdFlow(node1, state, cc, summaryCtx, argAp, ap1, config) and
|
||||
PrevStage::storeStepCand(node1, unbindApa(getApprox(ap1)), tc, node2, contentType, config) and
|
||||
typecheckStore(ap1, contentType)
|
||||
)
|
||||
@@ -1366,7 +1427,7 @@ private module MkStage<StageSig PrevStage> {
|
||||
pragma[nomagic]
|
||||
private predicate fwdFlowConsCand(Ap cons, Content c, Ap tail, Configuration config) {
|
||||
exists(TypedContent tc |
|
||||
fwdFlowStore(_, tail, tc, _, _, _, _, config) and
|
||||
fwdFlowStore(_, tail, tc, _, _, _, _, _, config) and
|
||||
tc.getContent() = c and
|
||||
cons = apCons(tc, tail)
|
||||
)
|
||||
@@ -1374,21 +1435,21 @@ private module MkStage<StageSig PrevStage> {
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate fwdFlowRead(
|
||||
Ap ap, Content c, NodeEx node1, NodeEx node2, FlowState state, Cc cc, ApOption argAp,
|
||||
Configuration config
|
||||
Ap ap, Content c, NodeEx node1, NodeEx node2, FlowState state, Cc cc,
|
||||
ParameterPositionOption summaryCtx, ApOption argAp, Configuration config
|
||||
) {
|
||||
fwdFlow(node1, state, cc, 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, Cc innercc, ApOption argAp,
|
||||
Ap ap, 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, argAp, ap, 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()
|
||||
@@ -1396,29 +1457,19 @@ private module MkStage<StageSig PrevStage> {
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate fwdFlowOutNotFromArg(
|
||||
NodeEx out, FlowState state, Cc ccOut, ApOption argAp, Ap ap, Configuration config
|
||||
private predicate fwdFlowOutFromArg(
|
||||
DataFlowCall call, NodeEx out, FlowState state, ParameterPosition summaryCtx, Ap argAp, Ap ap,
|
||||
Configuration config
|
||||
) {
|
||||
exists(
|
||||
DataFlowCall call, RetNodeEx ret, boolean allowsFieldFlow, CcNoCall innercc,
|
||||
DataFlowCallable inner
|
||||
DataFlowCallable c, RetNodeEx ret, ReturnKindExt kind, boolean allowsFieldFlow, CcCall ccc
|
||||
|
|
||||
fwdFlow(ret, state, innercc, argAp, ap, config) and
|
||||
flowOutOfCall(call, ret, out, allowsFieldFlow, config) and
|
||||
inner = ret.getEnclosingCallable() and
|
||||
ccOut = getCallContextReturn(inner, call, innercc) and
|
||||
if allowsFieldFlow = false then ap instanceof ApNil else any()
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate fwdFlowOutFromArg(
|
||||
DataFlowCall call, NodeEx out, FlowState state, Ap argAp, Ap ap, Configuration config
|
||||
) {
|
||||
exists(RetNodeEx ret, boolean allowsFieldFlow, CcCall ccc |
|
||||
fwdFlow(ret, state, ccc, apSome(argAp), ap, config) and
|
||||
flowThroughOutOfCall(call, ccc, ret, out, allowsFieldFlow, config) and
|
||||
if allowsFieldFlow = false then ap instanceof ApNil else any()
|
||||
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
|
||||
(if allowsFieldFlow = false then ap instanceof ApNil else any()) and
|
||||
parameterFlowThroughAllowed(c, pragma[only_bind_into](summaryCtx), kind)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1428,11 +1479,13 @@ private module MkStage<StageSig PrevStage> {
|
||||
*/
|
||||
pragma[nomagic]
|
||||
private predicate fwdFlowIsEntered(
|
||||
DataFlowCall call, Cc cc, ApOption argAp, Ap ap, Configuration config
|
||||
DataFlowCall call, Cc cc, ParameterPositionOption summaryCtx, ApOption argAp,
|
||||
ParameterPosition pos, Ap ap, Configuration config
|
||||
) {
|
||||
exists(ParamNodeEx p |
|
||||
fwdFlowIn(call, p, _, cc, _, argAp, ap, config) and
|
||||
PrevStage::parameterMayFlowThrough(p, _, unbindApa(getApprox(ap)), config)
|
||||
exists(ParamNodeEx param |
|
||||
fwdFlowIn(call, param, _, cc, _, summaryCtx, argAp, ap, config) and
|
||||
PrevStage::parameterMayFlowThrough(param, unbindApa(getApprox(ap)), config) and
|
||||
pos = param.getPosition()
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1440,27 +1493,40 @@ private module MkStage<StageSig PrevStage> {
|
||||
private predicate storeStepFwd(
|
||||
NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, Ap ap2, Configuration config
|
||||
) {
|
||||
fwdFlowStore(node1, ap1, tc, node2, _, _, _, config) and
|
||||
fwdFlowStore(node1, ap1, tc, node2, _, _, _, _, config) and
|
||||
ap2 = apCons(tc, ap1) and
|
||||
fwdFlowRead(ap2, tc.getContent(), _, _, _, _, _, config)
|
||||
fwdFlowRead(ap2, tc.getContent(), _, _, _, _, _, _, config)
|
||||
}
|
||||
|
||||
private predicate readStepFwd(
|
||||
NodeEx n1, Ap ap1, Content c, NodeEx n2, Ap ap2, Configuration config
|
||||
) {
|
||||
fwdFlowRead(ap1, c, n1, n2, _, _, _, config) and
|
||||
fwdFlowRead(ap1, c, n1, n2, _, _, _, _, config) and
|
||||
fwdFlowConsCand(ap1, c, ap2, config)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate callMayFlowThroughFwd(DataFlowCall call, Configuration config) {
|
||||
exists(Ap argAp0, NodeEx out, FlowState state, Cc cc, ApOption argAp, Ap ap |
|
||||
fwdFlow(out, state, pragma[only_bind_into](cc), pragma[only_bind_into](argAp), ap,
|
||||
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
|
||||
fwdFlowOutFromArg(call, out, state, argAp0, ap, config) and
|
||||
fwdFlowIsEntered(pragma[only_bind_into](call), pragma[only_bind_into](cc),
|
||||
pragma[only_bind_into](argAp), pragma[only_bind_into](argAp0),
|
||||
pragma[only_bind_into](config))
|
||||
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(DataFlowCallable c, ParameterPosition ppos |
|
||||
returnFlowsThrough0(ret, kind, state, ccc, c, ppos, argAp, ap, config) and
|
||||
p.isParameterOf(c, ppos) and
|
||||
parameterFlowThroughAllowed(p, kind)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1468,118 +1534,130 @@ private module MkStage<StageSig PrevStage> {
|
||||
private predicate flowThroughIntoCall(
|
||||
DataFlowCall call, ArgNodeEx arg, ParamNodeEx p, boolean allowsFieldFlow, Configuration config
|
||||
) {
|
||||
flowIntoCall(call, arg, p, allowsFieldFlow, config) and
|
||||
fwdFlow(arg, _, _, _, _, pragma[only_bind_into](config)) and
|
||||
PrevStage::parameterMayFlowThrough(p, _, _, pragma[only_bind_into](config)) and
|
||||
callMayFlowThroughFwd(call, pragma[only_bind_into](config))
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate returnNodeMayFlowThrough(
|
||||
RetNodeEx ret, FlowState state, Ap ap, Configuration config
|
||||
) {
|
||||
fwdFlow(ret, state, any(CcCall ccc), apSome(_), ap, config)
|
||||
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))
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `node` with access path `ap` is part of a path from a source to a
|
||||
* sink in the configuration `config`.
|
||||
*
|
||||
* The Boolean `toReturn` records whether the node must be returned from the
|
||||
* enclosing callable in order to reach a sink, and if so, `returnAp` records
|
||||
* the access path of the returned value.
|
||||
* The parameter `returnCtx` records whether (and how) the node must be returned
|
||||
* from the enclosing callable in order to reach a sink, and if so, `returnAp`
|
||||
* records the access path of the returned value.
|
||||
*/
|
||||
pragma[nomagic]
|
||||
additional predicate revFlow(
|
||||
NodeEx node, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, Configuration config
|
||||
NodeEx node, FlowState state, ReturnCtx returnCtx, ApOption returnAp, Ap ap,
|
||||
Configuration config
|
||||
) {
|
||||
revFlow0(node, state, toReturn, returnAp, ap, config) and
|
||||
fwdFlow(node, state, _, _, ap, config)
|
||||
revFlow0(node, state, returnCtx, returnAp, ap, config) and
|
||||
fwdFlow(node, state, _, _, _, ap, config)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate revFlow0(
|
||||
NodeEx node, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, Configuration config
|
||||
NodeEx node, FlowState state, ReturnCtx returnCtx, ApOption returnAp, Ap ap,
|
||||
Configuration config
|
||||
) {
|
||||
fwdFlow(node, state, _, _, ap, config) and
|
||||
fwdFlow(node, state, _, _, _, ap, config) and
|
||||
sinkNode(node, state, config) and
|
||||
(if hasSinkCallCtx(config) then toReturn = true else toReturn = false) and
|
||||
(
|
||||
if hasSinkCallCtx(config)
|
||||
then returnCtx = TReturnCtxNoFlowThrough()
|
||||
else returnCtx = TReturnCtxNone()
|
||||
) and
|
||||
returnAp = apNone() and
|
||||
ap instanceof ApNil
|
||||
or
|
||||
exists(NodeEx mid, FlowState state0 |
|
||||
localStep(node, state, mid, state0, true, _, config, _) and
|
||||
revFlow(mid, state0, toReturn, returnAp, ap, config)
|
||||
revFlow(mid, state0, returnCtx, returnAp, ap, config)
|
||||
)
|
||||
or
|
||||
exists(NodeEx mid, FlowState state0, ApNil nil |
|
||||
fwdFlow(node, pragma[only_bind_into](state), _, _, ap, pragma[only_bind_into](config)) and
|
||||
fwdFlow(node, pragma[only_bind_into](state), _, _, _, ap, pragma[only_bind_into](config)) and
|
||||
localStep(node, pragma[only_bind_into](state), mid, state0, false, _, config, _) and
|
||||
revFlow(mid, state0, toReturn, returnAp, nil, pragma[only_bind_into](config)) and
|
||||
revFlow(mid, state0, returnCtx, returnAp, nil, pragma[only_bind_into](config)) and
|
||||
ap instanceof ApNil
|
||||
)
|
||||
or
|
||||
exists(NodeEx mid |
|
||||
jumpStep(node, mid, config) and
|
||||
revFlow(mid, state, _, _, ap, config) and
|
||||
toReturn = false and
|
||||
returnCtx = TReturnCtxNone() and
|
||||
returnAp = apNone()
|
||||
)
|
||||
or
|
||||
exists(NodeEx mid, ApNil nil |
|
||||
fwdFlow(node, _, _, _, ap, pragma[only_bind_into](config)) and
|
||||
fwdFlow(node, _, _, _, _, ap, pragma[only_bind_into](config)) and
|
||||
additionalJumpStep(node, mid, config) and
|
||||
revFlow(pragma[only_bind_into](mid), state, _, _, nil, pragma[only_bind_into](config)) and
|
||||
toReturn = false and
|
||||
returnCtx = TReturnCtxNone() and
|
||||
returnAp = apNone() and
|
||||
ap instanceof ApNil
|
||||
)
|
||||
or
|
||||
exists(NodeEx mid, FlowState state0, ApNil nil |
|
||||
fwdFlow(node, _, _, _, ap, pragma[only_bind_into](config)) and
|
||||
fwdFlow(node, _, _, _, _, ap, pragma[only_bind_into](config)) and
|
||||
additionalJumpStateStep(node, state, mid, state0, config) and
|
||||
revFlow(pragma[only_bind_into](mid), pragma[only_bind_into](state0), _, _, nil,
|
||||
pragma[only_bind_into](config)) and
|
||||
toReturn = false and
|
||||
returnCtx = TReturnCtxNone() and
|
||||
returnAp = apNone() and
|
||||
ap instanceof ApNil
|
||||
)
|
||||
or
|
||||
// store
|
||||
exists(Ap ap0, Content c |
|
||||
revFlowStore(ap0, c, ap, node, state, _, _, toReturn, returnAp, config) and
|
||||
revFlowStore(ap0, c, ap, node, state, _, _, returnCtx, returnAp, config) and
|
||||
revFlowConsCand(ap0, c, ap, config)
|
||||
)
|
||||
or
|
||||
// read
|
||||
exists(NodeEx mid, Ap ap0 |
|
||||
revFlow(mid, state, toReturn, returnAp, ap0, config) and
|
||||
revFlow(mid, state, returnCtx, returnAp, ap0, config) and
|
||||
readStepFwd(node, ap, _, mid, ap0, config)
|
||||
)
|
||||
or
|
||||
// flow into a callable
|
||||
revFlowInNotToReturn(node, state, returnAp, ap, config) and
|
||||
toReturn = false
|
||||
exists(ParamNodeEx p, boolean allowsFieldFlow |
|
||||
revFlow(p, state, TReturnCtxNone(), returnAp, ap, config) and
|
||||
flowIntoCall(_, node, p, allowsFieldFlow, config) and
|
||||
(if allowsFieldFlow = false then ap instanceof ApNil else any()) and
|
||||
returnCtx = TReturnCtxNone()
|
||||
)
|
||||
or
|
||||
exists(DataFlowCall call, Ap returnAp0 |
|
||||
revFlowInToReturn(call, node, state, returnAp0, ap, config) and
|
||||
revFlowIsReturned(call, toReturn, returnAp, returnAp0, config)
|
||||
// flow through a callable
|
||||
exists(DataFlowCall call, ReturnKindExt returnKind0, Ap returnAp0 |
|
||||
revFlowInToReturn(call, node, state, returnKind0, returnAp0, ap, config) and
|
||||
revFlowIsReturned(call, returnCtx, returnAp, returnKind0, returnAp0, config)
|
||||
)
|
||||
or
|
||||
// flow out of a callable
|
||||
revFlowOut(_, node, state, _, _, ap, config) and
|
||||
toReturn = true and
|
||||
if returnNodeMayFlowThrough(node, state, ap, config)
|
||||
then returnAp = apSome(ap)
|
||||
else returnAp = apNone()
|
||||
exists(ReturnKindExt kind |
|
||||
revFlowOut(_, node, kind, state, _, _, ap, config) and
|
||||
if returnFlowsThrough(node, kind, state, _, _, _, ap, config)
|
||||
then (
|
||||
returnCtx = TReturnCtxMaybeFlowThrough(kind) and
|
||||
returnAp = apSome(ap)
|
||||
) else (
|
||||
returnCtx = TReturnCtxNoFlowThrough() and returnAp = apNone()
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate revFlowStore(
|
||||
Ap ap0, Content c, Ap ap, NodeEx node, FlowState state, TypedContent tc, NodeEx mid,
|
||||
boolean toReturn, ApOption returnAp, Configuration config
|
||||
ReturnCtx returnCtx, ApOption returnAp, Configuration config
|
||||
) {
|
||||
revFlow(mid, state, toReturn, returnAp, ap0, config) and
|
||||
revFlow(mid, state, returnCtx, returnAp, ap0, config) and
|
||||
storeStepFwd(node, ap, tc, mid, ap0, config) and
|
||||
tc.getContent() = c
|
||||
}
|
||||
@@ -1599,35 +1677,27 @@ private module MkStage<StageSig PrevStage> {
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate revFlowOut(
|
||||
DataFlowCall call, RetNodeEx ret, FlowState state, boolean toReturn, ApOption returnAp, Ap ap,
|
||||
Configuration config
|
||||
DataFlowCall call, RetNodeEx ret, ReturnKindExt kind, FlowState state, ReturnCtx returnCtx,
|
||||
ApOption returnAp, Ap ap, Configuration config
|
||||
) {
|
||||
exists(NodeEx out, boolean allowsFieldFlow |
|
||||
revFlow(out, state, toReturn, returnAp, ap, config) and
|
||||
flowOutOfCall(call, ret, out, allowsFieldFlow, config) and
|
||||
if allowsFieldFlow = false then ap instanceof ApNil else any()
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate revFlowInNotToReturn(
|
||||
ArgNodeEx arg, FlowState state, ApOption returnAp, Ap ap, Configuration config
|
||||
) {
|
||||
exists(ParamNodeEx p, boolean allowsFieldFlow |
|
||||
revFlow(p, state, false, returnAp, ap, config) and
|
||||
flowIntoCall(_, arg, p, allowsFieldFlow, config) and
|
||||
revFlow(out, state, returnCtx, returnAp, ap, config) and
|
||||
flowOutOfCall(call, ret, kind, out, allowsFieldFlow, config) and
|
||||
if allowsFieldFlow = false then ap instanceof ApNil else any()
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate revFlowInToReturn(
|
||||
DataFlowCall call, ArgNodeEx arg, FlowState state, Ap returnAp, Ap ap, Configuration config
|
||||
DataFlowCall call, ArgNodeEx arg, FlowState state, ReturnKindExt kind, Ap returnAp, Ap ap,
|
||||
Configuration config
|
||||
) {
|
||||
exists(ParamNodeEx p, boolean allowsFieldFlow |
|
||||
revFlow(p, state, true, apSome(returnAp), ap, config) and
|
||||
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()
|
||||
(if allowsFieldFlow = false then ap instanceof ApNil else any()) and
|
||||
parameterFlowThroughAllowed(p, kind)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1638,11 +1708,12 @@ private module MkStage<StageSig PrevStage> {
|
||||
*/
|
||||
pragma[nomagic]
|
||||
private predicate revFlowIsReturned(
|
||||
DataFlowCall call, boolean toReturn, ApOption returnAp, Ap ap, Configuration config
|
||||
DataFlowCall call, ReturnCtx returnCtx, ApOption returnAp, ReturnKindExt kind, Ap ap,
|
||||
Configuration config
|
||||
) {
|
||||
exists(RetNodeEx ret, FlowState state, CcCall ccc |
|
||||
revFlowOut(call, ret, state, toReturn, returnAp, ap, config) and
|
||||
fwdFlow(ret, state, ccc, apSome(_), ap, config) and
|
||||
revFlowOut(call, ret, kind, state, returnCtx, returnAp, ap, config) and
|
||||
returnFlowsThrough(ret, kind, state, ccc, _, _, ap, config) and
|
||||
matchesCall(ccc, call)
|
||||
)
|
||||
}
|
||||
@@ -1713,40 +1784,39 @@ private module MkStage<StageSig PrevStage> {
|
||||
validAp(ap, config)
|
||||
}
|
||||
|
||||
pragma[noinline]
|
||||
private predicate parameterFlow(
|
||||
ParamNodeEx p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config
|
||||
pragma[nomagic]
|
||||
private predicate parameterFlowsThroughRev(
|
||||
ParamNodeEx p, Ap ap, ReturnKindExt kind, Configuration config
|
||||
) {
|
||||
revFlow(p, _, true, apSome(ap0), ap, config) and
|
||||
c = p.getEnclosingCallable()
|
||||
revFlow(p, _, TReturnCtxMaybeFlowThrough(kind), apSome(_), ap, config) and
|
||||
parameterFlowThroughAllowed(p, kind)
|
||||
}
|
||||
|
||||
predicate parameterMayFlowThrough(ParamNodeEx p, DataFlowCallable c, Ap ap, Configuration config) {
|
||||
exists(RetNodeEx ret, FlowState state, Ap ap0, ReturnKindExt kind, ParameterPosition pos |
|
||||
parameterFlow(p, ap, ap0, c, config) and
|
||||
c = ret.getEnclosingCallable() and
|
||||
revFlow(pragma[only_bind_into](ret), pragma[only_bind_into](state), true, apSome(_),
|
||||
pragma[only_bind_into](ap0), pragma[only_bind_into](config)) and
|
||||
fwdFlow(ret, state, any(CcCall ccc), apSome(ap), ap0, config) and
|
||||
kind = ret.getKind() and
|
||||
p.getPosition() = pos and
|
||||
// we don't expect a parameter to return stored in itself, unless explicitly allowed
|
||||
(
|
||||
not kind.(ParamUpdateReturnKind).getPosition() = pos
|
||||
or
|
||||
p.allowParameterReturnInSelf()
|
||||
)
|
||||
pragma[nomagic]
|
||||
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)
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
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(
|
||||
Ap returnAp0, ArgNodeEx arg, FlowState state, boolean toReturn, ApOption returnAp, Ap ap
|
||||
ReturnKindExt returnKind0, Ap returnAp0, ArgNodeEx arg, FlowState state,
|
||||
ReturnCtx returnCtx, ApOption returnAp, Ap ap
|
||||
|
|
||||
revFlow(arg, state, toReturn, returnAp, ap, config) and
|
||||
revFlowInToReturn(call, arg, state, returnAp0, ap, config) and
|
||||
revFlowIsReturned(call, toReturn, returnAp, returnAp0, config)
|
||||
revFlow(arg, state, returnCtx, returnAp, ap, config) and
|
||||
revFlowInToReturn(call, arg, state, returnKind0, returnAp0, ap, config) and
|
||||
revFlowIsReturned(call, returnCtx, returnAp, returnKind0, returnAp0, config)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1754,14 +1824,13 @@ private module MkStage<StageSig PrevStage> {
|
||||
boolean fwd, int nodes, int fields, int conscand, int states, int tuples, Configuration config
|
||||
) {
|
||||
fwd = true and
|
||||
nodes = count(NodeEx node | fwdFlow(node, _, _, _, _, config)) and
|
||||
nodes = count(NodeEx node | fwdFlow(node, _, _, _, _, _, config)) and
|
||||
fields = count(TypedContent f0 | fwdConsCand(f0, _, config)) and
|
||||
conscand = count(TypedContent f0, Ap ap | fwdConsCand(f0, ap, config)) and
|
||||
states = count(FlowState state | fwdFlow(_, state, _, _, _, config)) and
|
||||
states = count(FlowState state | fwdFlow(_, state, _, _, _, _, config)) and
|
||||
tuples =
|
||||
count(NodeEx n, FlowState state, Cc cc, ApOption argAp, Ap ap |
|
||||
fwdFlow(n, state, cc, argAp, ap, config)
|
||||
)
|
||||
count(NodeEx n, FlowState state, Cc cc, ParameterPositionOption summaryCtx, ApOption argAp,
|
||||
Ap ap | fwdFlow(n, state, cc, summaryCtx, argAp, ap, config))
|
||||
or
|
||||
fwd = false and
|
||||
nodes = count(NodeEx node | revFlow(node, _, _, _, _, config)) and
|
||||
@@ -1769,8 +1838,8 @@ private module MkStage<StageSig PrevStage> {
|
||||
conscand = count(TypedContent f0, Ap ap | consCand(f0, ap, config)) and
|
||||
states = count(FlowState state | revFlow(_, state, _, _, _, config)) and
|
||||
tuples =
|
||||
count(NodeEx n, FlowState state, boolean b, ApOption retAp, Ap ap |
|
||||
revFlow(n, state, b, retAp, ap, config)
|
||||
count(NodeEx n, FlowState state, ReturnCtx returnCtx, ApOption retAp, Ap ap |
|
||||
revFlow(n, state, returnCtx, retAp, ap, config)
|
||||
)
|
||||
}
|
||||
/* End: Stage logic. */
|
||||
@@ -1915,7 +1984,7 @@ private module Stage2Param implements MkStage<Stage1>::StageParam {
|
||||
exists(lcc)
|
||||
}
|
||||
|
||||
predicate flowOutOfCall = flowOutOfCallNodeCand1/5;
|
||||
predicate flowOutOfCall = flowOutOfCallNodeCand1/6;
|
||||
|
||||
predicate flowIntoCall = flowIntoCallNodeCand1/5;
|
||||
|
||||
@@ -1951,9 +2020,10 @@ private module Stage2 implements StageSig {
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate flowOutOfCallNodeCand2(
|
||||
DataFlowCall call, RetNodeEx node1, NodeEx node2, boolean allowsFieldFlow, Configuration config
|
||||
DataFlowCall call, RetNodeEx node1, ReturnKindExt kind, NodeEx node2, boolean allowsFieldFlow,
|
||||
Configuration config
|
||||
) {
|
||||
flowOutOfCallNodeCand1(call, node1, node2, allowsFieldFlow, config) and
|
||||
flowOutOfCallNodeCand1(call, node1, kind, node2, allowsFieldFlow, config) and
|
||||
Stage2::revFlow(node2, pragma[only_bind_into](config)) and
|
||||
Stage2::revFlowAlias(node1, pragma[only_bind_into](config))
|
||||
}
|
||||
@@ -2021,8 +2091,8 @@ private module LocalFlowBigStep {
|
||||
exists(NodeEx next | Stage2::revFlow(next, state, config) |
|
||||
jumpStep(node, next, config) or
|
||||
additionalJumpStep(node, next, config) or
|
||||
flowIntoCallNodeCand1(_, node, next, config) or
|
||||
flowOutOfCallNodeCand1(_, node, next, config) or
|
||||
flowIntoCallNodeCand2(_, node, next, _, config) or
|
||||
flowOutOfCallNodeCand2(_, node, _, next, _, config) or
|
||||
Stage2::storeStepCand(node, _, _, next, _, config) or
|
||||
Stage2::readStepCand(node, _, next, config)
|
||||
)
|
||||
@@ -2163,7 +2233,7 @@ private module Stage3Param implements MkStage<Stage2>::StageParam {
|
||||
localFlowBigStep(node1, state1, node2, state2, preservesValue, ap, config, _) and exists(lcc)
|
||||
}
|
||||
|
||||
predicate flowOutOfCall = flowOutOfCallNodeCand2/5;
|
||||
predicate flowOutOfCall = flowOutOfCallNodeCand2/6;
|
||||
|
||||
predicate flowIntoCall = flowIntoCallNodeCand2/5;
|
||||
|
||||
@@ -2233,8 +2303,9 @@ private predicate flowCandSummaryCtx(
|
||||
NodeEx node, FlowState state, AccessPathFront argApf, Configuration config
|
||||
) {
|
||||
exists(AccessPathFront apf |
|
||||
Stage3::revFlow(node, state, true, _, apf, config) and
|
||||
Stage3::fwdFlow(node, state, any(Stage3::CcCall ccc), TAccessPathFrontSome(argApf), apf, config)
|
||||
Stage3::revFlow(node, state, TReturnCtxMaybeFlowThrough(_), _, apf, config) and
|
||||
Stage3::fwdFlow(node, state, any(Stage3::CcCall ccc), _, TAccessPathFrontSome(argApf), apf,
|
||||
config)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -2468,10 +2539,11 @@ private module Stage4Param implements MkStage<Stage3>::StageParam {
|
||||
|
||||
pragma[nomagic]
|
||||
predicate flowOutOfCall(
|
||||
DataFlowCall call, RetNodeEx node1, NodeEx node2, boolean allowsFieldFlow, Configuration config
|
||||
DataFlowCall call, RetNodeEx node1, ReturnKindExt kind, NodeEx node2, boolean allowsFieldFlow,
|
||||
Configuration config
|
||||
) {
|
||||
exists(FlowState state |
|
||||
flowOutOfCallNodeCand2(call, node1, node2, allowsFieldFlow, config) and
|
||||
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))
|
||||
@@ -2508,13 +2580,14 @@ private Configuration unbindConf(Configuration conf) {
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate nodeMayUseSummary0(
|
||||
NodeEx n, DataFlowCallable c, FlowState state, AccessPathApprox apa, Configuration config
|
||||
NodeEx n, DataFlowCallable c, ParameterPosition pos, FlowState state, AccessPathApprox apa,
|
||||
Configuration config
|
||||
) {
|
||||
exists(AccessPathApprox apa0 |
|
||||
Stage4::parameterMayFlowThrough(_, c, _, _) and
|
||||
Stage4::revFlow(n, state, true, _, apa0, config) and
|
||||
Stage4::fwdFlow(n, state, any(CallContextCall ccc), TAccessPathApproxSome(apa), apa0, config) and
|
||||
n.getEnclosingCallable() = c
|
||||
c = n.getEnclosingCallable() and
|
||||
Stage4::revFlow(n, state, TReturnCtxMaybeFlowThrough(_), _, apa0, config) and
|
||||
Stage4::fwdFlow(n, state, any(CallContextCall ccc), TParameterPositionSome(pos),
|
||||
TAccessPathApproxSome(apa), apa0, config)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -2522,9 +2595,10 @@ pragma[nomagic]
|
||||
private predicate nodeMayUseSummary(
|
||||
NodeEx n, FlowState state, AccessPathApprox apa, Configuration config
|
||||
) {
|
||||
exists(DataFlowCallable c |
|
||||
Stage4::parameterMayFlowThrough(_, c, apa, config) and
|
||||
nodeMayUseSummary0(n, c, state, apa, config)
|
||||
exists(DataFlowCallable c, ParameterPosition pos, ParamNodeEx p |
|
||||
Stage4::parameterMayFlowThrough(p, apa, config) and
|
||||
nodeMayUseSummary0(n, c, pos, state, apa, config) and
|
||||
p.isParameterOf(c, pos)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -2532,7 +2606,7 @@ private newtype TSummaryCtx =
|
||||
TSummaryCtxNone() or
|
||||
TSummaryCtxSome(ParamNodeEx p, FlowState state, AccessPath ap) {
|
||||
exists(Configuration config |
|
||||
Stage4::parameterMayFlowThrough(p, _, ap.getApprox(), config) and
|
||||
Stage4::parameterMayFlowThrough(p, ap.getApprox(), config) and
|
||||
Stage4::revFlow(p, state, _, config)
|
||||
)
|
||||
}
|
||||
@@ -3453,17 +3527,11 @@ private predicate paramFlowsThrough(
|
||||
ReturnKindExt kind, FlowState state, CallContextCall cc, SummaryCtxSome sc, AccessPath ap,
|
||||
AccessPathApprox apa, Configuration config
|
||||
) {
|
||||
exists(PathNodeMid mid, RetNodeEx ret, ParameterPosition pos |
|
||||
exists(PathNodeMid mid, RetNodeEx ret |
|
||||
pathNode(mid, ret, state, cc, sc, ap, config, _) and
|
||||
kind = ret.getKind() and
|
||||
apa = ap.getApprox() and
|
||||
pos = sc.getParameterPos() and
|
||||
// we don't expect a parameter to return stored in itself, unless explicitly allowed
|
||||
(
|
||||
not kind.(ParamUpdateReturnKind).getPosition() = pos
|
||||
or
|
||||
sc.getParamNode().allowParameterReturnInSelf()
|
||||
)
|
||||
parameterFlowThroughAllowed(sc.getParamNode(), kind)
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@@ -915,6 +915,17 @@ private module Cached {
|
||||
TDataFlowCallNone() or
|
||||
TDataFlowCallSome(DataFlowCall call)
|
||||
|
||||
cached
|
||||
newtype TParameterPositionOption =
|
||||
TParameterPositionNone() or
|
||||
TParameterPositionSome(ParameterPosition pos)
|
||||
|
||||
cached
|
||||
newtype TReturnCtx =
|
||||
TReturnCtxNone() or
|
||||
TReturnCtxNoFlowThrough() or
|
||||
TReturnCtxMaybeFlowThrough(ReturnKindExt kind)
|
||||
|
||||
cached
|
||||
newtype TTypedContent = MkTypedContent(Content c, DataFlowType t) { store(_, c, _, _, t) }
|
||||
|
||||
@@ -1304,6 +1315,44 @@ class DataFlowCallOption extends TDataFlowCallOption {
|
||||
}
|
||||
}
|
||||
|
||||
/** An optional `ParameterPosition`. */
|
||||
class ParameterPositionOption extends TParameterPositionOption {
|
||||
string toString() {
|
||||
this = TParameterPositionNone() and
|
||||
result = "(none)"
|
||||
or
|
||||
exists(ParameterPosition pos |
|
||||
this = TParameterPositionSome(pos) and
|
||||
result = pos.toString()
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A return context used to calculate flow summaries in reverse flow.
|
||||
*
|
||||
* The possible values are:
|
||||
*
|
||||
* - `TReturnCtxNone()`: no return flow.
|
||||
* - `TReturnCtxNoFlowThrough()`: return flow, but flow through is not possible.
|
||||
* - `TReturnCtxMaybeFlowThrough(ReturnKindExt kind)`: return flow, of kind `kind`, and
|
||||
* flow through may be possible.
|
||||
*/
|
||||
class ReturnCtx extends TReturnCtx {
|
||||
string toString() {
|
||||
this = TReturnCtxNone() and
|
||||
result = "(none)"
|
||||
or
|
||||
this = TReturnCtxNoFlowThrough() and
|
||||
result = "(no flow through)"
|
||||
or
|
||||
exists(ReturnKindExt kind |
|
||||
this = TReturnCtxMaybeFlowThrough(kind) and
|
||||
result = kind.toString()
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/** A `Content` tagged with the type of a containing object. */
|
||||
class TypedContent extends MkTypedContent {
|
||||
private Content c;
|
||||
|
||||
@@ -244,4 +244,20 @@ module Consistency {
|
||||
not callable = viableCallable(call) and
|
||||
not any(ConsistencyConfiguration c).viableImplInCallContextTooLargeExclude(call, ctx, callable)
|
||||
}
|
||||
|
||||
query predicate uniqueParameterNodeAtPosition(
|
||||
DataFlowCallable c, ParameterPosition pos, Node p, string msg
|
||||
) {
|
||||
isParameterNode(p, c, pos) and
|
||||
not exists(unique(Node p0 | isParameterNode(p0, c, pos))) and
|
||||
msg = "Parameters with overlapping positions."
|
||||
}
|
||||
|
||||
query predicate uniqueParameterNodePosition(
|
||||
DataFlowCallable c, ParameterPosition pos, Node p, string msg
|
||||
) {
|
||||
isParameterNode(p, c, pos) and
|
||||
not exists(unique(ParameterPosition pos0 | isParameterNode(p, c, pos0))) and
|
||||
msg = "Parameter node with multiple positions."
|
||||
}
|
||||
}
|
||||
|
||||
@@ -189,18 +189,6 @@ class Folder extends Container, @folder {
|
||||
* Gets the URL of this folder.
|
||||
*/
|
||||
deprecated override string getURL() { result = "file://" + this.getAbsolutePath() + ":0:0:0:0" }
|
||||
|
||||
/**
|
||||
* DEPRECATED: use `getAbsolutePath` instead.
|
||||
* Gets the name of this folder.
|
||||
*/
|
||||
deprecated string getName() { folders(underlyingElement(this), result) }
|
||||
|
||||
/**
|
||||
* DEPRECATED: use `getBaseName` instead.
|
||||
* Gets the last part of the folder name.
|
||||
*/
|
||||
deprecated string getShortName() { result = this.getBaseName() }
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -70,7 +70,7 @@ abstract class Configuration extends string {
|
||||
/**
|
||||
* Holds if `sink` is a relevant data flow sink accepting `state`.
|
||||
*/
|
||||
predicate isSink(Node source, FlowState state) { none() }
|
||||
predicate isSink(Node sink, FlowState state) { none() }
|
||||
|
||||
/**
|
||||
* Holds if data flow through `node` is prohibited. This completely removes
|
||||
@@ -319,8 +319,6 @@ private class ParamNodeEx extends NodeEx {
|
||||
}
|
||||
|
||||
ParameterPosition getPosition() { this.isParameterOf(_, result) }
|
||||
|
||||
predicate allowParameterReturnInSelf() { allowParameterReturnInSelfCached(this.asNode()) }
|
||||
}
|
||||
|
||||
private class RetNodeEx extends NodeEx {
|
||||
@@ -608,6 +606,38 @@ private predicate hasSinkCallCtx(Configuration config) {
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if flow from `p` to a return node of kind `kind` is allowed.
|
||||
*
|
||||
* We don't expect a parameter to return stored in itself, unless
|
||||
* explicitly allowed
|
||||
*/
|
||||
bindingset[p, kind]
|
||||
private predicate parameterFlowThroughAllowed(ParamNodeEx p, ReturnKindExt kind) {
|
||||
exists(ParameterPosition pos | p.isParameterOf(_, pos) |
|
||||
not kind.(ParamUpdateReturnKind).getPosition() = pos
|
||||
or
|
||||
allowParameterReturnInSelfCached(p.asNode())
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* 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;
|
||||
|
||||
@@ -981,21 +1011,22 @@ private module Stage1 implements StageSig {
|
||||
* candidate for the origin of a summary.
|
||||
*/
|
||||
pragma[nomagic]
|
||||
predicate parameterMayFlowThrough(ParamNodeEx p, DataFlowCallable c, Ap ap, Configuration config) {
|
||||
exists(ReturnKindExt kind |
|
||||
predicate parameterMayFlowThrough(ParamNodeEx p, Ap ap, Configuration config) {
|
||||
exists(DataFlowCallable c, ReturnKindExt kind |
|
||||
throughFlowNodeCand(p, config) and
|
||||
returnFlowCallableNodeCand(c, kind, config) and
|
||||
p.getEnclosingCallable() = c and
|
||||
exists(ap) and
|
||||
// we don't expect a parameter to return stored in itself, unless explicitly allowed
|
||||
(
|
||||
not kind.(ParamUpdateReturnKind).getPosition() = p.getPosition()
|
||||
or
|
||||
p.allowParameterReturnInSelf()
|
||||
)
|
||||
parameterFlowThroughAllowed(p, kind)
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
predicate returnMayFlowThrough(RetNodeEx ret, ReturnKindExt kind, Configuration config) {
|
||||
throughFlowNodeCand(ret, config) and
|
||||
kind = ret.getKind()
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
predicate callMayFlowThroughRev(DataFlowCall call, Configuration config) {
|
||||
exists(ArgNodeEx arg, boolean toReturn |
|
||||
@@ -1052,12 +1083,16 @@ private predicate viableReturnPosOutNodeCand1(
|
||||
*/
|
||||
pragma[nomagic]
|
||||
private predicate flowOutOfCallNodeCand1(
|
||||
DataFlowCall call, RetNodeEx ret, NodeEx out, Configuration config
|
||||
DataFlowCall call, RetNodeEx ret, ReturnKindExt kind, NodeEx out, Configuration config
|
||||
) {
|
||||
viableReturnPosOutNodeCand1(call, ret.getReturnPosition(), out, config) and
|
||||
Stage1::revFlow(ret, config) and
|
||||
not outBarrier(ret, config) and
|
||||
not inBarrier(out, config)
|
||||
exists(ReturnPosition pos |
|
||||
viableReturnPosOutNodeCand1(call, pos, out, config) and
|
||||
pos = ret.getReturnPosition() and
|
||||
kind = pos.getKind() and
|
||||
Stage1::revFlow(ret, config) and
|
||||
not outBarrier(ret, config) and
|
||||
not inBarrier(out, config)
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
@@ -1087,10 +1122,11 @@ private predicate flowIntoCallNodeCand1(
|
||||
* edge in the graph of paths between sources and sinks that ignores call
|
||||
* contexts.
|
||||
*/
|
||||
pragma[nomagic]
|
||||
private int branch(NodeEx n1, Configuration conf) {
|
||||
result =
|
||||
strictcount(NodeEx n |
|
||||
flowOutOfCallNodeCand1(_, n1, n, conf) or flowIntoCallNodeCand1(_, n1, n, conf)
|
||||
flowOutOfCallNodeCand1(_, n1, _, n, conf) or flowIntoCallNodeCand1(_, n1, n, conf)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1099,10 +1135,11 @@ private int branch(NodeEx n1, Configuration conf) {
|
||||
* edge in the graph of paths between sources and sinks that ignores call
|
||||
* contexts.
|
||||
*/
|
||||
pragma[nomagic]
|
||||
private int join(NodeEx n2, Configuration conf) {
|
||||
result =
|
||||
strictcount(NodeEx n |
|
||||
flowOutOfCallNodeCand1(_, n, n2, conf) or flowIntoCallNodeCand1(_, n, n2, conf)
|
||||
flowOutOfCallNodeCand1(_, n, _, n2, conf) or flowIntoCallNodeCand1(_, n, n2, conf)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1115,12 +1152,13 @@ private int join(NodeEx n2, Configuration conf) {
|
||||
*/
|
||||
pragma[nomagic]
|
||||
private predicate flowOutOfCallNodeCand1(
|
||||
DataFlowCall call, RetNodeEx ret, NodeEx out, boolean allowsFieldFlow, Configuration config
|
||||
DataFlowCall call, RetNodeEx ret, ReturnKindExt kind, NodeEx out, boolean allowsFieldFlow,
|
||||
Configuration config
|
||||
) {
|
||||
flowOutOfCallNodeCand1(call, ret, out, config) and
|
||||
flowOutOfCallNodeCand1(call, ret, kind, out, pragma[only_bind_into](config)) and
|
||||
exists(int b, int j |
|
||||
b = branch(ret, config) and
|
||||
j = join(out, config) and
|
||||
b = branch(ret, pragma[only_bind_into](config)) and
|
||||
j = join(out, pragma[only_bind_into](config)) and
|
||||
if b.minimum(j) <= config.fieldFlowBranchLimit()
|
||||
then allowsFieldFlow = true
|
||||
else allowsFieldFlow = false
|
||||
@@ -1136,10 +1174,10 @@ pragma[nomagic]
|
||||
private predicate flowIntoCallNodeCand1(
|
||||
DataFlowCall call, ArgNodeEx arg, ParamNodeEx p, boolean allowsFieldFlow, Configuration config
|
||||
) {
|
||||
flowIntoCallNodeCand1(call, arg, p, config) and
|
||||
flowIntoCallNodeCand1(call, arg, p, pragma[only_bind_into](config)) and
|
||||
exists(int b, int j |
|
||||
b = branch(arg, config) and
|
||||
j = join(p, config) and
|
||||
b = branch(arg, pragma[only_bind_into](config)) and
|
||||
j = join(p, pragma[only_bind_into](config)) and
|
||||
if b.minimum(j) <= config.fieldFlowBranchLimit()
|
||||
then allowsFieldFlow = true
|
||||
else allowsFieldFlow = false
|
||||
@@ -1156,7 +1194,9 @@ private signature module StageSig {
|
||||
|
||||
predicate callMayFlowThroughRev(DataFlowCall call, Configuration config);
|
||||
|
||||
predicate parameterMayFlowThrough(ParamNodeEx p, DataFlowCallable c, Ap ap, Configuration config);
|
||||
predicate parameterMayFlowThrough(ParamNodeEx p, Ap ap, Configuration config);
|
||||
|
||||
predicate returnMayFlowThrough(RetNodeEx ret, ReturnKindExt kind, Configuration config);
|
||||
|
||||
predicate storeStepCand(
|
||||
NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, DataFlowType contentType,
|
||||
@@ -1222,7 +1262,8 @@ private module MkStage<StageSig PrevStage> {
|
||||
);
|
||||
|
||||
predicate flowOutOfCall(
|
||||
DataFlowCall call, RetNodeEx ret, NodeEx out, boolean allowsFieldFlow, Configuration config
|
||||
DataFlowCall call, RetNodeEx ret, ReturnKindExt kind, NodeEx out, boolean allowsFieldFlow,
|
||||
Configuration config
|
||||
);
|
||||
|
||||
predicate flowIntoCall(
|
||||
@@ -1247,14 +1288,14 @@ private module MkStage<StageSig PrevStage> {
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate flowThroughOutOfCall(
|
||||
DataFlowCall call, CcCall ccc, RetNodeEx ret, NodeEx out, boolean allowsFieldFlow,
|
||||
Configuration config
|
||||
DataFlowCall call, DataFlowCallable c, CcCall ccc, RetNodeEx ret, ReturnKindExt kind,
|
||||
NodeEx out, boolean allowsFieldFlow, Configuration config
|
||||
) {
|
||||
flowOutOfCall(call, ret, out, allowsFieldFlow, pragma[only_bind_into](config)) and
|
||||
flowOutOfCall(call, ret, kind, out, allowsFieldFlow, pragma[only_bind_into](config)) and
|
||||
PrevStage::callMayFlowThroughRev(call, pragma[only_bind_into](config)) and
|
||||
PrevStage::parameterMayFlowThrough(_, ret.getEnclosingCallable(), _,
|
||||
pragma[only_bind_into](config)) and
|
||||
matchesCall(ccc, call)
|
||||
PrevStage::returnMayFlowThrough(ret, kind, pragma[only_bind_into](config)) and
|
||||
matchesCall(ccc, call) and
|
||||
c = ret.getEnclosingCallable()
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1262,29 +1303,32 @@ private module MkStage<StageSig PrevStage> {
|
||||
* configuration `config`.
|
||||
*
|
||||
* The call context `cc` records whether the node is reached through an
|
||||
* argument in a call, and if so, `argAp` records the access path of that
|
||||
* argument.
|
||||
* argument in a call, and if so, `summaryCtx` and `argAp` record the
|
||||
* corresponding parameter position and access path of that argument, respectively.
|
||||
*/
|
||||
pragma[nomagic]
|
||||
additional predicate fwdFlow(
|
||||
NodeEx node, FlowState state, Cc cc, ApOption argAp, Ap ap, Configuration config
|
||||
NodeEx node, FlowState state, Cc cc, ParameterPositionOption summaryCtx, ApOption argAp,
|
||||
Ap ap, Configuration config
|
||||
) {
|
||||
fwdFlow0(node, state, cc, argAp, ap, config) and
|
||||
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, ApOption argAp, Ap ap, Configuration config
|
||||
NodeEx node, FlowState state, Cc cc, ParameterPositionOption summaryCtx, ApOption argAp,
|
||||
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)
|
||||
or
|
||||
exists(NodeEx mid, FlowState state0, Ap ap0, LocalCc localCc |
|
||||
fwdFlow(mid, state0, cc, argAp, ap0, config) and
|
||||
fwdFlow(mid, state0, cc, summaryCtx, argAp, ap0, config) and
|
||||
localCc = getLocalCc(mid, cc)
|
||||
|
|
||||
localStep(mid, state0, node, state, true, _, config, localCc) and
|
||||
@@ -1295,65 +1339,82 @@ private module MkStage<StageSig PrevStage> {
|
||||
)
|
||||
or
|
||||
exists(NodeEx mid |
|
||||
fwdFlow(mid, pragma[only_bind_into](state), _, _, ap, 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
|
||||
argAp = apNone()
|
||||
)
|
||||
or
|
||||
exists(NodeEx mid, ApNil nil |
|
||||
fwdFlow(mid, state, _, _, nil, pragma[only_bind_into](config)) and
|
||||
fwdFlow(mid, state, _, _, _, nil, pragma[only_bind_into](config)) and
|
||||
additionalJumpStep(mid, node, config) and
|
||||
cc = ccNone() and
|
||||
summaryCtx = TParameterPositionNone() and
|
||||
argAp = apNone() and
|
||||
ap = getApNil(node)
|
||||
)
|
||||
or
|
||||
exists(NodeEx mid, FlowState state0, ApNil nil |
|
||||
fwdFlow(mid, state0, _, _, nil, pragma[only_bind_into](config)) and
|
||||
fwdFlow(mid, state0, _, _, _, nil, pragma[only_bind_into](config)) and
|
||||
additionalJumpStateStep(mid, state0, node, state, config) and
|
||||
cc = ccNone() and
|
||||
summaryCtx = TParameterPositionNone() and
|
||||
argAp = apNone() and
|
||||
ap = getApNil(node)
|
||||
)
|
||||
or
|
||||
// store
|
||||
exists(TypedContent tc, Ap ap0 |
|
||||
fwdFlowStore(_, ap0, tc, node, state, cc, argAp, config) and
|
||||
fwdFlowStore(_, ap0, tc, node, state, cc, summaryCtx, argAp, config) and
|
||||
ap = apCons(tc, ap0)
|
||||
)
|
||||
or
|
||||
// read
|
||||
exists(Ap ap0, Content c |
|
||||
fwdFlowRead(ap0, c, _, node, state, cc, argAp, config) and
|
||||
fwdFlowRead(ap0, c, _, node, state, cc, summaryCtx, argAp, config) and
|
||||
fwdFlowConsCand(ap0, c, ap, config)
|
||||
)
|
||||
or
|
||||
// flow into a callable
|
||||
exists(ApApprox apa |
|
||||
fwdFlowIn(_, node, state, _, cc, _, ap, config) and
|
||||
fwdFlowIn(_, node, state, _, cc, _, _, ap, config) and
|
||||
apa = getApprox(ap) and
|
||||
if PrevStage::parameterMayFlowThrough(node, _, apa, config)
|
||||
then argAp = apSome(ap)
|
||||
else argAp = apNone()
|
||||
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
|
||||
fwdFlowOutNotFromArg(node, state, cc, argAp, ap, config)
|
||||
exists(
|
||||
DataFlowCall call, RetNodeEx ret, boolean allowsFieldFlow, CcNoCall innercc,
|
||||
DataFlowCallable inner
|
||||
|
|
||||
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()
|
||||
)
|
||||
or
|
||||
exists(DataFlowCall call, Ap argAp0 |
|
||||
fwdFlowOutFromArg(call, node, state, argAp0, ap, config) and
|
||||
fwdFlowIsEntered(call, cc, argAp, argAp0, config)
|
||||
// flow through a callable
|
||||
exists(DataFlowCall call, ParameterPosition summaryCtx0, Ap argAp0 |
|
||||
fwdFlowOutFromArg(call, node, state, summaryCtx0, argAp0, ap, config) and
|
||||
fwdFlowIsEntered(call, cc, summaryCtx, argAp, summaryCtx0, argAp0, config)
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate fwdFlowStore(
|
||||
NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, FlowState state, Cc cc, ApOption argAp,
|
||||
Configuration config
|
||||
NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, FlowState state, Cc cc,
|
||||
ParameterPositionOption summaryCtx, ApOption argAp, Configuration config
|
||||
) {
|
||||
exists(DataFlowType contentType |
|
||||
fwdFlow(node1, state, cc, argAp, ap1, config) and
|
||||
fwdFlow(node1, state, cc, summaryCtx, argAp, ap1, config) and
|
||||
PrevStage::storeStepCand(node1, unbindApa(getApprox(ap1)), tc, node2, contentType, config) and
|
||||
typecheckStore(ap1, contentType)
|
||||
)
|
||||
@@ -1366,7 +1427,7 @@ private module MkStage<StageSig PrevStage> {
|
||||
pragma[nomagic]
|
||||
private predicate fwdFlowConsCand(Ap cons, Content c, Ap tail, Configuration config) {
|
||||
exists(TypedContent tc |
|
||||
fwdFlowStore(_, tail, tc, _, _, _, _, config) and
|
||||
fwdFlowStore(_, tail, tc, _, _, _, _, _, config) and
|
||||
tc.getContent() = c and
|
||||
cons = apCons(tc, tail)
|
||||
)
|
||||
@@ -1374,21 +1435,21 @@ private module MkStage<StageSig PrevStage> {
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate fwdFlowRead(
|
||||
Ap ap, Content c, NodeEx node1, NodeEx node2, FlowState state, Cc cc, ApOption argAp,
|
||||
Configuration config
|
||||
Ap ap, Content c, NodeEx node1, NodeEx node2, FlowState state, Cc cc,
|
||||
ParameterPositionOption summaryCtx, ApOption argAp, Configuration config
|
||||
) {
|
||||
fwdFlow(node1, state, cc, 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, Cc innercc, ApOption argAp,
|
||||
Ap ap, 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, argAp, ap, 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()
|
||||
@@ -1396,29 +1457,19 @@ private module MkStage<StageSig PrevStage> {
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate fwdFlowOutNotFromArg(
|
||||
NodeEx out, FlowState state, Cc ccOut, ApOption argAp, Ap ap, Configuration config
|
||||
private predicate fwdFlowOutFromArg(
|
||||
DataFlowCall call, NodeEx out, FlowState state, ParameterPosition summaryCtx, Ap argAp, Ap ap,
|
||||
Configuration config
|
||||
) {
|
||||
exists(
|
||||
DataFlowCall call, RetNodeEx ret, boolean allowsFieldFlow, CcNoCall innercc,
|
||||
DataFlowCallable inner
|
||||
DataFlowCallable c, RetNodeEx ret, ReturnKindExt kind, boolean allowsFieldFlow, CcCall ccc
|
||||
|
|
||||
fwdFlow(ret, state, innercc, argAp, ap, config) and
|
||||
flowOutOfCall(call, ret, out, allowsFieldFlow, config) and
|
||||
inner = ret.getEnclosingCallable() and
|
||||
ccOut = getCallContextReturn(inner, call, innercc) and
|
||||
if allowsFieldFlow = false then ap instanceof ApNil else any()
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate fwdFlowOutFromArg(
|
||||
DataFlowCall call, NodeEx out, FlowState state, Ap argAp, Ap ap, Configuration config
|
||||
) {
|
||||
exists(RetNodeEx ret, boolean allowsFieldFlow, CcCall ccc |
|
||||
fwdFlow(ret, state, ccc, apSome(argAp), ap, config) and
|
||||
flowThroughOutOfCall(call, ccc, ret, out, allowsFieldFlow, config) and
|
||||
if allowsFieldFlow = false then ap instanceof ApNil else any()
|
||||
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
|
||||
(if allowsFieldFlow = false then ap instanceof ApNil else any()) and
|
||||
parameterFlowThroughAllowed(c, pragma[only_bind_into](summaryCtx), kind)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1428,11 +1479,13 @@ private module MkStage<StageSig PrevStage> {
|
||||
*/
|
||||
pragma[nomagic]
|
||||
private predicate fwdFlowIsEntered(
|
||||
DataFlowCall call, Cc cc, ApOption argAp, Ap ap, Configuration config
|
||||
DataFlowCall call, Cc cc, ParameterPositionOption summaryCtx, ApOption argAp,
|
||||
ParameterPosition pos, Ap ap, Configuration config
|
||||
) {
|
||||
exists(ParamNodeEx p |
|
||||
fwdFlowIn(call, p, _, cc, _, argAp, ap, config) and
|
||||
PrevStage::parameterMayFlowThrough(p, _, unbindApa(getApprox(ap)), config)
|
||||
exists(ParamNodeEx param |
|
||||
fwdFlowIn(call, param, _, cc, _, summaryCtx, argAp, ap, config) and
|
||||
PrevStage::parameterMayFlowThrough(param, unbindApa(getApprox(ap)), config) and
|
||||
pos = param.getPosition()
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1440,27 +1493,40 @@ private module MkStage<StageSig PrevStage> {
|
||||
private predicate storeStepFwd(
|
||||
NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, Ap ap2, Configuration config
|
||||
) {
|
||||
fwdFlowStore(node1, ap1, tc, node2, _, _, _, config) and
|
||||
fwdFlowStore(node1, ap1, tc, node2, _, _, _, _, config) and
|
||||
ap2 = apCons(tc, ap1) and
|
||||
fwdFlowRead(ap2, tc.getContent(), _, _, _, _, _, config)
|
||||
fwdFlowRead(ap2, tc.getContent(), _, _, _, _, _, _, config)
|
||||
}
|
||||
|
||||
private predicate readStepFwd(
|
||||
NodeEx n1, Ap ap1, Content c, NodeEx n2, Ap ap2, Configuration config
|
||||
) {
|
||||
fwdFlowRead(ap1, c, n1, n2, _, _, _, config) and
|
||||
fwdFlowRead(ap1, c, n1, n2, _, _, _, _, config) and
|
||||
fwdFlowConsCand(ap1, c, ap2, config)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate callMayFlowThroughFwd(DataFlowCall call, Configuration config) {
|
||||
exists(Ap argAp0, NodeEx out, FlowState state, Cc cc, ApOption argAp, Ap ap |
|
||||
fwdFlow(out, state, pragma[only_bind_into](cc), pragma[only_bind_into](argAp), ap,
|
||||
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
|
||||
fwdFlowOutFromArg(call, out, state, argAp0, ap, config) and
|
||||
fwdFlowIsEntered(pragma[only_bind_into](call), pragma[only_bind_into](cc),
|
||||
pragma[only_bind_into](argAp), pragma[only_bind_into](argAp0),
|
||||
pragma[only_bind_into](config))
|
||||
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(DataFlowCallable c, ParameterPosition ppos |
|
||||
returnFlowsThrough0(ret, kind, state, ccc, c, ppos, argAp, ap, config) and
|
||||
p.isParameterOf(c, ppos) and
|
||||
parameterFlowThroughAllowed(p, kind)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1468,118 +1534,130 @@ private module MkStage<StageSig PrevStage> {
|
||||
private predicate flowThroughIntoCall(
|
||||
DataFlowCall call, ArgNodeEx arg, ParamNodeEx p, boolean allowsFieldFlow, Configuration config
|
||||
) {
|
||||
flowIntoCall(call, arg, p, allowsFieldFlow, config) and
|
||||
fwdFlow(arg, _, _, _, _, pragma[only_bind_into](config)) and
|
||||
PrevStage::parameterMayFlowThrough(p, _, _, pragma[only_bind_into](config)) and
|
||||
callMayFlowThroughFwd(call, pragma[only_bind_into](config))
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate returnNodeMayFlowThrough(
|
||||
RetNodeEx ret, FlowState state, Ap ap, Configuration config
|
||||
) {
|
||||
fwdFlow(ret, state, any(CcCall ccc), apSome(_), ap, config)
|
||||
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))
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `node` with access path `ap` is part of a path from a source to a
|
||||
* sink in the configuration `config`.
|
||||
*
|
||||
* The Boolean `toReturn` records whether the node must be returned from the
|
||||
* enclosing callable in order to reach a sink, and if so, `returnAp` records
|
||||
* the access path of the returned value.
|
||||
* The parameter `returnCtx` records whether (and how) the node must be returned
|
||||
* from the enclosing callable in order to reach a sink, and if so, `returnAp`
|
||||
* records the access path of the returned value.
|
||||
*/
|
||||
pragma[nomagic]
|
||||
additional predicate revFlow(
|
||||
NodeEx node, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, Configuration config
|
||||
NodeEx node, FlowState state, ReturnCtx returnCtx, ApOption returnAp, Ap ap,
|
||||
Configuration config
|
||||
) {
|
||||
revFlow0(node, state, toReturn, returnAp, ap, config) and
|
||||
fwdFlow(node, state, _, _, ap, config)
|
||||
revFlow0(node, state, returnCtx, returnAp, ap, config) and
|
||||
fwdFlow(node, state, _, _, _, ap, config)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate revFlow0(
|
||||
NodeEx node, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, Configuration config
|
||||
NodeEx node, FlowState state, ReturnCtx returnCtx, ApOption returnAp, Ap ap,
|
||||
Configuration config
|
||||
) {
|
||||
fwdFlow(node, state, _, _, ap, config) and
|
||||
fwdFlow(node, state, _, _, _, ap, config) and
|
||||
sinkNode(node, state, config) and
|
||||
(if hasSinkCallCtx(config) then toReturn = true else toReturn = false) and
|
||||
(
|
||||
if hasSinkCallCtx(config)
|
||||
then returnCtx = TReturnCtxNoFlowThrough()
|
||||
else returnCtx = TReturnCtxNone()
|
||||
) and
|
||||
returnAp = apNone() and
|
||||
ap instanceof ApNil
|
||||
or
|
||||
exists(NodeEx mid, FlowState state0 |
|
||||
localStep(node, state, mid, state0, true, _, config, _) and
|
||||
revFlow(mid, state0, toReturn, returnAp, ap, config)
|
||||
revFlow(mid, state0, returnCtx, returnAp, ap, config)
|
||||
)
|
||||
or
|
||||
exists(NodeEx mid, FlowState state0, ApNil nil |
|
||||
fwdFlow(node, pragma[only_bind_into](state), _, _, ap, pragma[only_bind_into](config)) and
|
||||
fwdFlow(node, pragma[only_bind_into](state), _, _, _, ap, pragma[only_bind_into](config)) and
|
||||
localStep(node, pragma[only_bind_into](state), mid, state0, false, _, config, _) and
|
||||
revFlow(mid, state0, toReturn, returnAp, nil, pragma[only_bind_into](config)) and
|
||||
revFlow(mid, state0, returnCtx, returnAp, nil, pragma[only_bind_into](config)) and
|
||||
ap instanceof ApNil
|
||||
)
|
||||
or
|
||||
exists(NodeEx mid |
|
||||
jumpStep(node, mid, config) and
|
||||
revFlow(mid, state, _, _, ap, config) and
|
||||
toReturn = false and
|
||||
returnCtx = TReturnCtxNone() and
|
||||
returnAp = apNone()
|
||||
)
|
||||
or
|
||||
exists(NodeEx mid, ApNil nil |
|
||||
fwdFlow(node, _, _, _, ap, pragma[only_bind_into](config)) and
|
||||
fwdFlow(node, _, _, _, _, ap, pragma[only_bind_into](config)) and
|
||||
additionalJumpStep(node, mid, config) and
|
||||
revFlow(pragma[only_bind_into](mid), state, _, _, nil, pragma[only_bind_into](config)) and
|
||||
toReturn = false and
|
||||
returnCtx = TReturnCtxNone() and
|
||||
returnAp = apNone() and
|
||||
ap instanceof ApNil
|
||||
)
|
||||
or
|
||||
exists(NodeEx mid, FlowState state0, ApNil nil |
|
||||
fwdFlow(node, _, _, _, ap, pragma[only_bind_into](config)) and
|
||||
fwdFlow(node, _, _, _, _, ap, pragma[only_bind_into](config)) and
|
||||
additionalJumpStateStep(node, state, mid, state0, config) and
|
||||
revFlow(pragma[only_bind_into](mid), pragma[only_bind_into](state0), _, _, nil,
|
||||
pragma[only_bind_into](config)) and
|
||||
toReturn = false and
|
||||
returnCtx = TReturnCtxNone() and
|
||||
returnAp = apNone() and
|
||||
ap instanceof ApNil
|
||||
)
|
||||
or
|
||||
// store
|
||||
exists(Ap ap0, Content c |
|
||||
revFlowStore(ap0, c, ap, node, state, _, _, toReturn, returnAp, config) and
|
||||
revFlowStore(ap0, c, ap, node, state, _, _, returnCtx, returnAp, config) and
|
||||
revFlowConsCand(ap0, c, ap, config)
|
||||
)
|
||||
or
|
||||
// read
|
||||
exists(NodeEx mid, Ap ap0 |
|
||||
revFlow(mid, state, toReturn, returnAp, ap0, config) and
|
||||
revFlow(mid, state, returnCtx, returnAp, ap0, config) and
|
||||
readStepFwd(node, ap, _, mid, ap0, config)
|
||||
)
|
||||
or
|
||||
// flow into a callable
|
||||
revFlowInNotToReturn(node, state, returnAp, ap, config) and
|
||||
toReturn = false
|
||||
exists(ParamNodeEx p, boolean allowsFieldFlow |
|
||||
revFlow(p, state, TReturnCtxNone(), returnAp, ap, config) and
|
||||
flowIntoCall(_, node, p, allowsFieldFlow, config) and
|
||||
(if allowsFieldFlow = false then ap instanceof ApNil else any()) and
|
||||
returnCtx = TReturnCtxNone()
|
||||
)
|
||||
or
|
||||
exists(DataFlowCall call, Ap returnAp0 |
|
||||
revFlowInToReturn(call, node, state, returnAp0, ap, config) and
|
||||
revFlowIsReturned(call, toReturn, returnAp, returnAp0, config)
|
||||
// flow through a callable
|
||||
exists(DataFlowCall call, ReturnKindExt returnKind0, Ap returnAp0 |
|
||||
revFlowInToReturn(call, node, state, returnKind0, returnAp0, ap, config) and
|
||||
revFlowIsReturned(call, returnCtx, returnAp, returnKind0, returnAp0, config)
|
||||
)
|
||||
or
|
||||
// flow out of a callable
|
||||
revFlowOut(_, node, state, _, _, ap, config) and
|
||||
toReturn = true and
|
||||
if returnNodeMayFlowThrough(node, state, ap, config)
|
||||
then returnAp = apSome(ap)
|
||||
else returnAp = apNone()
|
||||
exists(ReturnKindExt kind |
|
||||
revFlowOut(_, node, kind, state, _, _, ap, config) and
|
||||
if returnFlowsThrough(node, kind, state, _, _, _, ap, config)
|
||||
then (
|
||||
returnCtx = TReturnCtxMaybeFlowThrough(kind) and
|
||||
returnAp = apSome(ap)
|
||||
) else (
|
||||
returnCtx = TReturnCtxNoFlowThrough() and returnAp = apNone()
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate revFlowStore(
|
||||
Ap ap0, Content c, Ap ap, NodeEx node, FlowState state, TypedContent tc, NodeEx mid,
|
||||
boolean toReturn, ApOption returnAp, Configuration config
|
||||
ReturnCtx returnCtx, ApOption returnAp, Configuration config
|
||||
) {
|
||||
revFlow(mid, state, toReturn, returnAp, ap0, config) and
|
||||
revFlow(mid, state, returnCtx, returnAp, ap0, config) and
|
||||
storeStepFwd(node, ap, tc, mid, ap0, config) and
|
||||
tc.getContent() = c
|
||||
}
|
||||
@@ -1599,35 +1677,27 @@ private module MkStage<StageSig PrevStage> {
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate revFlowOut(
|
||||
DataFlowCall call, RetNodeEx ret, FlowState state, boolean toReturn, ApOption returnAp, Ap ap,
|
||||
Configuration config
|
||||
DataFlowCall call, RetNodeEx ret, ReturnKindExt kind, FlowState state, ReturnCtx returnCtx,
|
||||
ApOption returnAp, Ap ap, Configuration config
|
||||
) {
|
||||
exists(NodeEx out, boolean allowsFieldFlow |
|
||||
revFlow(out, state, toReturn, returnAp, ap, config) and
|
||||
flowOutOfCall(call, ret, out, allowsFieldFlow, config) and
|
||||
if allowsFieldFlow = false then ap instanceof ApNil else any()
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate revFlowInNotToReturn(
|
||||
ArgNodeEx arg, FlowState state, ApOption returnAp, Ap ap, Configuration config
|
||||
) {
|
||||
exists(ParamNodeEx p, boolean allowsFieldFlow |
|
||||
revFlow(p, state, false, returnAp, ap, config) and
|
||||
flowIntoCall(_, arg, p, allowsFieldFlow, config) and
|
||||
revFlow(out, state, returnCtx, returnAp, ap, config) and
|
||||
flowOutOfCall(call, ret, kind, out, allowsFieldFlow, config) and
|
||||
if allowsFieldFlow = false then ap instanceof ApNil else any()
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate revFlowInToReturn(
|
||||
DataFlowCall call, ArgNodeEx arg, FlowState state, Ap returnAp, Ap ap, Configuration config
|
||||
DataFlowCall call, ArgNodeEx arg, FlowState state, ReturnKindExt kind, Ap returnAp, Ap ap,
|
||||
Configuration config
|
||||
) {
|
||||
exists(ParamNodeEx p, boolean allowsFieldFlow |
|
||||
revFlow(p, state, true, apSome(returnAp), ap, config) and
|
||||
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()
|
||||
(if allowsFieldFlow = false then ap instanceof ApNil else any()) and
|
||||
parameterFlowThroughAllowed(p, kind)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1638,11 +1708,12 @@ private module MkStage<StageSig PrevStage> {
|
||||
*/
|
||||
pragma[nomagic]
|
||||
private predicate revFlowIsReturned(
|
||||
DataFlowCall call, boolean toReturn, ApOption returnAp, Ap ap, Configuration config
|
||||
DataFlowCall call, ReturnCtx returnCtx, ApOption returnAp, ReturnKindExt kind, Ap ap,
|
||||
Configuration config
|
||||
) {
|
||||
exists(RetNodeEx ret, FlowState state, CcCall ccc |
|
||||
revFlowOut(call, ret, state, toReturn, returnAp, ap, config) and
|
||||
fwdFlow(ret, state, ccc, apSome(_), ap, config) and
|
||||
revFlowOut(call, ret, kind, state, returnCtx, returnAp, ap, config) and
|
||||
returnFlowsThrough(ret, kind, state, ccc, _, _, ap, config) and
|
||||
matchesCall(ccc, call)
|
||||
)
|
||||
}
|
||||
@@ -1713,40 +1784,39 @@ private module MkStage<StageSig PrevStage> {
|
||||
validAp(ap, config)
|
||||
}
|
||||
|
||||
pragma[noinline]
|
||||
private predicate parameterFlow(
|
||||
ParamNodeEx p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config
|
||||
pragma[nomagic]
|
||||
private predicate parameterFlowsThroughRev(
|
||||
ParamNodeEx p, Ap ap, ReturnKindExt kind, Configuration config
|
||||
) {
|
||||
revFlow(p, _, true, apSome(ap0), ap, config) and
|
||||
c = p.getEnclosingCallable()
|
||||
revFlow(p, _, TReturnCtxMaybeFlowThrough(kind), apSome(_), ap, config) and
|
||||
parameterFlowThroughAllowed(p, kind)
|
||||
}
|
||||
|
||||
predicate parameterMayFlowThrough(ParamNodeEx p, DataFlowCallable c, Ap ap, Configuration config) {
|
||||
exists(RetNodeEx ret, FlowState state, Ap ap0, ReturnKindExt kind, ParameterPosition pos |
|
||||
parameterFlow(p, ap, ap0, c, config) and
|
||||
c = ret.getEnclosingCallable() and
|
||||
revFlow(pragma[only_bind_into](ret), pragma[only_bind_into](state), true, apSome(_),
|
||||
pragma[only_bind_into](ap0), pragma[only_bind_into](config)) and
|
||||
fwdFlow(ret, state, any(CcCall ccc), apSome(ap), ap0, config) and
|
||||
kind = ret.getKind() and
|
||||
p.getPosition() = pos and
|
||||
// we don't expect a parameter to return stored in itself, unless explicitly allowed
|
||||
(
|
||||
not kind.(ParamUpdateReturnKind).getPosition() = pos
|
||||
or
|
||||
p.allowParameterReturnInSelf()
|
||||
)
|
||||
pragma[nomagic]
|
||||
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)
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
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(
|
||||
Ap returnAp0, ArgNodeEx arg, FlowState state, boolean toReturn, ApOption returnAp, Ap ap
|
||||
ReturnKindExt returnKind0, Ap returnAp0, ArgNodeEx arg, FlowState state,
|
||||
ReturnCtx returnCtx, ApOption returnAp, Ap ap
|
||||
|
|
||||
revFlow(arg, state, toReturn, returnAp, ap, config) and
|
||||
revFlowInToReturn(call, arg, state, returnAp0, ap, config) and
|
||||
revFlowIsReturned(call, toReturn, returnAp, returnAp0, config)
|
||||
revFlow(arg, state, returnCtx, returnAp, ap, config) and
|
||||
revFlowInToReturn(call, arg, state, returnKind0, returnAp0, ap, config) and
|
||||
revFlowIsReturned(call, returnCtx, returnAp, returnKind0, returnAp0, config)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1754,14 +1824,13 @@ private module MkStage<StageSig PrevStage> {
|
||||
boolean fwd, int nodes, int fields, int conscand, int states, int tuples, Configuration config
|
||||
) {
|
||||
fwd = true and
|
||||
nodes = count(NodeEx node | fwdFlow(node, _, _, _, _, config)) and
|
||||
nodes = count(NodeEx node | fwdFlow(node, _, _, _, _, _, config)) and
|
||||
fields = count(TypedContent f0 | fwdConsCand(f0, _, config)) and
|
||||
conscand = count(TypedContent f0, Ap ap | fwdConsCand(f0, ap, config)) and
|
||||
states = count(FlowState state | fwdFlow(_, state, _, _, _, config)) and
|
||||
states = count(FlowState state | fwdFlow(_, state, _, _, _, _, config)) and
|
||||
tuples =
|
||||
count(NodeEx n, FlowState state, Cc cc, ApOption argAp, Ap ap |
|
||||
fwdFlow(n, state, cc, argAp, ap, config)
|
||||
)
|
||||
count(NodeEx n, FlowState state, Cc cc, ParameterPositionOption summaryCtx, ApOption argAp,
|
||||
Ap ap | fwdFlow(n, state, cc, summaryCtx, argAp, ap, config))
|
||||
or
|
||||
fwd = false and
|
||||
nodes = count(NodeEx node | revFlow(node, _, _, _, _, config)) and
|
||||
@@ -1769,8 +1838,8 @@ private module MkStage<StageSig PrevStage> {
|
||||
conscand = count(TypedContent f0, Ap ap | consCand(f0, ap, config)) and
|
||||
states = count(FlowState state | revFlow(_, state, _, _, _, config)) and
|
||||
tuples =
|
||||
count(NodeEx n, FlowState state, boolean b, ApOption retAp, Ap ap |
|
||||
revFlow(n, state, b, retAp, ap, config)
|
||||
count(NodeEx n, FlowState state, ReturnCtx returnCtx, ApOption retAp, Ap ap |
|
||||
revFlow(n, state, returnCtx, retAp, ap, config)
|
||||
)
|
||||
}
|
||||
/* End: Stage logic. */
|
||||
@@ -1915,7 +1984,7 @@ private module Stage2Param implements MkStage<Stage1>::StageParam {
|
||||
exists(lcc)
|
||||
}
|
||||
|
||||
predicate flowOutOfCall = flowOutOfCallNodeCand1/5;
|
||||
predicate flowOutOfCall = flowOutOfCallNodeCand1/6;
|
||||
|
||||
predicate flowIntoCall = flowIntoCallNodeCand1/5;
|
||||
|
||||
@@ -1951,9 +2020,10 @@ private module Stage2 implements StageSig {
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate flowOutOfCallNodeCand2(
|
||||
DataFlowCall call, RetNodeEx node1, NodeEx node2, boolean allowsFieldFlow, Configuration config
|
||||
DataFlowCall call, RetNodeEx node1, ReturnKindExt kind, NodeEx node2, boolean allowsFieldFlow,
|
||||
Configuration config
|
||||
) {
|
||||
flowOutOfCallNodeCand1(call, node1, node2, allowsFieldFlow, config) and
|
||||
flowOutOfCallNodeCand1(call, node1, kind, node2, allowsFieldFlow, config) and
|
||||
Stage2::revFlow(node2, pragma[only_bind_into](config)) and
|
||||
Stage2::revFlowAlias(node1, pragma[only_bind_into](config))
|
||||
}
|
||||
@@ -2021,8 +2091,8 @@ private module LocalFlowBigStep {
|
||||
exists(NodeEx next | Stage2::revFlow(next, state, config) |
|
||||
jumpStep(node, next, config) or
|
||||
additionalJumpStep(node, next, config) or
|
||||
flowIntoCallNodeCand1(_, node, next, config) or
|
||||
flowOutOfCallNodeCand1(_, node, next, config) or
|
||||
flowIntoCallNodeCand2(_, node, next, _, config) or
|
||||
flowOutOfCallNodeCand2(_, node, _, next, _, config) or
|
||||
Stage2::storeStepCand(node, _, _, next, _, config) or
|
||||
Stage2::readStepCand(node, _, next, config)
|
||||
)
|
||||
@@ -2163,7 +2233,7 @@ private module Stage3Param implements MkStage<Stage2>::StageParam {
|
||||
localFlowBigStep(node1, state1, node2, state2, preservesValue, ap, config, _) and exists(lcc)
|
||||
}
|
||||
|
||||
predicate flowOutOfCall = flowOutOfCallNodeCand2/5;
|
||||
predicate flowOutOfCall = flowOutOfCallNodeCand2/6;
|
||||
|
||||
predicate flowIntoCall = flowIntoCallNodeCand2/5;
|
||||
|
||||
@@ -2233,8 +2303,9 @@ private predicate flowCandSummaryCtx(
|
||||
NodeEx node, FlowState state, AccessPathFront argApf, Configuration config
|
||||
) {
|
||||
exists(AccessPathFront apf |
|
||||
Stage3::revFlow(node, state, true, _, apf, config) and
|
||||
Stage3::fwdFlow(node, state, any(Stage3::CcCall ccc), TAccessPathFrontSome(argApf), apf, config)
|
||||
Stage3::revFlow(node, state, TReturnCtxMaybeFlowThrough(_), _, apf, config) and
|
||||
Stage3::fwdFlow(node, state, any(Stage3::CcCall ccc), _, TAccessPathFrontSome(argApf), apf,
|
||||
config)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -2468,10 +2539,11 @@ private module Stage4Param implements MkStage<Stage3>::StageParam {
|
||||
|
||||
pragma[nomagic]
|
||||
predicate flowOutOfCall(
|
||||
DataFlowCall call, RetNodeEx node1, NodeEx node2, boolean allowsFieldFlow, Configuration config
|
||||
DataFlowCall call, RetNodeEx node1, ReturnKindExt kind, NodeEx node2, boolean allowsFieldFlow,
|
||||
Configuration config
|
||||
) {
|
||||
exists(FlowState state |
|
||||
flowOutOfCallNodeCand2(call, node1, node2, allowsFieldFlow, config) and
|
||||
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))
|
||||
@@ -2508,13 +2580,14 @@ private Configuration unbindConf(Configuration conf) {
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate nodeMayUseSummary0(
|
||||
NodeEx n, DataFlowCallable c, FlowState state, AccessPathApprox apa, Configuration config
|
||||
NodeEx n, DataFlowCallable c, ParameterPosition pos, FlowState state, AccessPathApprox apa,
|
||||
Configuration config
|
||||
) {
|
||||
exists(AccessPathApprox apa0 |
|
||||
Stage4::parameterMayFlowThrough(_, c, _, _) and
|
||||
Stage4::revFlow(n, state, true, _, apa0, config) and
|
||||
Stage4::fwdFlow(n, state, any(CallContextCall ccc), TAccessPathApproxSome(apa), apa0, config) and
|
||||
n.getEnclosingCallable() = c
|
||||
c = n.getEnclosingCallable() and
|
||||
Stage4::revFlow(n, state, TReturnCtxMaybeFlowThrough(_), _, apa0, config) and
|
||||
Stage4::fwdFlow(n, state, any(CallContextCall ccc), TParameterPositionSome(pos),
|
||||
TAccessPathApproxSome(apa), apa0, config)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -2522,9 +2595,10 @@ pragma[nomagic]
|
||||
private predicate nodeMayUseSummary(
|
||||
NodeEx n, FlowState state, AccessPathApprox apa, Configuration config
|
||||
) {
|
||||
exists(DataFlowCallable c |
|
||||
Stage4::parameterMayFlowThrough(_, c, apa, config) and
|
||||
nodeMayUseSummary0(n, c, state, apa, config)
|
||||
exists(DataFlowCallable c, ParameterPosition pos, ParamNodeEx p |
|
||||
Stage4::parameterMayFlowThrough(p, apa, config) and
|
||||
nodeMayUseSummary0(n, c, pos, state, apa, config) and
|
||||
p.isParameterOf(c, pos)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -2532,7 +2606,7 @@ private newtype TSummaryCtx =
|
||||
TSummaryCtxNone() or
|
||||
TSummaryCtxSome(ParamNodeEx p, FlowState state, AccessPath ap) {
|
||||
exists(Configuration config |
|
||||
Stage4::parameterMayFlowThrough(p, _, ap.getApprox(), config) and
|
||||
Stage4::parameterMayFlowThrough(p, ap.getApprox(), config) and
|
||||
Stage4::revFlow(p, state, _, config)
|
||||
)
|
||||
}
|
||||
@@ -3453,17 +3527,11 @@ private predicate paramFlowsThrough(
|
||||
ReturnKindExt kind, FlowState state, CallContextCall cc, SummaryCtxSome sc, AccessPath ap,
|
||||
AccessPathApprox apa, Configuration config
|
||||
) {
|
||||
exists(PathNodeMid mid, RetNodeEx ret, ParameterPosition pos |
|
||||
exists(PathNodeMid mid, RetNodeEx ret |
|
||||
pathNode(mid, ret, state, cc, sc, ap, config, _) and
|
||||
kind = ret.getKind() and
|
||||
apa = ap.getApprox() and
|
||||
pos = sc.getParameterPos() and
|
||||
// we don't expect a parameter to return stored in itself, unless explicitly allowed
|
||||
(
|
||||
not kind.(ParamUpdateReturnKind).getPosition() = pos
|
||||
or
|
||||
sc.getParamNode().allowParameterReturnInSelf()
|
||||
)
|
||||
parameterFlowThroughAllowed(sc.getParamNode(), kind)
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@@ -70,7 +70,7 @@ abstract class Configuration extends string {
|
||||
/**
|
||||
* Holds if `sink` is a relevant data flow sink accepting `state`.
|
||||
*/
|
||||
predicate isSink(Node source, FlowState state) { none() }
|
||||
predicate isSink(Node sink, FlowState state) { none() }
|
||||
|
||||
/**
|
||||
* Holds if data flow through `node` is prohibited. This completely removes
|
||||
@@ -319,8 +319,6 @@ private class ParamNodeEx extends NodeEx {
|
||||
}
|
||||
|
||||
ParameterPosition getPosition() { this.isParameterOf(_, result) }
|
||||
|
||||
predicate allowParameterReturnInSelf() { allowParameterReturnInSelfCached(this.asNode()) }
|
||||
}
|
||||
|
||||
private class RetNodeEx extends NodeEx {
|
||||
@@ -608,6 +606,38 @@ private predicate hasSinkCallCtx(Configuration config) {
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if flow from `p` to a return node of kind `kind` is allowed.
|
||||
*
|
||||
* We don't expect a parameter to return stored in itself, unless
|
||||
* explicitly allowed
|
||||
*/
|
||||
bindingset[p, kind]
|
||||
private predicate parameterFlowThroughAllowed(ParamNodeEx p, ReturnKindExt kind) {
|
||||
exists(ParameterPosition pos | p.isParameterOf(_, pos) |
|
||||
not kind.(ParamUpdateReturnKind).getPosition() = pos
|
||||
or
|
||||
allowParameterReturnInSelfCached(p.asNode())
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* 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;
|
||||
|
||||
@@ -981,21 +1011,22 @@ private module Stage1 implements StageSig {
|
||||
* candidate for the origin of a summary.
|
||||
*/
|
||||
pragma[nomagic]
|
||||
predicate parameterMayFlowThrough(ParamNodeEx p, DataFlowCallable c, Ap ap, Configuration config) {
|
||||
exists(ReturnKindExt kind |
|
||||
predicate parameterMayFlowThrough(ParamNodeEx p, Ap ap, Configuration config) {
|
||||
exists(DataFlowCallable c, ReturnKindExt kind |
|
||||
throughFlowNodeCand(p, config) and
|
||||
returnFlowCallableNodeCand(c, kind, config) and
|
||||
p.getEnclosingCallable() = c and
|
||||
exists(ap) and
|
||||
// we don't expect a parameter to return stored in itself, unless explicitly allowed
|
||||
(
|
||||
not kind.(ParamUpdateReturnKind).getPosition() = p.getPosition()
|
||||
or
|
||||
p.allowParameterReturnInSelf()
|
||||
)
|
||||
parameterFlowThroughAllowed(p, kind)
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
predicate returnMayFlowThrough(RetNodeEx ret, ReturnKindExt kind, Configuration config) {
|
||||
throughFlowNodeCand(ret, config) and
|
||||
kind = ret.getKind()
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
predicate callMayFlowThroughRev(DataFlowCall call, Configuration config) {
|
||||
exists(ArgNodeEx arg, boolean toReturn |
|
||||
@@ -1052,12 +1083,16 @@ private predicate viableReturnPosOutNodeCand1(
|
||||
*/
|
||||
pragma[nomagic]
|
||||
private predicate flowOutOfCallNodeCand1(
|
||||
DataFlowCall call, RetNodeEx ret, NodeEx out, Configuration config
|
||||
DataFlowCall call, RetNodeEx ret, ReturnKindExt kind, NodeEx out, Configuration config
|
||||
) {
|
||||
viableReturnPosOutNodeCand1(call, ret.getReturnPosition(), out, config) and
|
||||
Stage1::revFlow(ret, config) and
|
||||
not outBarrier(ret, config) and
|
||||
not inBarrier(out, config)
|
||||
exists(ReturnPosition pos |
|
||||
viableReturnPosOutNodeCand1(call, pos, out, config) and
|
||||
pos = ret.getReturnPosition() and
|
||||
kind = pos.getKind() and
|
||||
Stage1::revFlow(ret, config) and
|
||||
not outBarrier(ret, config) and
|
||||
not inBarrier(out, config)
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
@@ -1087,10 +1122,11 @@ private predicate flowIntoCallNodeCand1(
|
||||
* edge in the graph of paths between sources and sinks that ignores call
|
||||
* contexts.
|
||||
*/
|
||||
pragma[nomagic]
|
||||
private int branch(NodeEx n1, Configuration conf) {
|
||||
result =
|
||||
strictcount(NodeEx n |
|
||||
flowOutOfCallNodeCand1(_, n1, n, conf) or flowIntoCallNodeCand1(_, n1, n, conf)
|
||||
flowOutOfCallNodeCand1(_, n1, _, n, conf) or flowIntoCallNodeCand1(_, n1, n, conf)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1099,10 +1135,11 @@ private int branch(NodeEx n1, Configuration conf) {
|
||||
* edge in the graph of paths between sources and sinks that ignores call
|
||||
* contexts.
|
||||
*/
|
||||
pragma[nomagic]
|
||||
private int join(NodeEx n2, Configuration conf) {
|
||||
result =
|
||||
strictcount(NodeEx n |
|
||||
flowOutOfCallNodeCand1(_, n, n2, conf) or flowIntoCallNodeCand1(_, n, n2, conf)
|
||||
flowOutOfCallNodeCand1(_, n, _, n2, conf) or flowIntoCallNodeCand1(_, n, n2, conf)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1115,12 +1152,13 @@ private int join(NodeEx n2, Configuration conf) {
|
||||
*/
|
||||
pragma[nomagic]
|
||||
private predicate flowOutOfCallNodeCand1(
|
||||
DataFlowCall call, RetNodeEx ret, NodeEx out, boolean allowsFieldFlow, Configuration config
|
||||
DataFlowCall call, RetNodeEx ret, ReturnKindExt kind, NodeEx out, boolean allowsFieldFlow,
|
||||
Configuration config
|
||||
) {
|
||||
flowOutOfCallNodeCand1(call, ret, out, config) and
|
||||
flowOutOfCallNodeCand1(call, ret, kind, out, pragma[only_bind_into](config)) and
|
||||
exists(int b, int j |
|
||||
b = branch(ret, config) and
|
||||
j = join(out, config) and
|
||||
b = branch(ret, pragma[only_bind_into](config)) and
|
||||
j = join(out, pragma[only_bind_into](config)) and
|
||||
if b.minimum(j) <= config.fieldFlowBranchLimit()
|
||||
then allowsFieldFlow = true
|
||||
else allowsFieldFlow = false
|
||||
@@ -1136,10 +1174,10 @@ pragma[nomagic]
|
||||
private predicate flowIntoCallNodeCand1(
|
||||
DataFlowCall call, ArgNodeEx arg, ParamNodeEx p, boolean allowsFieldFlow, Configuration config
|
||||
) {
|
||||
flowIntoCallNodeCand1(call, arg, p, config) and
|
||||
flowIntoCallNodeCand1(call, arg, p, pragma[only_bind_into](config)) and
|
||||
exists(int b, int j |
|
||||
b = branch(arg, config) and
|
||||
j = join(p, config) and
|
||||
b = branch(arg, pragma[only_bind_into](config)) and
|
||||
j = join(p, pragma[only_bind_into](config)) and
|
||||
if b.minimum(j) <= config.fieldFlowBranchLimit()
|
||||
then allowsFieldFlow = true
|
||||
else allowsFieldFlow = false
|
||||
@@ -1156,7 +1194,9 @@ private signature module StageSig {
|
||||
|
||||
predicate callMayFlowThroughRev(DataFlowCall call, Configuration config);
|
||||
|
||||
predicate parameterMayFlowThrough(ParamNodeEx p, DataFlowCallable c, Ap ap, Configuration config);
|
||||
predicate parameterMayFlowThrough(ParamNodeEx p, Ap ap, Configuration config);
|
||||
|
||||
predicate returnMayFlowThrough(RetNodeEx ret, ReturnKindExt kind, Configuration config);
|
||||
|
||||
predicate storeStepCand(
|
||||
NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, DataFlowType contentType,
|
||||
@@ -1222,7 +1262,8 @@ private module MkStage<StageSig PrevStage> {
|
||||
);
|
||||
|
||||
predicate flowOutOfCall(
|
||||
DataFlowCall call, RetNodeEx ret, NodeEx out, boolean allowsFieldFlow, Configuration config
|
||||
DataFlowCall call, RetNodeEx ret, ReturnKindExt kind, NodeEx out, boolean allowsFieldFlow,
|
||||
Configuration config
|
||||
);
|
||||
|
||||
predicate flowIntoCall(
|
||||
@@ -1247,14 +1288,14 @@ private module MkStage<StageSig PrevStage> {
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate flowThroughOutOfCall(
|
||||
DataFlowCall call, CcCall ccc, RetNodeEx ret, NodeEx out, boolean allowsFieldFlow,
|
||||
Configuration config
|
||||
DataFlowCall call, DataFlowCallable c, CcCall ccc, RetNodeEx ret, ReturnKindExt kind,
|
||||
NodeEx out, boolean allowsFieldFlow, Configuration config
|
||||
) {
|
||||
flowOutOfCall(call, ret, out, allowsFieldFlow, pragma[only_bind_into](config)) and
|
||||
flowOutOfCall(call, ret, kind, out, allowsFieldFlow, pragma[only_bind_into](config)) and
|
||||
PrevStage::callMayFlowThroughRev(call, pragma[only_bind_into](config)) and
|
||||
PrevStage::parameterMayFlowThrough(_, ret.getEnclosingCallable(), _,
|
||||
pragma[only_bind_into](config)) and
|
||||
matchesCall(ccc, call)
|
||||
PrevStage::returnMayFlowThrough(ret, kind, pragma[only_bind_into](config)) and
|
||||
matchesCall(ccc, call) and
|
||||
c = ret.getEnclosingCallable()
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1262,29 +1303,32 @@ private module MkStage<StageSig PrevStage> {
|
||||
* configuration `config`.
|
||||
*
|
||||
* The call context `cc` records whether the node is reached through an
|
||||
* argument in a call, and if so, `argAp` records the access path of that
|
||||
* argument.
|
||||
* argument in a call, and if so, `summaryCtx` and `argAp` record the
|
||||
* corresponding parameter position and access path of that argument, respectively.
|
||||
*/
|
||||
pragma[nomagic]
|
||||
additional predicate fwdFlow(
|
||||
NodeEx node, FlowState state, Cc cc, ApOption argAp, Ap ap, Configuration config
|
||||
NodeEx node, FlowState state, Cc cc, ParameterPositionOption summaryCtx, ApOption argAp,
|
||||
Ap ap, Configuration config
|
||||
) {
|
||||
fwdFlow0(node, state, cc, argAp, ap, config) and
|
||||
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, ApOption argAp, Ap ap, Configuration config
|
||||
NodeEx node, FlowState state, Cc cc, ParameterPositionOption summaryCtx, ApOption argAp,
|
||||
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)
|
||||
or
|
||||
exists(NodeEx mid, FlowState state0, Ap ap0, LocalCc localCc |
|
||||
fwdFlow(mid, state0, cc, argAp, ap0, config) and
|
||||
fwdFlow(mid, state0, cc, summaryCtx, argAp, ap0, config) and
|
||||
localCc = getLocalCc(mid, cc)
|
||||
|
|
||||
localStep(mid, state0, node, state, true, _, config, localCc) and
|
||||
@@ -1295,65 +1339,82 @@ private module MkStage<StageSig PrevStage> {
|
||||
)
|
||||
or
|
||||
exists(NodeEx mid |
|
||||
fwdFlow(mid, pragma[only_bind_into](state), _, _, ap, 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
|
||||
argAp = apNone()
|
||||
)
|
||||
or
|
||||
exists(NodeEx mid, ApNil nil |
|
||||
fwdFlow(mid, state, _, _, nil, pragma[only_bind_into](config)) and
|
||||
fwdFlow(mid, state, _, _, _, nil, pragma[only_bind_into](config)) and
|
||||
additionalJumpStep(mid, node, config) and
|
||||
cc = ccNone() and
|
||||
summaryCtx = TParameterPositionNone() and
|
||||
argAp = apNone() and
|
||||
ap = getApNil(node)
|
||||
)
|
||||
or
|
||||
exists(NodeEx mid, FlowState state0, ApNil nil |
|
||||
fwdFlow(mid, state0, _, _, nil, pragma[only_bind_into](config)) and
|
||||
fwdFlow(mid, state0, _, _, _, nil, pragma[only_bind_into](config)) and
|
||||
additionalJumpStateStep(mid, state0, node, state, config) and
|
||||
cc = ccNone() and
|
||||
summaryCtx = TParameterPositionNone() and
|
||||
argAp = apNone() and
|
||||
ap = getApNil(node)
|
||||
)
|
||||
or
|
||||
// store
|
||||
exists(TypedContent tc, Ap ap0 |
|
||||
fwdFlowStore(_, ap0, tc, node, state, cc, argAp, config) and
|
||||
fwdFlowStore(_, ap0, tc, node, state, cc, summaryCtx, argAp, config) and
|
||||
ap = apCons(tc, ap0)
|
||||
)
|
||||
or
|
||||
// read
|
||||
exists(Ap ap0, Content c |
|
||||
fwdFlowRead(ap0, c, _, node, state, cc, argAp, config) and
|
||||
fwdFlowRead(ap0, c, _, node, state, cc, summaryCtx, argAp, config) and
|
||||
fwdFlowConsCand(ap0, c, ap, config)
|
||||
)
|
||||
or
|
||||
// flow into a callable
|
||||
exists(ApApprox apa |
|
||||
fwdFlowIn(_, node, state, _, cc, _, ap, config) and
|
||||
fwdFlowIn(_, node, state, _, cc, _, _, ap, config) and
|
||||
apa = getApprox(ap) and
|
||||
if PrevStage::parameterMayFlowThrough(node, _, apa, config)
|
||||
then argAp = apSome(ap)
|
||||
else argAp = apNone()
|
||||
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
|
||||
fwdFlowOutNotFromArg(node, state, cc, argAp, ap, config)
|
||||
exists(
|
||||
DataFlowCall call, RetNodeEx ret, boolean allowsFieldFlow, CcNoCall innercc,
|
||||
DataFlowCallable inner
|
||||
|
|
||||
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()
|
||||
)
|
||||
or
|
||||
exists(DataFlowCall call, Ap argAp0 |
|
||||
fwdFlowOutFromArg(call, node, state, argAp0, ap, config) and
|
||||
fwdFlowIsEntered(call, cc, argAp, argAp0, config)
|
||||
// flow through a callable
|
||||
exists(DataFlowCall call, ParameterPosition summaryCtx0, Ap argAp0 |
|
||||
fwdFlowOutFromArg(call, node, state, summaryCtx0, argAp0, ap, config) and
|
||||
fwdFlowIsEntered(call, cc, summaryCtx, argAp, summaryCtx0, argAp0, config)
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate fwdFlowStore(
|
||||
NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, FlowState state, Cc cc, ApOption argAp,
|
||||
Configuration config
|
||||
NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, FlowState state, Cc cc,
|
||||
ParameterPositionOption summaryCtx, ApOption argAp, Configuration config
|
||||
) {
|
||||
exists(DataFlowType contentType |
|
||||
fwdFlow(node1, state, cc, argAp, ap1, config) and
|
||||
fwdFlow(node1, state, cc, summaryCtx, argAp, ap1, config) and
|
||||
PrevStage::storeStepCand(node1, unbindApa(getApprox(ap1)), tc, node2, contentType, config) and
|
||||
typecheckStore(ap1, contentType)
|
||||
)
|
||||
@@ -1366,7 +1427,7 @@ private module MkStage<StageSig PrevStage> {
|
||||
pragma[nomagic]
|
||||
private predicate fwdFlowConsCand(Ap cons, Content c, Ap tail, Configuration config) {
|
||||
exists(TypedContent tc |
|
||||
fwdFlowStore(_, tail, tc, _, _, _, _, config) and
|
||||
fwdFlowStore(_, tail, tc, _, _, _, _, _, config) and
|
||||
tc.getContent() = c and
|
||||
cons = apCons(tc, tail)
|
||||
)
|
||||
@@ -1374,21 +1435,21 @@ private module MkStage<StageSig PrevStage> {
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate fwdFlowRead(
|
||||
Ap ap, Content c, NodeEx node1, NodeEx node2, FlowState state, Cc cc, ApOption argAp,
|
||||
Configuration config
|
||||
Ap ap, Content c, NodeEx node1, NodeEx node2, FlowState state, Cc cc,
|
||||
ParameterPositionOption summaryCtx, ApOption argAp, Configuration config
|
||||
) {
|
||||
fwdFlow(node1, state, cc, 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, Cc innercc, ApOption argAp,
|
||||
Ap ap, 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, argAp, ap, 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()
|
||||
@@ -1396,29 +1457,19 @@ private module MkStage<StageSig PrevStage> {
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate fwdFlowOutNotFromArg(
|
||||
NodeEx out, FlowState state, Cc ccOut, ApOption argAp, Ap ap, Configuration config
|
||||
private predicate fwdFlowOutFromArg(
|
||||
DataFlowCall call, NodeEx out, FlowState state, ParameterPosition summaryCtx, Ap argAp, Ap ap,
|
||||
Configuration config
|
||||
) {
|
||||
exists(
|
||||
DataFlowCall call, RetNodeEx ret, boolean allowsFieldFlow, CcNoCall innercc,
|
||||
DataFlowCallable inner
|
||||
DataFlowCallable c, RetNodeEx ret, ReturnKindExt kind, boolean allowsFieldFlow, CcCall ccc
|
||||
|
|
||||
fwdFlow(ret, state, innercc, argAp, ap, config) and
|
||||
flowOutOfCall(call, ret, out, allowsFieldFlow, config) and
|
||||
inner = ret.getEnclosingCallable() and
|
||||
ccOut = getCallContextReturn(inner, call, innercc) and
|
||||
if allowsFieldFlow = false then ap instanceof ApNil else any()
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate fwdFlowOutFromArg(
|
||||
DataFlowCall call, NodeEx out, FlowState state, Ap argAp, Ap ap, Configuration config
|
||||
) {
|
||||
exists(RetNodeEx ret, boolean allowsFieldFlow, CcCall ccc |
|
||||
fwdFlow(ret, state, ccc, apSome(argAp), ap, config) and
|
||||
flowThroughOutOfCall(call, ccc, ret, out, allowsFieldFlow, config) and
|
||||
if allowsFieldFlow = false then ap instanceof ApNil else any()
|
||||
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
|
||||
(if allowsFieldFlow = false then ap instanceof ApNil else any()) and
|
||||
parameterFlowThroughAllowed(c, pragma[only_bind_into](summaryCtx), kind)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1428,11 +1479,13 @@ private module MkStage<StageSig PrevStage> {
|
||||
*/
|
||||
pragma[nomagic]
|
||||
private predicate fwdFlowIsEntered(
|
||||
DataFlowCall call, Cc cc, ApOption argAp, Ap ap, Configuration config
|
||||
DataFlowCall call, Cc cc, ParameterPositionOption summaryCtx, ApOption argAp,
|
||||
ParameterPosition pos, Ap ap, Configuration config
|
||||
) {
|
||||
exists(ParamNodeEx p |
|
||||
fwdFlowIn(call, p, _, cc, _, argAp, ap, config) and
|
||||
PrevStage::parameterMayFlowThrough(p, _, unbindApa(getApprox(ap)), config)
|
||||
exists(ParamNodeEx param |
|
||||
fwdFlowIn(call, param, _, cc, _, summaryCtx, argAp, ap, config) and
|
||||
PrevStage::parameterMayFlowThrough(param, unbindApa(getApprox(ap)), config) and
|
||||
pos = param.getPosition()
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1440,27 +1493,40 @@ private module MkStage<StageSig PrevStage> {
|
||||
private predicate storeStepFwd(
|
||||
NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, Ap ap2, Configuration config
|
||||
) {
|
||||
fwdFlowStore(node1, ap1, tc, node2, _, _, _, config) and
|
||||
fwdFlowStore(node1, ap1, tc, node2, _, _, _, _, config) and
|
||||
ap2 = apCons(tc, ap1) and
|
||||
fwdFlowRead(ap2, tc.getContent(), _, _, _, _, _, config)
|
||||
fwdFlowRead(ap2, tc.getContent(), _, _, _, _, _, _, config)
|
||||
}
|
||||
|
||||
private predicate readStepFwd(
|
||||
NodeEx n1, Ap ap1, Content c, NodeEx n2, Ap ap2, Configuration config
|
||||
) {
|
||||
fwdFlowRead(ap1, c, n1, n2, _, _, _, config) and
|
||||
fwdFlowRead(ap1, c, n1, n2, _, _, _, _, config) and
|
||||
fwdFlowConsCand(ap1, c, ap2, config)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate callMayFlowThroughFwd(DataFlowCall call, Configuration config) {
|
||||
exists(Ap argAp0, NodeEx out, FlowState state, Cc cc, ApOption argAp, Ap ap |
|
||||
fwdFlow(out, state, pragma[only_bind_into](cc), pragma[only_bind_into](argAp), ap,
|
||||
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
|
||||
fwdFlowOutFromArg(call, out, state, argAp0, ap, config) and
|
||||
fwdFlowIsEntered(pragma[only_bind_into](call), pragma[only_bind_into](cc),
|
||||
pragma[only_bind_into](argAp), pragma[only_bind_into](argAp0),
|
||||
pragma[only_bind_into](config))
|
||||
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(DataFlowCallable c, ParameterPosition ppos |
|
||||
returnFlowsThrough0(ret, kind, state, ccc, c, ppos, argAp, ap, config) and
|
||||
p.isParameterOf(c, ppos) and
|
||||
parameterFlowThroughAllowed(p, kind)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1468,118 +1534,130 @@ private module MkStage<StageSig PrevStage> {
|
||||
private predicate flowThroughIntoCall(
|
||||
DataFlowCall call, ArgNodeEx arg, ParamNodeEx p, boolean allowsFieldFlow, Configuration config
|
||||
) {
|
||||
flowIntoCall(call, arg, p, allowsFieldFlow, config) and
|
||||
fwdFlow(arg, _, _, _, _, pragma[only_bind_into](config)) and
|
||||
PrevStage::parameterMayFlowThrough(p, _, _, pragma[only_bind_into](config)) and
|
||||
callMayFlowThroughFwd(call, pragma[only_bind_into](config))
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate returnNodeMayFlowThrough(
|
||||
RetNodeEx ret, FlowState state, Ap ap, Configuration config
|
||||
) {
|
||||
fwdFlow(ret, state, any(CcCall ccc), apSome(_), ap, config)
|
||||
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))
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `node` with access path `ap` is part of a path from a source to a
|
||||
* sink in the configuration `config`.
|
||||
*
|
||||
* The Boolean `toReturn` records whether the node must be returned from the
|
||||
* enclosing callable in order to reach a sink, and if so, `returnAp` records
|
||||
* the access path of the returned value.
|
||||
* The parameter `returnCtx` records whether (and how) the node must be returned
|
||||
* from the enclosing callable in order to reach a sink, and if so, `returnAp`
|
||||
* records the access path of the returned value.
|
||||
*/
|
||||
pragma[nomagic]
|
||||
additional predicate revFlow(
|
||||
NodeEx node, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, Configuration config
|
||||
NodeEx node, FlowState state, ReturnCtx returnCtx, ApOption returnAp, Ap ap,
|
||||
Configuration config
|
||||
) {
|
||||
revFlow0(node, state, toReturn, returnAp, ap, config) and
|
||||
fwdFlow(node, state, _, _, ap, config)
|
||||
revFlow0(node, state, returnCtx, returnAp, ap, config) and
|
||||
fwdFlow(node, state, _, _, _, ap, config)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate revFlow0(
|
||||
NodeEx node, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, Configuration config
|
||||
NodeEx node, FlowState state, ReturnCtx returnCtx, ApOption returnAp, Ap ap,
|
||||
Configuration config
|
||||
) {
|
||||
fwdFlow(node, state, _, _, ap, config) and
|
||||
fwdFlow(node, state, _, _, _, ap, config) and
|
||||
sinkNode(node, state, config) and
|
||||
(if hasSinkCallCtx(config) then toReturn = true else toReturn = false) and
|
||||
(
|
||||
if hasSinkCallCtx(config)
|
||||
then returnCtx = TReturnCtxNoFlowThrough()
|
||||
else returnCtx = TReturnCtxNone()
|
||||
) and
|
||||
returnAp = apNone() and
|
||||
ap instanceof ApNil
|
||||
or
|
||||
exists(NodeEx mid, FlowState state0 |
|
||||
localStep(node, state, mid, state0, true, _, config, _) and
|
||||
revFlow(mid, state0, toReturn, returnAp, ap, config)
|
||||
revFlow(mid, state0, returnCtx, returnAp, ap, config)
|
||||
)
|
||||
or
|
||||
exists(NodeEx mid, FlowState state0, ApNil nil |
|
||||
fwdFlow(node, pragma[only_bind_into](state), _, _, ap, pragma[only_bind_into](config)) and
|
||||
fwdFlow(node, pragma[only_bind_into](state), _, _, _, ap, pragma[only_bind_into](config)) and
|
||||
localStep(node, pragma[only_bind_into](state), mid, state0, false, _, config, _) and
|
||||
revFlow(mid, state0, toReturn, returnAp, nil, pragma[only_bind_into](config)) and
|
||||
revFlow(mid, state0, returnCtx, returnAp, nil, pragma[only_bind_into](config)) and
|
||||
ap instanceof ApNil
|
||||
)
|
||||
or
|
||||
exists(NodeEx mid |
|
||||
jumpStep(node, mid, config) and
|
||||
revFlow(mid, state, _, _, ap, config) and
|
||||
toReturn = false and
|
||||
returnCtx = TReturnCtxNone() and
|
||||
returnAp = apNone()
|
||||
)
|
||||
or
|
||||
exists(NodeEx mid, ApNil nil |
|
||||
fwdFlow(node, _, _, _, ap, pragma[only_bind_into](config)) and
|
||||
fwdFlow(node, _, _, _, _, ap, pragma[only_bind_into](config)) and
|
||||
additionalJumpStep(node, mid, config) and
|
||||
revFlow(pragma[only_bind_into](mid), state, _, _, nil, pragma[only_bind_into](config)) and
|
||||
toReturn = false and
|
||||
returnCtx = TReturnCtxNone() and
|
||||
returnAp = apNone() and
|
||||
ap instanceof ApNil
|
||||
)
|
||||
or
|
||||
exists(NodeEx mid, FlowState state0, ApNil nil |
|
||||
fwdFlow(node, _, _, _, ap, pragma[only_bind_into](config)) and
|
||||
fwdFlow(node, _, _, _, _, ap, pragma[only_bind_into](config)) and
|
||||
additionalJumpStateStep(node, state, mid, state0, config) and
|
||||
revFlow(pragma[only_bind_into](mid), pragma[only_bind_into](state0), _, _, nil,
|
||||
pragma[only_bind_into](config)) and
|
||||
toReturn = false and
|
||||
returnCtx = TReturnCtxNone() and
|
||||
returnAp = apNone() and
|
||||
ap instanceof ApNil
|
||||
)
|
||||
or
|
||||
// store
|
||||
exists(Ap ap0, Content c |
|
||||
revFlowStore(ap0, c, ap, node, state, _, _, toReturn, returnAp, config) and
|
||||
revFlowStore(ap0, c, ap, node, state, _, _, returnCtx, returnAp, config) and
|
||||
revFlowConsCand(ap0, c, ap, config)
|
||||
)
|
||||
or
|
||||
// read
|
||||
exists(NodeEx mid, Ap ap0 |
|
||||
revFlow(mid, state, toReturn, returnAp, ap0, config) and
|
||||
revFlow(mid, state, returnCtx, returnAp, ap0, config) and
|
||||
readStepFwd(node, ap, _, mid, ap0, config)
|
||||
)
|
||||
or
|
||||
// flow into a callable
|
||||
revFlowInNotToReturn(node, state, returnAp, ap, config) and
|
||||
toReturn = false
|
||||
exists(ParamNodeEx p, boolean allowsFieldFlow |
|
||||
revFlow(p, state, TReturnCtxNone(), returnAp, ap, config) and
|
||||
flowIntoCall(_, node, p, allowsFieldFlow, config) and
|
||||
(if allowsFieldFlow = false then ap instanceof ApNil else any()) and
|
||||
returnCtx = TReturnCtxNone()
|
||||
)
|
||||
or
|
||||
exists(DataFlowCall call, Ap returnAp0 |
|
||||
revFlowInToReturn(call, node, state, returnAp0, ap, config) and
|
||||
revFlowIsReturned(call, toReturn, returnAp, returnAp0, config)
|
||||
// flow through a callable
|
||||
exists(DataFlowCall call, ReturnKindExt returnKind0, Ap returnAp0 |
|
||||
revFlowInToReturn(call, node, state, returnKind0, returnAp0, ap, config) and
|
||||
revFlowIsReturned(call, returnCtx, returnAp, returnKind0, returnAp0, config)
|
||||
)
|
||||
or
|
||||
// flow out of a callable
|
||||
revFlowOut(_, node, state, _, _, ap, config) and
|
||||
toReturn = true and
|
||||
if returnNodeMayFlowThrough(node, state, ap, config)
|
||||
then returnAp = apSome(ap)
|
||||
else returnAp = apNone()
|
||||
exists(ReturnKindExt kind |
|
||||
revFlowOut(_, node, kind, state, _, _, ap, config) and
|
||||
if returnFlowsThrough(node, kind, state, _, _, _, ap, config)
|
||||
then (
|
||||
returnCtx = TReturnCtxMaybeFlowThrough(kind) and
|
||||
returnAp = apSome(ap)
|
||||
) else (
|
||||
returnCtx = TReturnCtxNoFlowThrough() and returnAp = apNone()
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate revFlowStore(
|
||||
Ap ap0, Content c, Ap ap, NodeEx node, FlowState state, TypedContent tc, NodeEx mid,
|
||||
boolean toReturn, ApOption returnAp, Configuration config
|
||||
ReturnCtx returnCtx, ApOption returnAp, Configuration config
|
||||
) {
|
||||
revFlow(mid, state, toReturn, returnAp, ap0, config) and
|
||||
revFlow(mid, state, returnCtx, returnAp, ap0, config) and
|
||||
storeStepFwd(node, ap, tc, mid, ap0, config) and
|
||||
tc.getContent() = c
|
||||
}
|
||||
@@ -1599,35 +1677,27 @@ private module MkStage<StageSig PrevStage> {
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate revFlowOut(
|
||||
DataFlowCall call, RetNodeEx ret, FlowState state, boolean toReturn, ApOption returnAp, Ap ap,
|
||||
Configuration config
|
||||
DataFlowCall call, RetNodeEx ret, ReturnKindExt kind, FlowState state, ReturnCtx returnCtx,
|
||||
ApOption returnAp, Ap ap, Configuration config
|
||||
) {
|
||||
exists(NodeEx out, boolean allowsFieldFlow |
|
||||
revFlow(out, state, toReturn, returnAp, ap, config) and
|
||||
flowOutOfCall(call, ret, out, allowsFieldFlow, config) and
|
||||
if allowsFieldFlow = false then ap instanceof ApNil else any()
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate revFlowInNotToReturn(
|
||||
ArgNodeEx arg, FlowState state, ApOption returnAp, Ap ap, Configuration config
|
||||
) {
|
||||
exists(ParamNodeEx p, boolean allowsFieldFlow |
|
||||
revFlow(p, state, false, returnAp, ap, config) and
|
||||
flowIntoCall(_, arg, p, allowsFieldFlow, config) and
|
||||
revFlow(out, state, returnCtx, returnAp, ap, config) and
|
||||
flowOutOfCall(call, ret, kind, out, allowsFieldFlow, config) and
|
||||
if allowsFieldFlow = false then ap instanceof ApNil else any()
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate revFlowInToReturn(
|
||||
DataFlowCall call, ArgNodeEx arg, FlowState state, Ap returnAp, Ap ap, Configuration config
|
||||
DataFlowCall call, ArgNodeEx arg, FlowState state, ReturnKindExt kind, Ap returnAp, Ap ap,
|
||||
Configuration config
|
||||
) {
|
||||
exists(ParamNodeEx p, boolean allowsFieldFlow |
|
||||
revFlow(p, state, true, apSome(returnAp), ap, config) and
|
||||
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()
|
||||
(if allowsFieldFlow = false then ap instanceof ApNil else any()) and
|
||||
parameterFlowThroughAllowed(p, kind)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1638,11 +1708,12 @@ private module MkStage<StageSig PrevStage> {
|
||||
*/
|
||||
pragma[nomagic]
|
||||
private predicate revFlowIsReturned(
|
||||
DataFlowCall call, boolean toReturn, ApOption returnAp, Ap ap, Configuration config
|
||||
DataFlowCall call, ReturnCtx returnCtx, ApOption returnAp, ReturnKindExt kind, Ap ap,
|
||||
Configuration config
|
||||
) {
|
||||
exists(RetNodeEx ret, FlowState state, CcCall ccc |
|
||||
revFlowOut(call, ret, state, toReturn, returnAp, ap, config) and
|
||||
fwdFlow(ret, state, ccc, apSome(_), ap, config) and
|
||||
revFlowOut(call, ret, kind, state, returnCtx, returnAp, ap, config) and
|
||||
returnFlowsThrough(ret, kind, state, ccc, _, _, ap, config) and
|
||||
matchesCall(ccc, call)
|
||||
)
|
||||
}
|
||||
@@ -1713,40 +1784,39 @@ private module MkStage<StageSig PrevStage> {
|
||||
validAp(ap, config)
|
||||
}
|
||||
|
||||
pragma[noinline]
|
||||
private predicate parameterFlow(
|
||||
ParamNodeEx p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config
|
||||
pragma[nomagic]
|
||||
private predicate parameterFlowsThroughRev(
|
||||
ParamNodeEx p, Ap ap, ReturnKindExt kind, Configuration config
|
||||
) {
|
||||
revFlow(p, _, true, apSome(ap0), ap, config) and
|
||||
c = p.getEnclosingCallable()
|
||||
revFlow(p, _, TReturnCtxMaybeFlowThrough(kind), apSome(_), ap, config) and
|
||||
parameterFlowThroughAllowed(p, kind)
|
||||
}
|
||||
|
||||
predicate parameterMayFlowThrough(ParamNodeEx p, DataFlowCallable c, Ap ap, Configuration config) {
|
||||
exists(RetNodeEx ret, FlowState state, Ap ap0, ReturnKindExt kind, ParameterPosition pos |
|
||||
parameterFlow(p, ap, ap0, c, config) and
|
||||
c = ret.getEnclosingCallable() and
|
||||
revFlow(pragma[only_bind_into](ret), pragma[only_bind_into](state), true, apSome(_),
|
||||
pragma[only_bind_into](ap0), pragma[only_bind_into](config)) and
|
||||
fwdFlow(ret, state, any(CcCall ccc), apSome(ap), ap0, config) and
|
||||
kind = ret.getKind() and
|
||||
p.getPosition() = pos and
|
||||
// we don't expect a parameter to return stored in itself, unless explicitly allowed
|
||||
(
|
||||
not kind.(ParamUpdateReturnKind).getPosition() = pos
|
||||
or
|
||||
p.allowParameterReturnInSelf()
|
||||
)
|
||||
pragma[nomagic]
|
||||
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)
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
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(
|
||||
Ap returnAp0, ArgNodeEx arg, FlowState state, boolean toReturn, ApOption returnAp, Ap ap
|
||||
ReturnKindExt returnKind0, Ap returnAp0, ArgNodeEx arg, FlowState state,
|
||||
ReturnCtx returnCtx, ApOption returnAp, Ap ap
|
||||
|
|
||||
revFlow(arg, state, toReturn, returnAp, ap, config) and
|
||||
revFlowInToReturn(call, arg, state, returnAp0, ap, config) and
|
||||
revFlowIsReturned(call, toReturn, returnAp, returnAp0, config)
|
||||
revFlow(arg, state, returnCtx, returnAp, ap, config) and
|
||||
revFlowInToReturn(call, arg, state, returnKind0, returnAp0, ap, config) and
|
||||
revFlowIsReturned(call, returnCtx, returnAp, returnKind0, returnAp0, config)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1754,14 +1824,13 @@ private module MkStage<StageSig PrevStage> {
|
||||
boolean fwd, int nodes, int fields, int conscand, int states, int tuples, Configuration config
|
||||
) {
|
||||
fwd = true and
|
||||
nodes = count(NodeEx node | fwdFlow(node, _, _, _, _, config)) and
|
||||
nodes = count(NodeEx node | fwdFlow(node, _, _, _, _, _, config)) and
|
||||
fields = count(TypedContent f0 | fwdConsCand(f0, _, config)) and
|
||||
conscand = count(TypedContent f0, Ap ap | fwdConsCand(f0, ap, config)) and
|
||||
states = count(FlowState state | fwdFlow(_, state, _, _, _, config)) and
|
||||
states = count(FlowState state | fwdFlow(_, state, _, _, _, _, config)) and
|
||||
tuples =
|
||||
count(NodeEx n, FlowState state, Cc cc, ApOption argAp, Ap ap |
|
||||
fwdFlow(n, state, cc, argAp, ap, config)
|
||||
)
|
||||
count(NodeEx n, FlowState state, Cc cc, ParameterPositionOption summaryCtx, ApOption argAp,
|
||||
Ap ap | fwdFlow(n, state, cc, summaryCtx, argAp, ap, config))
|
||||
or
|
||||
fwd = false and
|
||||
nodes = count(NodeEx node | revFlow(node, _, _, _, _, config)) and
|
||||
@@ -1769,8 +1838,8 @@ private module MkStage<StageSig PrevStage> {
|
||||
conscand = count(TypedContent f0, Ap ap | consCand(f0, ap, config)) and
|
||||
states = count(FlowState state | revFlow(_, state, _, _, _, config)) and
|
||||
tuples =
|
||||
count(NodeEx n, FlowState state, boolean b, ApOption retAp, Ap ap |
|
||||
revFlow(n, state, b, retAp, ap, config)
|
||||
count(NodeEx n, FlowState state, ReturnCtx returnCtx, ApOption retAp, Ap ap |
|
||||
revFlow(n, state, returnCtx, retAp, ap, config)
|
||||
)
|
||||
}
|
||||
/* End: Stage logic. */
|
||||
@@ -1915,7 +1984,7 @@ private module Stage2Param implements MkStage<Stage1>::StageParam {
|
||||
exists(lcc)
|
||||
}
|
||||
|
||||
predicate flowOutOfCall = flowOutOfCallNodeCand1/5;
|
||||
predicate flowOutOfCall = flowOutOfCallNodeCand1/6;
|
||||
|
||||
predicate flowIntoCall = flowIntoCallNodeCand1/5;
|
||||
|
||||
@@ -1951,9 +2020,10 @@ private module Stage2 implements StageSig {
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate flowOutOfCallNodeCand2(
|
||||
DataFlowCall call, RetNodeEx node1, NodeEx node2, boolean allowsFieldFlow, Configuration config
|
||||
DataFlowCall call, RetNodeEx node1, ReturnKindExt kind, NodeEx node2, boolean allowsFieldFlow,
|
||||
Configuration config
|
||||
) {
|
||||
flowOutOfCallNodeCand1(call, node1, node2, allowsFieldFlow, config) and
|
||||
flowOutOfCallNodeCand1(call, node1, kind, node2, allowsFieldFlow, config) and
|
||||
Stage2::revFlow(node2, pragma[only_bind_into](config)) and
|
||||
Stage2::revFlowAlias(node1, pragma[only_bind_into](config))
|
||||
}
|
||||
@@ -2021,8 +2091,8 @@ private module LocalFlowBigStep {
|
||||
exists(NodeEx next | Stage2::revFlow(next, state, config) |
|
||||
jumpStep(node, next, config) or
|
||||
additionalJumpStep(node, next, config) or
|
||||
flowIntoCallNodeCand1(_, node, next, config) or
|
||||
flowOutOfCallNodeCand1(_, node, next, config) or
|
||||
flowIntoCallNodeCand2(_, node, next, _, config) or
|
||||
flowOutOfCallNodeCand2(_, node, _, next, _, config) or
|
||||
Stage2::storeStepCand(node, _, _, next, _, config) or
|
||||
Stage2::readStepCand(node, _, next, config)
|
||||
)
|
||||
@@ -2163,7 +2233,7 @@ private module Stage3Param implements MkStage<Stage2>::StageParam {
|
||||
localFlowBigStep(node1, state1, node2, state2, preservesValue, ap, config, _) and exists(lcc)
|
||||
}
|
||||
|
||||
predicate flowOutOfCall = flowOutOfCallNodeCand2/5;
|
||||
predicate flowOutOfCall = flowOutOfCallNodeCand2/6;
|
||||
|
||||
predicate flowIntoCall = flowIntoCallNodeCand2/5;
|
||||
|
||||
@@ -2233,8 +2303,9 @@ private predicate flowCandSummaryCtx(
|
||||
NodeEx node, FlowState state, AccessPathFront argApf, Configuration config
|
||||
) {
|
||||
exists(AccessPathFront apf |
|
||||
Stage3::revFlow(node, state, true, _, apf, config) and
|
||||
Stage3::fwdFlow(node, state, any(Stage3::CcCall ccc), TAccessPathFrontSome(argApf), apf, config)
|
||||
Stage3::revFlow(node, state, TReturnCtxMaybeFlowThrough(_), _, apf, config) and
|
||||
Stage3::fwdFlow(node, state, any(Stage3::CcCall ccc), _, TAccessPathFrontSome(argApf), apf,
|
||||
config)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -2468,10 +2539,11 @@ private module Stage4Param implements MkStage<Stage3>::StageParam {
|
||||
|
||||
pragma[nomagic]
|
||||
predicate flowOutOfCall(
|
||||
DataFlowCall call, RetNodeEx node1, NodeEx node2, boolean allowsFieldFlow, Configuration config
|
||||
DataFlowCall call, RetNodeEx node1, ReturnKindExt kind, NodeEx node2, boolean allowsFieldFlow,
|
||||
Configuration config
|
||||
) {
|
||||
exists(FlowState state |
|
||||
flowOutOfCallNodeCand2(call, node1, node2, allowsFieldFlow, config) and
|
||||
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))
|
||||
@@ -2508,13 +2580,14 @@ private Configuration unbindConf(Configuration conf) {
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate nodeMayUseSummary0(
|
||||
NodeEx n, DataFlowCallable c, FlowState state, AccessPathApprox apa, Configuration config
|
||||
NodeEx n, DataFlowCallable c, ParameterPosition pos, FlowState state, AccessPathApprox apa,
|
||||
Configuration config
|
||||
) {
|
||||
exists(AccessPathApprox apa0 |
|
||||
Stage4::parameterMayFlowThrough(_, c, _, _) and
|
||||
Stage4::revFlow(n, state, true, _, apa0, config) and
|
||||
Stage4::fwdFlow(n, state, any(CallContextCall ccc), TAccessPathApproxSome(apa), apa0, config) and
|
||||
n.getEnclosingCallable() = c
|
||||
c = n.getEnclosingCallable() and
|
||||
Stage4::revFlow(n, state, TReturnCtxMaybeFlowThrough(_), _, apa0, config) and
|
||||
Stage4::fwdFlow(n, state, any(CallContextCall ccc), TParameterPositionSome(pos),
|
||||
TAccessPathApproxSome(apa), apa0, config)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -2522,9 +2595,10 @@ pragma[nomagic]
|
||||
private predicate nodeMayUseSummary(
|
||||
NodeEx n, FlowState state, AccessPathApprox apa, Configuration config
|
||||
) {
|
||||
exists(DataFlowCallable c |
|
||||
Stage4::parameterMayFlowThrough(_, c, apa, config) and
|
||||
nodeMayUseSummary0(n, c, state, apa, config)
|
||||
exists(DataFlowCallable c, ParameterPosition pos, ParamNodeEx p |
|
||||
Stage4::parameterMayFlowThrough(p, apa, config) and
|
||||
nodeMayUseSummary0(n, c, pos, state, apa, config) and
|
||||
p.isParameterOf(c, pos)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -2532,7 +2606,7 @@ private newtype TSummaryCtx =
|
||||
TSummaryCtxNone() or
|
||||
TSummaryCtxSome(ParamNodeEx p, FlowState state, AccessPath ap) {
|
||||
exists(Configuration config |
|
||||
Stage4::parameterMayFlowThrough(p, _, ap.getApprox(), config) and
|
||||
Stage4::parameterMayFlowThrough(p, ap.getApprox(), config) and
|
||||
Stage4::revFlow(p, state, _, config)
|
||||
)
|
||||
}
|
||||
@@ -3453,17 +3527,11 @@ private predicate paramFlowsThrough(
|
||||
ReturnKindExt kind, FlowState state, CallContextCall cc, SummaryCtxSome sc, AccessPath ap,
|
||||
AccessPathApprox apa, Configuration config
|
||||
) {
|
||||
exists(PathNodeMid mid, RetNodeEx ret, ParameterPosition pos |
|
||||
exists(PathNodeMid mid, RetNodeEx ret |
|
||||
pathNode(mid, ret, state, cc, sc, ap, config, _) and
|
||||
kind = ret.getKind() and
|
||||
apa = ap.getApprox() and
|
||||
pos = sc.getParameterPos() and
|
||||
// we don't expect a parameter to return stored in itself, unless explicitly allowed
|
||||
(
|
||||
not kind.(ParamUpdateReturnKind).getPosition() = pos
|
||||
or
|
||||
sc.getParamNode().allowParameterReturnInSelf()
|
||||
)
|
||||
parameterFlowThroughAllowed(sc.getParamNode(), kind)
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@@ -70,7 +70,7 @@ abstract class Configuration extends string {
|
||||
/**
|
||||
* Holds if `sink` is a relevant data flow sink accepting `state`.
|
||||
*/
|
||||
predicate isSink(Node source, FlowState state) { none() }
|
||||
predicate isSink(Node sink, FlowState state) { none() }
|
||||
|
||||
/**
|
||||
* Holds if data flow through `node` is prohibited. This completely removes
|
||||
@@ -319,8 +319,6 @@ private class ParamNodeEx extends NodeEx {
|
||||
}
|
||||
|
||||
ParameterPosition getPosition() { this.isParameterOf(_, result) }
|
||||
|
||||
predicate allowParameterReturnInSelf() { allowParameterReturnInSelfCached(this.asNode()) }
|
||||
}
|
||||
|
||||
private class RetNodeEx extends NodeEx {
|
||||
@@ -608,6 +606,38 @@ private predicate hasSinkCallCtx(Configuration config) {
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if flow from `p` to a return node of kind `kind` is allowed.
|
||||
*
|
||||
* We don't expect a parameter to return stored in itself, unless
|
||||
* explicitly allowed
|
||||
*/
|
||||
bindingset[p, kind]
|
||||
private predicate parameterFlowThroughAllowed(ParamNodeEx p, ReturnKindExt kind) {
|
||||
exists(ParameterPosition pos | p.isParameterOf(_, pos) |
|
||||
not kind.(ParamUpdateReturnKind).getPosition() = pos
|
||||
or
|
||||
allowParameterReturnInSelfCached(p.asNode())
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* 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;
|
||||
|
||||
@@ -981,21 +1011,22 @@ private module Stage1 implements StageSig {
|
||||
* candidate for the origin of a summary.
|
||||
*/
|
||||
pragma[nomagic]
|
||||
predicate parameterMayFlowThrough(ParamNodeEx p, DataFlowCallable c, Ap ap, Configuration config) {
|
||||
exists(ReturnKindExt kind |
|
||||
predicate parameterMayFlowThrough(ParamNodeEx p, Ap ap, Configuration config) {
|
||||
exists(DataFlowCallable c, ReturnKindExt kind |
|
||||
throughFlowNodeCand(p, config) and
|
||||
returnFlowCallableNodeCand(c, kind, config) and
|
||||
p.getEnclosingCallable() = c and
|
||||
exists(ap) and
|
||||
// we don't expect a parameter to return stored in itself, unless explicitly allowed
|
||||
(
|
||||
not kind.(ParamUpdateReturnKind).getPosition() = p.getPosition()
|
||||
or
|
||||
p.allowParameterReturnInSelf()
|
||||
)
|
||||
parameterFlowThroughAllowed(p, kind)
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
predicate returnMayFlowThrough(RetNodeEx ret, ReturnKindExt kind, Configuration config) {
|
||||
throughFlowNodeCand(ret, config) and
|
||||
kind = ret.getKind()
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
predicate callMayFlowThroughRev(DataFlowCall call, Configuration config) {
|
||||
exists(ArgNodeEx arg, boolean toReturn |
|
||||
@@ -1052,12 +1083,16 @@ private predicate viableReturnPosOutNodeCand1(
|
||||
*/
|
||||
pragma[nomagic]
|
||||
private predicate flowOutOfCallNodeCand1(
|
||||
DataFlowCall call, RetNodeEx ret, NodeEx out, Configuration config
|
||||
DataFlowCall call, RetNodeEx ret, ReturnKindExt kind, NodeEx out, Configuration config
|
||||
) {
|
||||
viableReturnPosOutNodeCand1(call, ret.getReturnPosition(), out, config) and
|
||||
Stage1::revFlow(ret, config) and
|
||||
not outBarrier(ret, config) and
|
||||
not inBarrier(out, config)
|
||||
exists(ReturnPosition pos |
|
||||
viableReturnPosOutNodeCand1(call, pos, out, config) and
|
||||
pos = ret.getReturnPosition() and
|
||||
kind = pos.getKind() and
|
||||
Stage1::revFlow(ret, config) and
|
||||
not outBarrier(ret, config) and
|
||||
not inBarrier(out, config)
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
@@ -1087,10 +1122,11 @@ private predicate flowIntoCallNodeCand1(
|
||||
* edge in the graph of paths between sources and sinks that ignores call
|
||||
* contexts.
|
||||
*/
|
||||
pragma[nomagic]
|
||||
private int branch(NodeEx n1, Configuration conf) {
|
||||
result =
|
||||
strictcount(NodeEx n |
|
||||
flowOutOfCallNodeCand1(_, n1, n, conf) or flowIntoCallNodeCand1(_, n1, n, conf)
|
||||
flowOutOfCallNodeCand1(_, n1, _, n, conf) or flowIntoCallNodeCand1(_, n1, n, conf)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1099,10 +1135,11 @@ private int branch(NodeEx n1, Configuration conf) {
|
||||
* edge in the graph of paths between sources and sinks that ignores call
|
||||
* contexts.
|
||||
*/
|
||||
pragma[nomagic]
|
||||
private int join(NodeEx n2, Configuration conf) {
|
||||
result =
|
||||
strictcount(NodeEx n |
|
||||
flowOutOfCallNodeCand1(_, n, n2, conf) or flowIntoCallNodeCand1(_, n, n2, conf)
|
||||
flowOutOfCallNodeCand1(_, n, _, n2, conf) or flowIntoCallNodeCand1(_, n, n2, conf)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1115,12 +1152,13 @@ private int join(NodeEx n2, Configuration conf) {
|
||||
*/
|
||||
pragma[nomagic]
|
||||
private predicate flowOutOfCallNodeCand1(
|
||||
DataFlowCall call, RetNodeEx ret, NodeEx out, boolean allowsFieldFlow, Configuration config
|
||||
DataFlowCall call, RetNodeEx ret, ReturnKindExt kind, NodeEx out, boolean allowsFieldFlow,
|
||||
Configuration config
|
||||
) {
|
||||
flowOutOfCallNodeCand1(call, ret, out, config) and
|
||||
flowOutOfCallNodeCand1(call, ret, kind, out, pragma[only_bind_into](config)) and
|
||||
exists(int b, int j |
|
||||
b = branch(ret, config) and
|
||||
j = join(out, config) and
|
||||
b = branch(ret, pragma[only_bind_into](config)) and
|
||||
j = join(out, pragma[only_bind_into](config)) and
|
||||
if b.minimum(j) <= config.fieldFlowBranchLimit()
|
||||
then allowsFieldFlow = true
|
||||
else allowsFieldFlow = false
|
||||
@@ -1136,10 +1174,10 @@ pragma[nomagic]
|
||||
private predicate flowIntoCallNodeCand1(
|
||||
DataFlowCall call, ArgNodeEx arg, ParamNodeEx p, boolean allowsFieldFlow, Configuration config
|
||||
) {
|
||||
flowIntoCallNodeCand1(call, arg, p, config) and
|
||||
flowIntoCallNodeCand1(call, arg, p, pragma[only_bind_into](config)) and
|
||||
exists(int b, int j |
|
||||
b = branch(arg, config) and
|
||||
j = join(p, config) and
|
||||
b = branch(arg, pragma[only_bind_into](config)) and
|
||||
j = join(p, pragma[only_bind_into](config)) and
|
||||
if b.minimum(j) <= config.fieldFlowBranchLimit()
|
||||
then allowsFieldFlow = true
|
||||
else allowsFieldFlow = false
|
||||
@@ -1156,7 +1194,9 @@ private signature module StageSig {
|
||||
|
||||
predicate callMayFlowThroughRev(DataFlowCall call, Configuration config);
|
||||
|
||||
predicate parameterMayFlowThrough(ParamNodeEx p, DataFlowCallable c, Ap ap, Configuration config);
|
||||
predicate parameterMayFlowThrough(ParamNodeEx p, Ap ap, Configuration config);
|
||||
|
||||
predicate returnMayFlowThrough(RetNodeEx ret, ReturnKindExt kind, Configuration config);
|
||||
|
||||
predicate storeStepCand(
|
||||
NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, DataFlowType contentType,
|
||||
@@ -1222,7 +1262,8 @@ private module MkStage<StageSig PrevStage> {
|
||||
);
|
||||
|
||||
predicate flowOutOfCall(
|
||||
DataFlowCall call, RetNodeEx ret, NodeEx out, boolean allowsFieldFlow, Configuration config
|
||||
DataFlowCall call, RetNodeEx ret, ReturnKindExt kind, NodeEx out, boolean allowsFieldFlow,
|
||||
Configuration config
|
||||
);
|
||||
|
||||
predicate flowIntoCall(
|
||||
@@ -1247,14 +1288,14 @@ private module MkStage<StageSig PrevStage> {
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate flowThroughOutOfCall(
|
||||
DataFlowCall call, CcCall ccc, RetNodeEx ret, NodeEx out, boolean allowsFieldFlow,
|
||||
Configuration config
|
||||
DataFlowCall call, DataFlowCallable c, CcCall ccc, RetNodeEx ret, ReturnKindExt kind,
|
||||
NodeEx out, boolean allowsFieldFlow, Configuration config
|
||||
) {
|
||||
flowOutOfCall(call, ret, out, allowsFieldFlow, pragma[only_bind_into](config)) and
|
||||
flowOutOfCall(call, ret, kind, out, allowsFieldFlow, pragma[only_bind_into](config)) and
|
||||
PrevStage::callMayFlowThroughRev(call, pragma[only_bind_into](config)) and
|
||||
PrevStage::parameterMayFlowThrough(_, ret.getEnclosingCallable(), _,
|
||||
pragma[only_bind_into](config)) and
|
||||
matchesCall(ccc, call)
|
||||
PrevStage::returnMayFlowThrough(ret, kind, pragma[only_bind_into](config)) and
|
||||
matchesCall(ccc, call) and
|
||||
c = ret.getEnclosingCallable()
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1262,29 +1303,32 @@ private module MkStage<StageSig PrevStage> {
|
||||
* configuration `config`.
|
||||
*
|
||||
* The call context `cc` records whether the node is reached through an
|
||||
* argument in a call, and if so, `argAp` records the access path of that
|
||||
* argument.
|
||||
* argument in a call, and if so, `summaryCtx` and `argAp` record the
|
||||
* corresponding parameter position and access path of that argument, respectively.
|
||||
*/
|
||||
pragma[nomagic]
|
||||
additional predicate fwdFlow(
|
||||
NodeEx node, FlowState state, Cc cc, ApOption argAp, Ap ap, Configuration config
|
||||
NodeEx node, FlowState state, Cc cc, ParameterPositionOption summaryCtx, ApOption argAp,
|
||||
Ap ap, Configuration config
|
||||
) {
|
||||
fwdFlow0(node, state, cc, argAp, ap, config) and
|
||||
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, ApOption argAp, Ap ap, Configuration config
|
||||
NodeEx node, FlowState state, Cc cc, ParameterPositionOption summaryCtx, ApOption argAp,
|
||||
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)
|
||||
or
|
||||
exists(NodeEx mid, FlowState state0, Ap ap0, LocalCc localCc |
|
||||
fwdFlow(mid, state0, cc, argAp, ap0, config) and
|
||||
fwdFlow(mid, state0, cc, summaryCtx, argAp, ap0, config) and
|
||||
localCc = getLocalCc(mid, cc)
|
||||
|
|
||||
localStep(mid, state0, node, state, true, _, config, localCc) and
|
||||
@@ -1295,65 +1339,82 @@ private module MkStage<StageSig PrevStage> {
|
||||
)
|
||||
or
|
||||
exists(NodeEx mid |
|
||||
fwdFlow(mid, pragma[only_bind_into](state), _, _, ap, 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
|
||||
argAp = apNone()
|
||||
)
|
||||
or
|
||||
exists(NodeEx mid, ApNil nil |
|
||||
fwdFlow(mid, state, _, _, nil, pragma[only_bind_into](config)) and
|
||||
fwdFlow(mid, state, _, _, _, nil, pragma[only_bind_into](config)) and
|
||||
additionalJumpStep(mid, node, config) and
|
||||
cc = ccNone() and
|
||||
summaryCtx = TParameterPositionNone() and
|
||||
argAp = apNone() and
|
||||
ap = getApNil(node)
|
||||
)
|
||||
or
|
||||
exists(NodeEx mid, FlowState state0, ApNil nil |
|
||||
fwdFlow(mid, state0, _, _, nil, pragma[only_bind_into](config)) and
|
||||
fwdFlow(mid, state0, _, _, _, nil, pragma[only_bind_into](config)) and
|
||||
additionalJumpStateStep(mid, state0, node, state, config) and
|
||||
cc = ccNone() and
|
||||
summaryCtx = TParameterPositionNone() and
|
||||
argAp = apNone() and
|
||||
ap = getApNil(node)
|
||||
)
|
||||
or
|
||||
// store
|
||||
exists(TypedContent tc, Ap ap0 |
|
||||
fwdFlowStore(_, ap0, tc, node, state, cc, argAp, config) and
|
||||
fwdFlowStore(_, ap0, tc, node, state, cc, summaryCtx, argAp, config) and
|
||||
ap = apCons(tc, ap0)
|
||||
)
|
||||
or
|
||||
// read
|
||||
exists(Ap ap0, Content c |
|
||||
fwdFlowRead(ap0, c, _, node, state, cc, argAp, config) and
|
||||
fwdFlowRead(ap0, c, _, node, state, cc, summaryCtx, argAp, config) and
|
||||
fwdFlowConsCand(ap0, c, ap, config)
|
||||
)
|
||||
or
|
||||
// flow into a callable
|
||||
exists(ApApprox apa |
|
||||
fwdFlowIn(_, node, state, _, cc, _, ap, config) and
|
||||
fwdFlowIn(_, node, state, _, cc, _, _, ap, config) and
|
||||
apa = getApprox(ap) and
|
||||
if PrevStage::parameterMayFlowThrough(node, _, apa, config)
|
||||
then argAp = apSome(ap)
|
||||
else argAp = apNone()
|
||||
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
|
||||
fwdFlowOutNotFromArg(node, state, cc, argAp, ap, config)
|
||||
exists(
|
||||
DataFlowCall call, RetNodeEx ret, boolean allowsFieldFlow, CcNoCall innercc,
|
||||
DataFlowCallable inner
|
||||
|
|
||||
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()
|
||||
)
|
||||
or
|
||||
exists(DataFlowCall call, Ap argAp0 |
|
||||
fwdFlowOutFromArg(call, node, state, argAp0, ap, config) and
|
||||
fwdFlowIsEntered(call, cc, argAp, argAp0, config)
|
||||
// flow through a callable
|
||||
exists(DataFlowCall call, ParameterPosition summaryCtx0, Ap argAp0 |
|
||||
fwdFlowOutFromArg(call, node, state, summaryCtx0, argAp0, ap, config) and
|
||||
fwdFlowIsEntered(call, cc, summaryCtx, argAp, summaryCtx0, argAp0, config)
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate fwdFlowStore(
|
||||
NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, FlowState state, Cc cc, ApOption argAp,
|
||||
Configuration config
|
||||
NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, FlowState state, Cc cc,
|
||||
ParameterPositionOption summaryCtx, ApOption argAp, Configuration config
|
||||
) {
|
||||
exists(DataFlowType contentType |
|
||||
fwdFlow(node1, state, cc, argAp, ap1, config) and
|
||||
fwdFlow(node1, state, cc, summaryCtx, argAp, ap1, config) and
|
||||
PrevStage::storeStepCand(node1, unbindApa(getApprox(ap1)), tc, node2, contentType, config) and
|
||||
typecheckStore(ap1, contentType)
|
||||
)
|
||||
@@ -1366,7 +1427,7 @@ private module MkStage<StageSig PrevStage> {
|
||||
pragma[nomagic]
|
||||
private predicate fwdFlowConsCand(Ap cons, Content c, Ap tail, Configuration config) {
|
||||
exists(TypedContent tc |
|
||||
fwdFlowStore(_, tail, tc, _, _, _, _, config) and
|
||||
fwdFlowStore(_, tail, tc, _, _, _, _, _, config) and
|
||||
tc.getContent() = c and
|
||||
cons = apCons(tc, tail)
|
||||
)
|
||||
@@ -1374,21 +1435,21 @@ private module MkStage<StageSig PrevStage> {
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate fwdFlowRead(
|
||||
Ap ap, Content c, NodeEx node1, NodeEx node2, FlowState state, Cc cc, ApOption argAp,
|
||||
Configuration config
|
||||
Ap ap, Content c, NodeEx node1, NodeEx node2, FlowState state, Cc cc,
|
||||
ParameterPositionOption summaryCtx, ApOption argAp, Configuration config
|
||||
) {
|
||||
fwdFlow(node1, state, cc, 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, Cc innercc, ApOption argAp,
|
||||
Ap ap, 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, argAp, ap, 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()
|
||||
@@ -1396,29 +1457,19 @@ private module MkStage<StageSig PrevStage> {
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate fwdFlowOutNotFromArg(
|
||||
NodeEx out, FlowState state, Cc ccOut, ApOption argAp, Ap ap, Configuration config
|
||||
private predicate fwdFlowOutFromArg(
|
||||
DataFlowCall call, NodeEx out, FlowState state, ParameterPosition summaryCtx, Ap argAp, Ap ap,
|
||||
Configuration config
|
||||
) {
|
||||
exists(
|
||||
DataFlowCall call, RetNodeEx ret, boolean allowsFieldFlow, CcNoCall innercc,
|
||||
DataFlowCallable inner
|
||||
DataFlowCallable c, RetNodeEx ret, ReturnKindExt kind, boolean allowsFieldFlow, CcCall ccc
|
||||
|
|
||||
fwdFlow(ret, state, innercc, argAp, ap, config) and
|
||||
flowOutOfCall(call, ret, out, allowsFieldFlow, config) and
|
||||
inner = ret.getEnclosingCallable() and
|
||||
ccOut = getCallContextReturn(inner, call, innercc) and
|
||||
if allowsFieldFlow = false then ap instanceof ApNil else any()
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate fwdFlowOutFromArg(
|
||||
DataFlowCall call, NodeEx out, FlowState state, Ap argAp, Ap ap, Configuration config
|
||||
) {
|
||||
exists(RetNodeEx ret, boolean allowsFieldFlow, CcCall ccc |
|
||||
fwdFlow(ret, state, ccc, apSome(argAp), ap, config) and
|
||||
flowThroughOutOfCall(call, ccc, ret, out, allowsFieldFlow, config) and
|
||||
if allowsFieldFlow = false then ap instanceof ApNil else any()
|
||||
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
|
||||
(if allowsFieldFlow = false then ap instanceof ApNil else any()) and
|
||||
parameterFlowThroughAllowed(c, pragma[only_bind_into](summaryCtx), kind)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1428,11 +1479,13 @@ private module MkStage<StageSig PrevStage> {
|
||||
*/
|
||||
pragma[nomagic]
|
||||
private predicate fwdFlowIsEntered(
|
||||
DataFlowCall call, Cc cc, ApOption argAp, Ap ap, Configuration config
|
||||
DataFlowCall call, Cc cc, ParameterPositionOption summaryCtx, ApOption argAp,
|
||||
ParameterPosition pos, Ap ap, Configuration config
|
||||
) {
|
||||
exists(ParamNodeEx p |
|
||||
fwdFlowIn(call, p, _, cc, _, argAp, ap, config) and
|
||||
PrevStage::parameterMayFlowThrough(p, _, unbindApa(getApprox(ap)), config)
|
||||
exists(ParamNodeEx param |
|
||||
fwdFlowIn(call, param, _, cc, _, summaryCtx, argAp, ap, config) and
|
||||
PrevStage::parameterMayFlowThrough(param, unbindApa(getApprox(ap)), config) and
|
||||
pos = param.getPosition()
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1440,27 +1493,40 @@ private module MkStage<StageSig PrevStage> {
|
||||
private predicate storeStepFwd(
|
||||
NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, Ap ap2, Configuration config
|
||||
) {
|
||||
fwdFlowStore(node1, ap1, tc, node2, _, _, _, config) and
|
||||
fwdFlowStore(node1, ap1, tc, node2, _, _, _, _, config) and
|
||||
ap2 = apCons(tc, ap1) and
|
||||
fwdFlowRead(ap2, tc.getContent(), _, _, _, _, _, config)
|
||||
fwdFlowRead(ap2, tc.getContent(), _, _, _, _, _, _, config)
|
||||
}
|
||||
|
||||
private predicate readStepFwd(
|
||||
NodeEx n1, Ap ap1, Content c, NodeEx n2, Ap ap2, Configuration config
|
||||
) {
|
||||
fwdFlowRead(ap1, c, n1, n2, _, _, _, config) and
|
||||
fwdFlowRead(ap1, c, n1, n2, _, _, _, _, config) and
|
||||
fwdFlowConsCand(ap1, c, ap2, config)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate callMayFlowThroughFwd(DataFlowCall call, Configuration config) {
|
||||
exists(Ap argAp0, NodeEx out, FlowState state, Cc cc, ApOption argAp, Ap ap |
|
||||
fwdFlow(out, state, pragma[only_bind_into](cc), pragma[only_bind_into](argAp), ap,
|
||||
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
|
||||
fwdFlowOutFromArg(call, out, state, argAp0, ap, config) and
|
||||
fwdFlowIsEntered(pragma[only_bind_into](call), pragma[only_bind_into](cc),
|
||||
pragma[only_bind_into](argAp), pragma[only_bind_into](argAp0),
|
||||
pragma[only_bind_into](config))
|
||||
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(DataFlowCallable c, ParameterPosition ppos |
|
||||
returnFlowsThrough0(ret, kind, state, ccc, c, ppos, argAp, ap, config) and
|
||||
p.isParameterOf(c, ppos) and
|
||||
parameterFlowThroughAllowed(p, kind)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1468,118 +1534,130 @@ private module MkStage<StageSig PrevStage> {
|
||||
private predicate flowThroughIntoCall(
|
||||
DataFlowCall call, ArgNodeEx arg, ParamNodeEx p, boolean allowsFieldFlow, Configuration config
|
||||
) {
|
||||
flowIntoCall(call, arg, p, allowsFieldFlow, config) and
|
||||
fwdFlow(arg, _, _, _, _, pragma[only_bind_into](config)) and
|
||||
PrevStage::parameterMayFlowThrough(p, _, _, pragma[only_bind_into](config)) and
|
||||
callMayFlowThroughFwd(call, pragma[only_bind_into](config))
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate returnNodeMayFlowThrough(
|
||||
RetNodeEx ret, FlowState state, Ap ap, Configuration config
|
||||
) {
|
||||
fwdFlow(ret, state, any(CcCall ccc), apSome(_), ap, config)
|
||||
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))
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `node` with access path `ap` is part of a path from a source to a
|
||||
* sink in the configuration `config`.
|
||||
*
|
||||
* The Boolean `toReturn` records whether the node must be returned from the
|
||||
* enclosing callable in order to reach a sink, and if so, `returnAp` records
|
||||
* the access path of the returned value.
|
||||
* The parameter `returnCtx` records whether (and how) the node must be returned
|
||||
* from the enclosing callable in order to reach a sink, and if so, `returnAp`
|
||||
* records the access path of the returned value.
|
||||
*/
|
||||
pragma[nomagic]
|
||||
additional predicate revFlow(
|
||||
NodeEx node, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, Configuration config
|
||||
NodeEx node, FlowState state, ReturnCtx returnCtx, ApOption returnAp, Ap ap,
|
||||
Configuration config
|
||||
) {
|
||||
revFlow0(node, state, toReturn, returnAp, ap, config) and
|
||||
fwdFlow(node, state, _, _, ap, config)
|
||||
revFlow0(node, state, returnCtx, returnAp, ap, config) and
|
||||
fwdFlow(node, state, _, _, _, ap, config)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate revFlow0(
|
||||
NodeEx node, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, Configuration config
|
||||
NodeEx node, FlowState state, ReturnCtx returnCtx, ApOption returnAp, Ap ap,
|
||||
Configuration config
|
||||
) {
|
||||
fwdFlow(node, state, _, _, ap, config) and
|
||||
fwdFlow(node, state, _, _, _, ap, config) and
|
||||
sinkNode(node, state, config) and
|
||||
(if hasSinkCallCtx(config) then toReturn = true else toReturn = false) and
|
||||
(
|
||||
if hasSinkCallCtx(config)
|
||||
then returnCtx = TReturnCtxNoFlowThrough()
|
||||
else returnCtx = TReturnCtxNone()
|
||||
) and
|
||||
returnAp = apNone() and
|
||||
ap instanceof ApNil
|
||||
or
|
||||
exists(NodeEx mid, FlowState state0 |
|
||||
localStep(node, state, mid, state0, true, _, config, _) and
|
||||
revFlow(mid, state0, toReturn, returnAp, ap, config)
|
||||
revFlow(mid, state0, returnCtx, returnAp, ap, config)
|
||||
)
|
||||
or
|
||||
exists(NodeEx mid, FlowState state0, ApNil nil |
|
||||
fwdFlow(node, pragma[only_bind_into](state), _, _, ap, pragma[only_bind_into](config)) and
|
||||
fwdFlow(node, pragma[only_bind_into](state), _, _, _, ap, pragma[only_bind_into](config)) and
|
||||
localStep(node, pragma[only_bind_into](state), mid, state0, false, _, config, _) and
|
||||
revFlow(mid, state0, toReturn, returnAp, nil, pragma[only_bind_into](config)) and
|
||||
revFlow(mid, state0, returnCtx, returnAp, nil, pragma[only_bind_into](config)) and
|
||||
ap instanceof ApNil
|
||||
)
|
||||
or
|
||||
exists(NodeEx mid |
|
||||
jumpStep(node, mid, config) and
|
||||
revFlow(mid, state, _, _, ap, config) and
|
||||
toReturn = false and
|
||||
returnCtx = TReturnCtxNone() and
|
||||
returnAp = apNone()
|
||||
)
|
||||
or
|
||||
exists(NodeEx mid, ApNil nil |
|
||||
fwdFlow(node, _, _, _, ap, pragma[only_bind_into](config)) and
|
||||
fwdFlow(node, _, _, _, _, ap, pragma[only_bind_into](config)) and
|
||||
additionalJumpStep(node, mid, config) and
|
||||
revFlow(pragma[only_bind_into](mid), state, _, _, nil, pragma[only_bind_into](config)) and
|
||||
toReturn = false and
|
||||
returnCtx = TReturnCtxNone() and
|
||||
returnAp = apNone() and
|
||||
ap instanceof ApNil
|
||||
)
|
||||
or
|
||||
exists(NodeEx mid, FlowState state0, ApNil nil |
|
||||
fwdFlow(node, _, _, _, ap, pragma[only_bind_into](config)) and
|
||||
fwdFlow(node, _, _, _, _, ap, pragma[only_bind_into](config)) and
|
||||
additionalJumpStateStep(node, state, mid, state0, config) and
|
||||
revFlow(pragma[only_bind_into](mid), pragma[only_bind_into](state0), _, _, nil,
|
||||
pragma[only_bind_into](config)) and
|
||||
toReturn = false and
|
||||
returnCtx = TReturnCtxNone() and
|
||||
returnAp = apNone() and
|
||||
ap instanceof ApNil
|
||||
)
|
||||
or
|
||||
// store
|
||||
exists(Ap ap0, Content c |
|
||||
revFlowStore(ap0, c, ap, node, state, _, _, toReturn, returnAp, config) and
|
||||
revFlowStore(ap0, c, ap, node, state, _, _, returnCtx, returnAp, config) and
|
||||
revFlowConsCand(ap0, c, ap, config)
|
||||
)
|
||||
or
|
||||
// read
|
||||
exists(NodeEx mid, Ap ap0 |
|
||||
revFlow(mid, state, toReturn, returnAp, ap0, config) and
|
||||
revFlow(mid, state, returnCtx, returnAp, ap0, config) and
|
||||
readStepFwd(node, ap, _, mid, ap0, config)
|
||||
)
|
||||
or
|
||||
// flow into a callable
|
||||
revFlowInNotToReturn(node, state, returnAp, ap, config) and
|
||||
toReturn = false
|
||||
exists(ParamNodeEx p, boolean allowsFieldFlow |
|
||||
revFlow(p, state, TReturnCtxNone(), returnAp, ap, config) and
|
||||
flowIntoCall(_, node, p, allowsFieldFlow, config) and
|
||||
(if allowsFieldFlow = false then ap instanceof ApNil else any()) and
|
||||
returnCtx = TReturnCtxNone()
|
||||
)
|
||||
or
|
||||
exists(DataFlowCall call, Ap returnAp0 |
|
||||
revFlowInToReturn(call, node, state, returnAp0, ap, config) and
|
||||
revFlowIsReturned(call, toReturn, returnAp, returnAp0, config)
|
||||
// flow through a callable
|
||||
exists(DataFlowCall call, ReturnKindExt returnKind0, Ap returnAp0 |
|
||||
revFlowInToReturn(call, node, state, returnKind0, returnAp0, ap, config) and
|
||||
revFlowIsReturned(call, returnCtx, returnAp, returnKind0, returnAp0, config)
|
||||
)
|
||||
or
|
||||
// flow out of a callable
|
||||
revFlowOut(_, node, state, _, _, ap, config) and
|
||||
toReturn = true and
|
||||
if returnNodeMayFlowThrough(node, state, ap, config)
|
||||
then returnAp = apSome(ap)
|
||||
else returnAp = apNone()
|
||||
exists(ReturnKindExt kind |
|
||||
revFlowOut(_, node, kind, state, _, _, ap, config) and
|
||||
if returnFlowsThrough(node, kind, state, _, _, _, ap, config)
|
||||
then (
|
||||
returnCtx = TReturnCtxMaybeFlowThrough(kind) and
|
||||
returnAp = apSome(ap)
|
||||
) else (
|
||||
returnCtx = TReturnCtxNoFlowThrough() and returnAp = apNone()
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate revFlowStore(
|
||||
Ap ap0, Content c, Ap ap, NodeEx node, FlowState state, TypedContent tc, NodeEx mid,
|
||||
boolean toReturn, ApOption returnAp, Configuration config
|
||||
ReturnCtx returnCtx, ApOption returnAp, Configuration config
|
||||
) {
|
||||
revFlow(mid, state, toReturn, returnAp, ap0, config) and
|
||||
revFlow(mid, state, returnCtx, returnAp, ap0, config) and
|
||||
storeStepFwd(node, ap, tc, mid, ap0, config) and
|
||||
tc.getContent() = c
|
||||
}
|
||||
@@ -1599,35 +1677,27 @@ private module MkStage<StageSig PrevStage> {
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate revFlowOut(
|
||||
DataFlowCall call, RetNodeEx ret, FlowState state, boolean toReturn, ApOption returnAp, Ap ap,
|
||||
Configuration config
|
||||
DataFlowCall call, RetNodeEx ret, ReturnKindExt kind, FlowState state, ReturnCtx returnCtx,
|
||||
ApOption returnAp, Ap ap, Configuration config
|
||||
) {
|
||||
exists(NodeEx out, boolean allowsFieldFlow |
|
||||
revFlow(out, state, toReturn, returnAp, ap, config) and
|
||||
flowOutOfCall(call, ret, out, allowsFieldFlow, config) and
|
||||
if allowsFieldFlow = false then ap instanceof ApNil else any()
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate revFlowInNotToReturn(
|
||||
ArgNodeEx arg, FlowState state, ApOption returnAp, Ap ap, Configuration config
|
||||
) {
|
||||
exists(ParamNodeEx p, boolean allowsFieldFlow |
|
||||
revFlow(p, state, false, returnAp, ap, config) and
|
||||
flowIntoCall(_, arg, p, allowsFieldFlow, config) and
|
||||
revFlow(out, state, returnCtx, returnAp, ap, config) and
|
||||
flowOutOfCall(call, ret, kind, out, allowsFieldFlow, config) and
|
||||
if allowsFieldFlow = false then ap instanceof ApNil else any()
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate revFlowInToReturn(
|
||||
DataFlowCall call, ArgNodeEx arg, FlowState state, Ap returnAp, Ap ap, Configuration config
|
||||
DataFlowCall call, ArgNodeEx arg, FlowState state, ReturnKindExt kind, Ap returnAp, Ap ap,
|
||||
Configuration config
|
||||
) {
|
||||
exists(ParamNodeEx p, boolean allowsFieldFlow |
|
||||
revFlow(p, state, true, apSome(returnAp), ap, config) and
|
||||
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()
|
||||
(if allowsFieldFlow = false then ap instanceof ApNil else any()) and
|
||||
parameterFlowThroughAllowed(p, kind)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1638,11 +1708,12 @@ private module MkStage<StageSig PrevStage> {
|
||||
*/
|
||||
pragma[nomagic]
|
||||
private predicate revFlowIsReturned(
|
||||
DataFlowCall call, boolean toReturn, ApOption returnAp, Ap ap, Configuration config
|
||||
DataFlowCall call, ReturnCtx returnCtx, ApOption returnAp, ReturnKindExt kind, Ap ap,
|
||||
Configuration config
|
||||
) {
|
||||
exists(RetNodeEx ret, FlowState state, CcCall ccc |
|
||||
revFlowOut(call, ret, state, toReturn, returnAp, ap, config) and
|
||||
fwdFlow(ret, state, ccc, apSome(_), ap, config) and
|
||||
revFlowOut(call, ret, kind, state, returnCtx, returnAp, ap, config) and
|
||||
returnFlowsThrough(ret, kind, state, ccc, _, _, ap, config) and
|
||||
matchesCall(ccc, call)
|
||||
)
|
||||
}
|
||||
@@ -1713,40 +1784,39 @@ private module MkStage<StageSig PrevStage> {
|
||||
validAp(ap, config)
|
||||
}
|
||||
|
||||
pragma[noinline]
|
||||
private predicate parameterFlow(
|
||||
ParamNodeEx p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config
|
||||
pragma[nomagic]
|
||||
private predicate parameterFlowsThroughRev(
|
||||
ParamNodeEx p, Ap ap, ReturnKindExt kind, Configuration config
|
||||
) {
|
||||
revFlow(p, _, true, apSome(ap0), ap, config) and
|
||||
c = p.getEnclosingCallable()
|
||||
revFlow(p, _, TReturnCtxMaybeFlowThrough(kind), apSome(_), ap, config) and
|
||||
parameterFlowThroughAllowed(p, kind)
|
||||
}
|
||||
|
||||
predicate parameterMayFlowThrough(ParamNodeEx p, DataFlowCallable c, Ap ap, Configuration config) {
|
||||
exists(RetNodeEx ret, FlowState state, Ap ap0, ReturnKindExt kind, ParameterPosition pos |
|
||||
parameterFlow(p, ap, ap0, c, config) and
|
||||
c = ret.getEnclosingCallable() and
|
||||
revFlow(pragma[only_bind_into](ret), pragma[only_bind_into](state), true, apSome(_),
|
||||
pragma[only_bind_into](ap0), pragma[only_bind_into](config)) and
|
||||
fwdFlow(ret, state, any(CcCall ccc), apSome(ap), ap0, config) and
|
||||
kind = ret.getKind() and
|
||||
p.getPosition() = pos and
|
||||
// we don't expect a parameter to return stored in itself, unless explicitly allowed
|
||||
(
|
||||
not kind.(ParamUpdateReturnKind).getPosition() = pos
|
||||
or
|
||||
p.allowParameterReturnInSelf()
|
||||
)
|
||||
pragma[nomagic]
|
||||
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)
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
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(
|
||||
Ap returnAp0, ArgNodeEx arg, FlowState state, boolean toReturn, ApOption returnAp, Ap ap
|
||||
ReturnKindExt returnKind0, Ap returnAp0, ArgNodeEx arg, FlowState state,
|
||||
ReturnCtx returnCtx, ApOption returnAp, Ap ap
|
||||
|
|
||||
revFlow(arg, state, toReturn, returnAp, ap, config) and
|
||||
revFlowInToReturn(call, arg, state, returnAp0, ap, config) and
|
||||
revFlowIsReturned(call, toReturn, returnAp, returnAp0, config)
|
||||
revFlow(arg, state, returnCtx, returnAp, ap, config) and
|
||||
revFlowInToReturn(call, arg, state, returnKind0, returnAp0, ap, config) and
|
||||
revFlowIsReturned(call, returnCtx, returnAp, returnKind0, returnAp0, config)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1754,14 +1824,13 @@ private module MkStage<StageSig PrevStage> {
|
||||
boolean fwd, int nodes, int fields, int conscand, int states, int tuples, Configuration config
|
||||
) {
|
||||
fwd = true and
|
||||
nodes = count(NodeEx node | fwdFlow(node, _, _, _, _, config)) and
|
||||
nodes = count(NodeEx node | fwdFlow(node, _, _, _, _, _, config)) and
|
||||
fields = count(TypedContent f0 | fwdConsCand(f0, _, config)) and
|
||||
conscand = count(TypedContent f0, Ap ap | fwdConsCand(f0, ap, config)) and
|
||||
states = count(FlowState state | fwdFlow(_, state, _, _, _, config)) and
|
||||
states = count(FlowState state | fwdFlow(_, state, _, _, _, _, config)) and
|
||||
tuples =
|
||||
count(NodeEx n, FlowState state, Cc cc, ApOption argAp, Ap ap |
|
||||
fwdFlow(n, state, cc, argAp, ap, config)
|
||||
)
|
||||
count(NodeEx n, FlowState state, Cc cc, ParameterPositionOption summaryCtx, ApOption argAp,
|
||||
Ap ap | fwdFlow(n, state, cc, summaryCtx, argAp, ap, config))
|
||||
or
|
||||
fwd = false and
|
||||
nodes = count(NodeEx node | revFlow(node, _, _, _, _, config)) and
|
||||
@@ -1769,8 +1838,8 @@ private module MkStage<StageSig PrevStage> {
|
||||
conscand = count(TypedContent f0, Ap ap | consCand(f0, ap, config)) and
|
||||
states = count(FlowState state | revFlow(_, state, _, _, _, config)) and
|
||||
tuples =
|
||||
count(NodeEx n, FlowState state, boolean b, ApOption retAp, Ap ap |
|
||||
revFlow(n, state, b, retAp, ap, config)
|
||||
count(NodeEx n, FlowState state, ReturnCtx returnCtx, ApOption retAp, Ap ap |
|
||||
revFlow(n, state, returnCtx, retAp, ap, config)
|
||||
)
|
||||
}
|
||||
/* End: Stage logic. */
|
||||
@@ -1915,7 +1984,7 @@ private module Stage2Param implements MkStage<Stage1>::StageParam {
|
||||
exists(lcc)
|
||||
}
|
||||
|
||||
predicate flowOutOfCall = flowOutOfCallNodeCand1/5;
|
||||
predicate flowOutOfCall = flowOutOfCallNodeCand1/6;
|
||||
|
||||
predicate flowIntoCall = flowIntoCallNodeCand1/5;
|
||||
|
||||
@@ -1951,9 +2020,10 @@ private module Stage2 implements StageSig {
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate flowOutOfCallNodeCand2(
|
||||
DataFlowCall call, RetNodeEx node1, NodeEx node2, boolean allowsFieldFlow, Configuration config
|
||||
DataFlowCall call, RetNodeEx node1, ReturnKindExt kind, NodeEx node2, boolean allowsFieldFlow,
|
||||
Configuration config
|
||||
) {
|
||||
flowOutOfCallNodeCand1(call, node1, node2, allowsFieldFlow, config) and
|
||||
flowOutOfCallNodeCand1(call, node1, kind, node2, allowsFieldFlow, config) and
|
||||
Stage2::revFlow(node2, pragma[only_bind_into](config)) and
|
||||
Stage2::revFlowAlias(node1, pragma[only_bind_into](config))
|
||||
}
|
||||
@@ -2021,8 +2091,8 @@ private module LocalFlowBigStep {
|
||||
exists(NodeEx next | Stage2::revFlow(next, state, config) |
|
||||
jumpStep(node, next, config) or
|
||||
additionalJumpStep(node, next, config) or
|
||||
flowIntoCallNodeCand1(_, node, next, config) or
|
||||
flowOutOfCallNodeCand1(_, node, next, config) or
|
||||
flowIntoCallNodeCand2(_, node, next, _, config) or
|
||||
flowOutOfCallNodeCand2(_, node, _, next, _, config) or
|
||||
Stage2::storeStepCand(node, _, _, next, _, config) or
|
||||
Stage2::readStepCand(node, _, next, config)
|
||||
)
|
||||
@@ -2163,7 +2233,7 @@ private module Stage3Param implements MkStage<Stage2>::StageParam {
|
||||
localFlowBigStep(node1, state1, node2, state2, preservesValue, ap, config, _) and exists(lcc)
|
||||
}
|
||||
|
||||
predicate flowOutOfCall = flowOutOfCallNodeCand2/5;
|
||||
predicate flowOutOfCall = flowOutOfCallNodeCand2/6;
|
||||
|
||||
predicate flowIntoCall = flowIntoCallNodeCand2/5;
|
||||
|
||||
@@ -2233,8 +2303,9 @@ private predicate flowCandSummaryCtx(
|
||||
NodeEx node, FlowState state, AccessPathFront argApf, Configuration config
|
||||
) {
|
||||
exists(AccessPathFront apf |
|
||||
Stage3::revFlow(node, state, true, _, apf, config) and
|
||||
Stage3::fwdFlow(node, state, any(Stage3::CcCall ccc), TAccessPathFrontSome(argApf), apf, config)
|
||||
Stage3::revFlow(node, state, TReturnCtxMaybeFlowThrough(_), _, apf, config) and
|
||||
Stage3::fwdFlow(node, state, any(Stage3::CcCall ccc), _, TAccessPathFrontSome(argApf), apf,
|
||||
config)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -2468,10 +2539,11 @@ private module Stage4Param implements MkStage<Stage3>::StageParam {
|
||||
|
||||
pragma[nomagic]
|
||||
predicate flowOutOfCall(
|
||||
DataFlowCall call, RetNodeEx node1, NodeEx node2, boolean allowsFieldFlow, Configuration config
|
||||
DataFlowCall call, RetNodeEx node1, ReturnKindExt kind, NodeEx node2, boolean allowsFieldFlow,
|
||||
Configuration config
|
||||
) {
|
||||
exists(FlowState state |
|
||||
flowOutOfCallNodeCand2(call, node1, node2, allowsFieldFlow, config) and
|
||||
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))
|
||||
@@ -2508,13 +2580,14 @@ private Configuration unbindConf(Configuration conf) {
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate nodeMayUseSummary0(
|
||||
NodeEx n, DataFlowCallable c, FlowState state, AccessPathApprox apa, Configuration config
|
||||
NodeEx n, DataFlowCallable c, ParameterPosition pos, FlowState state, AccessPathApprox apa,
|
||||
Configuration config
|
||||
) {
|
||||
exists(AccessPathApprox apa0 |
|
||||
Stage4::parameterMayFlowThrough(_, c, _, _) and
|
||||
Stage4::revFlow(n, state, true, _, apa0, config) and
|
||||
Stage4::fwdFlow(n, state, any(CallContextCall ccc), TAccessPathApproxSome(apa), apa0, config) and
|
||||
n.getEnclosingCallable() = c
|
||||
c = n.getEnclosingCallable() and
|
||||
Stage4::revFlow(n, state, TReturnCtxMaybeFlowThrough(_), _, apa0, config) and
|
||||
Stage4::fwdFlow(n, state, any(CallContextCall ccc), TParameterPositionSome(pos),
|
||||
TAccessPathApproxSome(apa), apa0, config)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -2522,9 +2595,10 @@ pragma[nomagic]
|
||||
private predicate nodeMayUseSummary(
|
||||
NodeEx n, FlowState state, AccessPathApprox apa, Configuration config
|
||||
) {
|
||||
exists(DataFlowCallable c |
|
||||
Stage4::parameterMayFlowThrough(_, c, apa, config) and
|
||||
nodeMayUseSummary0(n, c, state, apa, config)
|
||||
exists(DataFlowCallable c, ParameterPosition pos, ParamNodeEx p |
|
||||
Stage4::parameterMayFlowThrough(p, apa, config) and
|
||||
nodeMayUseSummary0(n, c, pos, state, apa, config) and
|
||||
p.isParameterOf(c, pos)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -2532,7 +2606,7 @@ private newtype TSummaryCtx =
|
||||
TSummaryCtxNone() or
|
||||
TSummaryCtxSome(ParamNodeEx p, FlowState state, AccessPath ap) {
|
||||
exists(Configuration config |
|
||||
Stage4::parameterMayFlowThrough(p, _, ap.getApprox(), config) and
|
||||
Stage4::parameterMayFlowThrough(p, ap.getApprox(), config) and
|
||||
Stage4::revFlow(p, state, _, config)
|
||||
)
|
||||
}
|
||||
@@ -3453,17 +3527,11 @@ private predicate paramFlowsThrough(
|
||||
ReturnKindExt kind, FlowState state, CallContextCall cc, SummaryCtxSome sc, AccessPath ap,
|
||||
AccessPathApprox apa, Configuration config
|
||||
) {
|
||||
exists(PathNodeMid mid, RetNodeEx ret, ParameterPosition pos |
|
||||
exists(PathNodeMid mid, RetNodeEx ret |
|
||||
pathNode(mid, ret, state, cc, sc, ap, config, _) and
|
||||
kind = ret.getKind() and
|
||||
apa = ap.getApprox() and
|
||||
pos = sc.getParameterPos() and
|
||||
// we don't expect a parameter to return stored in itself, unless explicitly allowed
|
||||
(
|
||||
not kind.(ParamUpdateReturnKind).getPosition() = pos
|
||||
or
|
||||
sc.getParamNode().allowParameterReturnInSelf()
|
||||
)
|
||||
parameterFlowThroughAllowed(sc.getParamNode(), kind)
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@@ -70,7 +70,7 @@ abstract class Configuration extends string {
|
||||
/**
|
||||
* Holds if `sink` is a relevant data flow sink accepting `state`.
|
||||
*/
|
||||
predicate isSink(Node source, FlowState state) { none() }
|
||||
predicate isSink(Node sink, FlowState state) { none() }
|
||||
|
||||
/**
|
||||
* Holds if data flow through `node` is prohibited. This completely removes
|
||||
@@ -319,8 +319,6 @@ private class ParamNodeEx extends NodeEx {
|
||||
}
|
||||
|
||||
ParameterPosition getPosition() { this.isParameterOf(_, result) }
|
||||
|
||||
predicate allowParameterReturnInSelf() { allowParameterReturnInSelfCached(this.asNode()) }
|
||||
}
|
||||
|
||||
private class RetNodeEx extends NodeEx {
|
||||
@@ -608,6 +606,38 @@ private predicate hasSinkCallCtx(Configuration config) {
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if flow from `p` to a return node of kind `kind` is allowed.
|
||||
*
|
||||
* We don't expect a parameter to return stored in itself, unless
|
||||
* explicitly allowed
|
||||
*/
|
||||
bindingset[p, kind]
|
||||
private predicate parameterFlowThroughAllowed(ParamNodeEx p, ReturnKindExt kind) {
|
||||
exists(ParameterPosition pos | p.isParameterOf(_, pos) |
|
||||
not kind.(ParamUpdateReturnKind).getPosition() = pos
|
||||
or
|
||||
allowParameterReturnInSelfCached(p.asNode())
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* 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;
|
||||
|
||||
@@ -981,21 +1011,22 @@ private module Stage1 implements StageSig {
|
||||
* candidate for the origin of a summary.
|
||||
*/
|
||||
pragma[nomagic]
|
||||
predicate parameterMayFlowThrough(ParamNodeEx p, DataFlowCallable c, Ap ap, Configuration config) {
|
||||
exists(ReturnKindExt kind |
|
||||
predicate parameterMayFlowThrough(ParamNodeEx p, Ap ap, Configuration config) {
|
||||
exists(DataFlowCallable c, ReturnKindExt kind |
|
||||
throughFlowNodeCand(p, config) and
|
||||
returnFlowCallableNodeCand(c, kind, config) and
|
||||
p.getEnclosingCallable() = c and
|
||||
exists(ap) and
|
||||
// we don't expect a parameter to return stored in itself, unless explicitly allowed
|
||||
(
|
||||
not kind.(ParamUpdateReturnKind).getPosition() = p.getPosition()
|
||||
or
|
||||
p.allowParameterReturnInSelf()
|
||||
)
|
||||
parameterFlowThroughAllowed(p, kind)
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
predicate returnMayFlowThrough(RetNodeEx ret, ReturnKindExt kind, Configuration config) {
|
||||
throughFlowNodeCand(ret, config) and
|
||||
kind = ret.getKind()
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
predicate callMayFlowThroughRev(DataFlowCall call, Configuration config) {
|
||||
exists(ArgNodeEx arg, boolean toReturn |
|
||||
@@ -1052,12 +1083,16 @@ private predicate viableReturnPosOutNodeCand1(
|
||||
*/
|
||||
pragma[nomagic]
|
||||
private predicate flowOutOfCallNodeCand1(
|
||||
DataFlowCall call, RetNodeEx ret, NodeEx out, Configuration config
|
||||
DataFlowCall call, RetNodeEx ret, ReturnKindExt kind, NodeEx out, Configuration config
|
||||
) {
|
||||
viableReturnPosOutNodeCand1(call, ret.getReturnPosition(), out, config) and
|
||||
Stage1::revFlow(ret, config) and
|
||||
not outBarrier(ret, config) and
|
||||
not inBarrier(out, config)
|
||||
exists(ReturnPosition pos |
|
||||
viableReturnPosOutNodeCand1(call, pos, out, config) and
|
||||
pos = ret.getReturnPosition() and
|
||||
kind = pos.getKind() and
|
||||
Stage1::revFlow(ret, config) and
|
||||
not outBarrier(ret, config) and
|
||||
not inBarrier(out, config)
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
@@ -1087,10 +1122,11 @@ private predicate flowIntoCallNodeCand1(
|
||||
* edge in the graph of paths between sources and sinks that ignores call
|
||||
* contexts.
|
||||
*/
|
||||
pragma[nomagic]
|
||||
private int branch(NodeEx n1, Configuration conf) {
|
||||
result =
|
||||
strictcount(NodeEx n |
|
||||
flowOutOfCallNodeCand1(_, n1, n, conf) or flowIntoCallNodeCand1(_, n1, n, conf)
|
||||
flowOutOfCallNodeCand1(_, n1, _, n, conf) or flowIntoCallNodeCand1(_, n1, n, conf)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1099,10 +1135,11 @@ private int branch(NodeEx n1, Configuration conf) {
|
||||
* edge in the graph of paths between sources and sinks that ignores call
|
||||
* contexts.
|
||||
*/
|
||||
pragma[nomagic]
|
||||
private int join(NodeEx n2, Configuration conf) {
|
||||
result =
|
||||
strictcount(NodeEx n |
|
||||
flowOutOfCallNodeCand1(_, n, n2, conf) or flowIntoCallNodeCand1(_, n, n2, conf)
|
||||
flowOutOfCallNodeCand1(_, n, _, n2, conf) or flowIntoCallNodeCand1(_, n, n2, conf)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1115,12 +1152,13 @@ private int join(NodeEx n2, Configuration conf) {
|
||||
*/
|
||||
pragma[nomagic]
|
||||
private predicate flowOutOfCallNodeCand1(
|
||||
DataFlowCall call, RetNodeEx ret, NodeEx out, boolean allowsFieldFlow, Configuration config
|
||||
DataFlowCall call, RetNodeEx ret, ReturnKindExt kind, NodeEx out, boolean allowsFieldFlow,
|
||||
Configuration config
|
||||
) {
|
||||
flowOutOfCallNodeCand1(call, ret, out, config) and
|
||||
flowOutOfCallNodeCand1(call, ret, kind, out, pragma[only_bind_into](config)) and
|
||||
exists(int b, int j |
|
||||
b = branch(ret, config) and
|
||||
j = join(out, config) and
|
||||
b = branch(ret, pragma[only_bind_into](config)) and
|
||||
j = join(out, pragma[only_bind_into](config)) and
|
||||
if b.minimum(j) <= config.fieldFlowBranchLimit()
|
||||
then allowsFieldFlow = true
|
||||
else allowsFieldFlow = false
|
||||
@@ -1136,10 +1174,10 @@ pragma[nomagic]
|
||||
private predicate flowIntoCallNodeCand1(
|
||||
DataFlowCall call, ArgNodeEx arg, ParamNodeEx p, boolean allowsFieldFlow, Configuration config
|
||||
) {
|
||||
flowIntoCallNodeCand1(call, arg, p, config) and
|
||||
flowIntoCallNodeCand1(call, arg, p, pragma[only_bind_into](config)) and
|
||||
exists(int b, int j |
|
||||
b = branch(arg, config) and
|
||||
j = join(p, config) and
|
||||
b = branch(arg, pragma[only_bind_into](config)) and
|
||||
j = join(p, pragma[only_bind_into](config)) and
|
||||
if b.minimum(j) <= config.fieldFlowBranchLimit()
|
||||
then allowsFieldFlow = true
|
||||
else allowsFieldFlow = false
|
||||
@@ -1156,7 +1194,9 @@ private signature module StageSig {
|
||||
|
||||
predicate callMayFlowThroughRev(DataFlowCall call, Configuration config);
|
||||
|
||||
predicate parameterMayFlowThrough(ParamNodeEx p, DataFlowCallable c, Ap ap, Configuration config);
|
||||
predicate parameterMayFlowThrough(ParamNodeEx p, Ap ap, Configuration config);
|
||||
|
||||
predicate returnMayFlowThrough(RetNodeEx ret, ReturnKindExt kind, Configuration config);
|
||||
|
||||
predicate storeStepCand(
|
||||
NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, DataFlowType contentType,
|
||||
@@ -1222,7 +1262,8 @@ private module MkStage<StageSig PrevStage> {
|
||||
);
|
||||
|
||||
predicate flowOutOfCall(
|
||||
DataFlowCall call, RetNodeEx ret, NodeEx out, boolean allowsFieldFlow, Configuration config
|
||||
DataFlowCall call, RetNodeEx ret, ReturnKindExt kind, NodeEx out, boolean allowsFieldFlow,
|
||||
Configuration config
|
||||
);
|
||||
|
||||
predicate flowIntoCall(
|
||||
@@ -1247,14 +1288,14 @@ private module MkStage<StageSig PrevStage> {
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate flowThroughOutOfCall(
|
||||
DataFlowCall call, CcCall ccc, RetNodeEx ret, NodeEx out, boolean allowsFieldFlow,
|
||||
Configuration config
|
||||
DataFlowCall call, DataFlowCallable c, CcCall ccc, RetNodeEx ret, ReturnKindExt kind,
|
||||
NodeEx out, boolean allowsFieldFlow, Configuration config
|
||||
) {
|
||||
flowOutOfCall(call, ret, out, allowsFieldFlow, pragma[only_bind_into](config)) and
|
||||
flowOutOfCall(call, ret, kind, out, allowsFieldFlow, pragma[only_bind_into](config)) and
|
||||
PrevStage::callMayFlowThroughRev(call, pragma[only_bind_into](config)) and
|
||||
PrevStage::parameterMayFlowThrough(_, ret.getEnclosingCallable(), _,
|
||||
pragma[only_bind_into](config)) and
|
||||
matchesCall(ccc, call)
|
||||
PrevStage::returnMayFlowThrough(ret, kind, pragma[only_bind_into](config)) and
|
||||
matchesCall(ccc, call) and
|
||||
c = ret.getEnclosingCallable()
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1262,29 +1303,32 @@ private module MkStage<StageSig PrevStage> {
|
||||
* configuration `config`.
|
||||
*
|
||||
* The call context `cc` records whether the node is reached through an
|
||||
* argument in a call, and if so, `argAp` records the access path of that
|
||||
* argument.
|
||||
* argument in a call, and if so, `summaryCtx` and `argAp` record the
|
||||
* corresponding parameter position and access path of that argument, respectively.
|
||||
*/
|
||||
pragma[nomagic]
|
||||
additional predicate fwdFlow(
|
||||
NodeEx node, FlowState state, Cc cc, ApOption argAp, Ap ap, Configuration config
|
||||
NodeEx node, FlowState state, Cc cc, ParameterPositionOption summaryCtx, ApOption argAp,
|
||||
Ap ap, Configuration config
|
||||
) {
|
||||
fwdFlow0(node, state, cc, argAp, ap, config) and
|
||||
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, ApOption argAp, Ap ap, Configuration config
|
||||
NodeEx node, FlowState state, Cc cc, ParameterPositionOption summaryCtx, ApOption argAp,
|
||||
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)
|
||||
or
|
||||
exists(NodeEx mid, FlowState state0, Ap ap0, LocalCc localCc |
|
||||
fwdFlow(mid, state0, cc, argAp, ap0, config) and
|
||||
fwdFlow(mid, state0, cc, summaryCtx, argAp, ap0, config) and
|
||||
localCc = getLocalCc(mid, cc)
|
||||
|
|
||||
localStep(mid, state0, node, state, true, _, config, localCc) and
|
||||
@@ -1295,65 +1339,82 @@ private module MkStage<StageSig PrevStage> {
|
||||
)
|
||||
or
|
||||
exists(NodeEx mid |
|
||||
fwdFlow(mid, pragma[only_bind_into](state), _, _, ap, 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
|
||||
argAp = apNone()
|
||||
)
|
||||
or
|
||||
exists(NodeEx mid, ApNil nil |
|
||||
fwdFlow(mid, state, _, _, nil, pragma[only_bind_into](config)) and
|
||||
fwdFlow(mid, state, _, _, _, nil, pragma[only_bind_into](config)) and
|
||||
additionalJumpStep(mid, node, config) and
|
||||
cc = ccNone() and
|
||||
summaryCtx = TParameterPositionNone() and
|
||||
argAp = apNone() and
|
||||
ap = getApNil(node)
|
||||
)
|
||||
or
|
||||
exists(NodeEx mid, FlowState state0, ApNil nil |
|
||||
fwdFlow(mid, state0, _, _, nil, pragma[only_bind_into](config)) and
|
||||
fwdFlow(mid, state0, _, _, _, nil, pragma[only_bind_into](config)) and
|
||||
additionalJumpStateStep(mid, state0, node, state, config) and
|
||||
cc = ccNone() and
|
||||
summaryCtx = TParameterPositionNone() and
|
||||
argAp = apNone() and
|
||||
ap = getApNil(node)
|
||||
)
|
||||
or
|
||||
// store
|
||||
exists(TypedContent tc, Ap ap0 |
|
||||
fwdFlowStore(_, ap0, tc, node, state, cc, argAp, config) and
|
||||
fwdFlowStore(_, ap0, tc, node, state, cc, summaryCtx, argAp, config) and
|
||||
ap = apCons(tc, ap0)
|
||||
)
|
||||
or
|
||||
// read
|
||||
exists(Ap ap0, Content c |
|
||||
fwdFlowRead(ap0, c, _, node, state, cc, argAp, config) and
|
||||
fwdFlowRead(ap0, c, _, node, state, cc, summaryCtx, argAp, config) and
|
||||
fwdFlowConsCand(ap0, c, ap, config)
|
||||
)
|
||||
or
|
||||
// flow into a callable
|
||||
exists(ApApprox apa |
|
||||
fwdFlowIn(_, node, state, _, cc, _, ap, config) and
|
||||
fwdFlowIn(_, node, state, _, cc, _, _, ap, config) and
|
||||
apa = getApprox(ap) and
|
||||
if PrevStage::parameterMayFlowThrough(node, _, apa, config)
|
||||
then argAp = apSome(ap)
|
||||
else argAp = apNone()
|
||||
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
|
||||
fwdFlowOutNotFromArg(node, state, cc, argAp, ap, config)
|
||||
exists(
|
||||
DataFlowCall call, RetNodeEx ret, boolean allowsFieldFlow, CcNoCall innercc,
|
||||
DataFlowCallable inner
|
||||
|
|
||||
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()
|
||||
)
|
||||
or
|
||||
exists(DataFlowCall call, Ap argAp0 |
|
||||
fwdFlowOutFromArg(call, node, state, argAp0, ap, config) and
|
||||
fwdFlowIsEntered(call, cc, argAp, argAp0, config)
|
||||
// flow through a callable
|
||||
exists(DataFlowCall call, ParameterPosition summaryCtx0, Ap argAp0 |
|
||||
fwdFlowOutFromArg(call, node, state, summaryCtx0, argAp0, ap, config) and
|
||||
fwdFlowIsEntered(call, cc, summaryCtx, argAp, summaryCtx0, argAp0, config)
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate fwdFlowStore(
|
||||
NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, FlowState state, Cc cc, ApOption argAp,
|
||||
Configuration config
|
||||
NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, FlowState state, Cc cc,
|
||||
ParameterPositionOption summaryCtx, ApOption argAp, Configuration config
|
||||
) {
|
||||
exists(DataFlowType contentType |
|
||||
fwdFlow(node1, state, cc, argAp, ap1, config) and
|
||||
fwdFlow(node1, state, cc, summaryCtx, argAp, ap1, config) and
|
||||
PrevStage::storeStepCand(node1, unbindApa(getApprox(ap1)), tc, node2, contentType, config) and
|
||||
typecheckStore(ap1, contentType)
|
||||
)
|
||||
@@ -1366,7 +1427,7 @@ private module MkStage<StageSig PrevStage> {
|
||||
pragma[nomagic]
|
||||
private predicate fwdFlowConsCand(Ap cons, Content c, Ap tail, Configuration config) {
|
||||
exists(TypedContent tc |
|
||||
fwdFlowStore(_, tail, tc, _, _, _, _, config) and
|
||||
fwdFlowStore(_, tail, tc, _, _, _, _, _, config) and
|
||||
tc.getContent() = c and
|
||||
cons = apCons(tc, tail)
|
||||
)
|
||||
@@ -1374,21 +1435,21 @@ private module MkStage<StageSig PrevStage> {
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate fwdFlowRead(
|
||||
Ap ap, Content c, NodeEx node1, NodeEx node2, FlowState state, Cc cc, ApOption argAp,
|
||||
Configuration config
|
||||
Ap ap, Content c, NodeEx node1, NodeEx node2, FlowState state, Cc cc,
|
||||
ParameterPositionOption summaryCtx, ApOption argAp, Configuration config
|
||||
) {
|
||||
fwdFlow(node1, state, cc, 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, Cc innercc, ApOption argAp,
|
||||
Ap ap, 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, argAp, ap, 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()
|
||||
@@ -1396,29 +1457,19 @@ private module MkStage<StageSig PrevStage> {
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate fwdFlowOutNotFromArg(
|
||||
NodeEx out, FlowState state, Cc ccOut, ApOption argAp, Ap ap, Configuration config
|
||||
private predicate fwdFlowOutFromArg(
|
||||
DataFlowCall call, NodeEx out, FlowState state, ParameterPosition summaryCtx, Ap argAp, Ap ap,
|
||||
Configuration config
|
||||
) {
|
||||
exists(
|
||||
DataFlowCall call, RetNodeEx ret, boolean allowsFieldFlow, CcNoCall innercc,
|
||||
DataFlowCallable inner
|
||||
DataFlowCallable c, RetNodeEx ret, ReturnKindExt kind, boolean allowsFieldFlow, CcCall ccc
|
||||
|
|
||||
fwdFlow(ret, state, innercc, argAp, ap, config) and
|
||||
flowOutOfCall(call, ret, out, allowsFieldFlow, config) and
|
||||
inner = ret.getEnclosingCallable() and
|
||||
ccOut = getCallContextReturn(inner, call, innercc) and
|
||||
if allowsFieldFlow = false then ap instanceof ApNil else any()
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate fwdFlowOutFromArg(
|
||||
DataFlowCall call, NodeEx out, FlowState state, Ap argAp, Ap ap, Configuration config
|
||||
) {
|
||||
exists(RetNodeEx ret, boolean allowsFieldFlow, CcCall ccc |
|
||||
fwdFlow(ret, state, ccc, apSome(argAp), ap, config) and
|
||||
flowThroughOutOfCall(call, ccc, ret, out, allowsFieldFlow, config) and
|
||||
if allowsFieldFlow = false then ap instanceof ApNil else any()
|
||||
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
|
||||
(if allowsFieldFlow = false then ap instanceof ApNil else any()) and
|
||||
parameterFlowThroughAllowed(c, pragma[only_bind_into](summaryCtx), kind)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1428,11 +1479,13 @@ private module MkStage<StageSig PrevStage> {
|
||||
*/
|
||||
pragma[nomagic]
|
||||
private predicate fwdFlowIsEntered(
|
||||
DataFlowCall call, Cc cc, ApOption argAp, Ap ap, Configuration config
|
||||
DataFlowCall call, Cc cc, ParameterPositionOption summaryCtx, ApOption argAp,
|
||||
ParameterPosition pos, Ap ap, Configuration config
|
||||
) {
|
||||
exists(ParamNodeEx p |
|
||||
fwdFlowIn(call, p, _, cc, _, argAp, ap, config) and
|
||||
PrevStage::parameterMayFlowThrough(p, _, unbindApa(getApprox(ap)), config)
|
||||
exists(ParamNodeEx param |
|
||||
fwdFlowIn(call, param, _, cc, _, summaryCtx, argAp, ap, config) and
|
||||
PrevStage::parameterMayFlowThrough(param, unbindApa(getApprox(ap)), config) and
|
||||
pos = param.getPosition()
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1440,27 +1493,40 @@ private module MkStage<StageSig PrevStage> {
|
||||
private predicate storeStepFwd(
|
||||
NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, Ap ap2, Configuration config
|
||||
) {
|
||||
fwdFlowStore(node1, ap1, tc, node2, _, _, _, config) and
|
||||
fwdFlowStore(node1, ap1, tc, node2, _, _, _, _, config) and
|
||||
ap2 = apCons(tc, ap1) and
|
||||
fwdFlowRead(ap2, tc.getContent(), _, _, _, _, _, config)
|
||||
fwdFlowRead(ap2, tc.getContent(), _, _, _, _, _, _, config)
|
||||
}
|
||||
|
||||
private predicate readStepFwd(
|
||||
NodeEx n1, Ap ap1, Content c, NodeEx n2, Ap ap2, Configuration config
|
||||
) {
|
||||
fwdFlowRead(ap1, c, n1, n2, _, _, _, config) and
|
||||
fwdFlowRead(ap1, c, n1, n2, _, _, _, _, config) and
|
||||
fwdFlowConsCand(ap1, c, ap2, config)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate callMayFlowThroughFwd(DataFlowCall call, Configuration config) {
|
||||
exists(Ap argAp0, NodeEx out, FlowState state, Cc cc, ApOption argAp, Ap ap |
|
||||
fwdFlow(out, state, pragma[only_bind_into](cc), pragma[only_bind_into](argAp), ap,
|
||||
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
|
||||
fwdFlowOutFromArg(call, out, state, argAp0, ap, config) and
|
||||
fwdFlowIsEntered(pragma[only_bind_into](call), pragma[only_bind_into](cc),
|
||||
pragma[only_bind_into](argAp), pragma[only_bind_into](argAp0),
|
||||
pragma[only_bind_into](config))
|
||||
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(DataFlowCallable c, ParameterPosition ppos |
|
||||
returnFlowsThrough0(ret, kind, state, ccc, c, ppos, argAp, ap, config) and
|
||||
p.isParameterOf(c, ppos) and
|
||||
parameterFlowThroughAllowed(p, kind)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1468,118 +1534,130 @@ private module MkStage<StageSig PrevStage> {
|
||||
private predicate flowThroughIntoCall(
|
||||
DataFlowCall call, ArgNodeEx arg, ParamNodeEx p, boolean allowsFieldFlow, Configuration config
|
||||
) {
|
||||
flowIntoCall(call, arg, p, allowsFieldFlow, config) and
|
||||
fwdFlow(arg, _, _, _, _, pragma[only_bind_into](config)) and
|
||||
PrevStage::parameterMayFlowThrough(p, _, _, pragma[only_bind_into](config)) and
|
||||
callMayFlowThroughFwd(call, pragma[only_bind_into](config))
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate returnNodeMayFlowThrough(
|
||||
RetNodeEx ret, FlowState state, Ap ap, Configuration config
|
||||
) {
|
||||
fwdFlow(ret, state, any(CcCall ccc), apSome(_), ap, config)
|
||||
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))
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `node` with access path `ap` is part of a path from a source to a
|
||||
* sink in the configuration `config`.
|
||||
*
|
||||
* The Boolean `toReturn` records whether the node must be returned from the
|
||||
* enclosing callable in order to reach a sink, and if so, `returnAp` records
|
||||
* the access path of the returned value.
|
||||
* The parameter `returnCtx` records whether (and how) the node must be returned
|
||||
* from the enclosing callable in order to reach a sink, and if so, `returnAp`
|
||||
* records the access path of the returned value.
|
||||
*/
|
||||
pragma[nomagic]
|
||||
additional predicate revFlow(
|
||||
NodeEx node, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, Configuration config
|
||||
NodeEx node, FlowState state, ReturnCtx returnCtx, ApOption returnAp, Ap ap,
|
||||
Configuration config
|
||||
) {
|
||||
revFlow0(node, state, toReturn, returnAp, ap, config) and
|
||||
fwdFlow(node, state, _, _, ap, config)
|
||||
revFlow0(node, state, returnCtx, returnAp, ap, config) and
|
||||
fwdFlow(node, state, _, _, _, ap, config)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate revFlow0(
|
||||
NodeEx node, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, Configuration config
|
||||
NodeEx node, FlowState state, ReturnCtx returnCtx, ApOption returnAp, Ap ap,
|
||||
Configuration config
|
||||
) {
|
||||
fwdFlow(node, state, _, _, ap, config) and
|
||||
fwdFlow(node, state, _, _, _, ap, config) and
|
||||
sinkNode(node, state, config) and
|
||||
(if hasSinkCallCtx(config) then toReturn = true else toReturn = false) and
|
||||
(
|
||||
if hasSinkCallCtx(config)
|
||||
then returnCtx = TReturnCtxNoFlowThrough()
|
||||
else returnCtx = TReturnCtxNone()
|
||||
) and
|
||||
returnAp = apNone() and
|
||||
ap instanceof ApNil
|
||||
or
|
||||
exists(NodeEx mid, FlowState state0 |
|
||||
localStep(node, state, mid, state0, true, _, config, _) and
|
||||
revFlow(mid, state0, toReturn, returnAp, ap, config)
|
||||
revFlow(mid, state0, returnCtx, returnAp, ap, config)
|
||||
)
|
||||
or
|
||||
exists(NodeEx mid, FlowState state0, ApNil nil |
|
||||
fwdFlow(node, pragma[only_bind_into](state), _, _, ap, pragma[only_bind_into](config)) and
|
||||
fwdFlow(node, pragma[only_bind_into](state), _, _, _, ap, pragma[only_bind_into](config)) and
|
||||
localStep(node, pragma[only_bind_into](state), mid, state0, false, _, config, _) and
|
||||
revFlow(mid, state0, toReturn, returnAp, nil, pragma[only_bind_into](config)) and
|
||||
revFlow(mid, state0, returnCtx, returnAp, nil, pragma[only_bind_into](config)) and
|
||||
ap instanceof ApNil
|
||||
)
|
||||
or
|
||||
exists(NodeEx mid |
|
||||
jumpStep(node, mid, config) and
|
||||
revFlow(mid, state, _, _, ap, config) and
|
||||
toReturn = false and
|
||||
returnCtx = TReturnCtxNone() and
|
||||
returnAp = apNone()
|
||||
)
|
||||
or
|
||||
exists(NodeEx mid, ApNil nil |
|
||||
fwdFlow(node, _, _, _, ap, pragma[only_bind_into](config)) and
|
||||
fwdFlow(node, _, _, _, _, ap, pragma[only_bind_into](config)) and
|
||||
additionalJumpStep(node, mid, config) and
|
||||
revFlow(pragma[only_bind_into](mid), state, _, _, nil, pragma[only_bind_into](config)) and
|
||||
toReturn = false and
|
||||
returnCtx = TReturnCtxNone() and
|
||||
returnAp = apNone() and
|
||||
ap instanceof ApNil
|
||||
)
|
||||
or
|
||||
exists(NodeEx mid, FlowState state0, ApNil nil |
|
||||
fwdFlow(node, _, _, _, ap, pragma[only_bind_into](config)) and
|
||||
fwdFlow(node, _, _, _, _, ap, pragma[only_bind_into](config)) and
|
||||
additionalJumpStateStep(node, state, mid, state0, config) and
|
||||
revFlow(pragma[only_bind_into](mid), pragma[only_bind_into](state0), _, _, nil,
|
||||
pragma[only_bind_into](config)) and
|
||||
toReturn = false and
|
||||
returnCtx = TReturnCtxNone() and
|
||||
returnAp = apNone() and
|
||||
ap instanceof ApNil
|
||||
)
|
||||
or
|
||||
// store
|
||||
exists(Ap ap0, Content c |
|
||||
revFlowStore(ap0, c, ap, node, state, _, _, toReturn, returnAp, config) and
|
||||
revFlowStore(ap0, c, ap, node, state, _, _, returnCtx, returnAp, config) and
|
||||
revFlowConsCand(ap0, c, ap, config)
|
||||
)
|
||||
or
|
||||
// read
|
||||
exists(NodeEx mid, Ap ap0 |
|
||||
revFlow(mid, state, toReturn, returnAp, ap0, config) and
|
||||
revFlow(mid, state, returnCtx, returnAp, ap0, config) and
|
||||
readStepFwd(node, ap, _, mid, ap0, config)
|
||||
)
|
||||
or
|
||||
// flow into a callable
|
||||
revFlowInNotToReturn(node, state, returnAp, ap, config) and
|
||||
toReturn = false
|
||||
exists(ParamNodeEx p, boolean allowsFieldFlow |
|
||||
revFlow(p, state, TReturnCtxNone(), returnAp, ap, config) and
|
||||
flowIntoCall(_, node, p, allowsFieldFlow, config) and
|
||||
(if allowsFieldFlow = false then ap instanceof ApNil else any()) and
|
||||
returnCtx = TReturnCtxNone()
|
||||
)
|
||||
or
|
||||
exists(DataFlowCall call, Ap returnAp0 |
|
||||
revFlowInToReturn(call, node, state, returnAp0, ap, config) and
|
||||
revFlowIsReturned(call, toReturn, returnAp, returnAp0, config)
|
||||
// flow through a callable
|
||||
exists(DataFlowCall call, ReturnKindExt returnKind0, Ap returnAp0 |
|
||||
revFlowInToReturn(call, node, state, returnKind0, returnAp0, ap, config) and
|
||||
revFlowIsReturned(call, returnCtx, returnAp, returnKind0, returnAp0, config)
|
||||
)
|
||||
or
|
||||
// flow out of a callable
|
||||
revFlowOut(_, node, state, _, _, ap, config) and
|
||||
toReturn = true and
|
||||
if returnNodeMayFlowThrough(node, state, ap, config)
|
||||
then returnAp = apSome(ap)
|
||||
else returnAp = apNone()
|
||||
exists(ReturnKindExt kind |
|
||||
revFlowOut(_, node, kind, state, _, _, ap, config) and
|
||||
if returnFlowsThrough(node, kind, state, _, _, _, ap, config)
|
||||
then (
|
||||
returnCtx = TReturnCtxMaybeFlowThrough(kind) and
|
||||
returnAp = apSome(ap)
|
||||
) else (
|
||||
returnCtx = TReturnCtxNoFlowThrough() and returnAp = apNone()
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate revFlowStore(
|
||||
Ap ap0, Content c, Ap ap, NodeEx node, FlowState state, TypedContent tc, NodeEx mid,
|
||||
boolean toReturn, ApOption returnAp, Configuration config
|
||||
ReturnCtx returnCtx, ApOption returnAp, Configuration config
|
||||
) {
|
||||
revFlow(mid, state, toReturn, returnAp, ap0, config) and
|
||||
revFlow(mid, state, returnCtx, returnAp, ap0, config) and
|
||||
storeStepFwd(node, ap, tc, mid, ap0, config) and
|
||||
tc.getContent() = c
|
||||
}
|
||||
@@ -1599,35 +1677,27 @@ private module MkStage<StageSig PrevStage> {
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate revFlowOut(
|
||||
DataFlowCall call, RetNodeEx ret, FlowState state, boolean toReturn, ApOption returnAp, Ap ap,
|
||||
Configuration config
|
||||
DataFlowCall call, RetNodeEx ret, ReturnKindExt kind, FlowState state, ReturnCtx returnCtx,
|
||||
ApOption returnAp, Ap ap, Configuration config
|
||||
) {
|
||||
exists(NodeEx out, boolean allowsFieldFlow |
|
||||
revFlow(out, state, toReturn, returnAp, ap, config) and
|
||||
flowOutOfCall(call, ret, out, allowsFieldFlow, config) and
|
||||
if allowsFieldFlow = false then ap instanceof ApNil else any()
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate revFlowInNotToReturn(
|
||||
ArgNodeEx arg, FlowState state, ApOption returnAp, Ap ap, Configuration config
|
||||
) {
|
||||
exists(ParamNodeEx p, boolean allowsFieldFlow |
|
||||
revFlow(p, state, false, returnAp, ap, config) and
|
||||
flowIntoCall(_, arg, p, allowsFieldFlow, config) and
|
||||
revFlow(out, state, returnCtx, returnAp, ap, config) and
|
||||
flowOutOfCall(call, ret, kind, out, allowsFieldFlow, config) and
|
||||
if allowsFieldFlow = false then ap instanceof ApNil else any()
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate revFlowInToReturn(
|
||||
DataFlowCall call, ArgNodeEx arg, FlowState state, Ap returnAp, Ap ap, Configuration config
|
||||
DataFlowCall call, ArgNodeEx arg, FlowState state, ReturnKindExt kind, Ap returnAp, Ap ap,
|
||||
Configuration config
|
||||
) {
|
||||
exists(ParamNodeEx p, boolean allowsFieldFlow |
|
||||
revFlow(p, state, true, apSome(returnAp), ap, config) and
|
||||
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()
|
||||
(if allowsFieldFlow = false then ap instanceof ApNil else any()) and
|
||||
parameterFlowThroughAllowed(p, kind)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1638,11 +1708,12 @@ private module MkStage<StageSig PrevStage> {
|
||||
*/
|
||||
pragma[nomagic]
|
||||
private predicate revFlowIsReturned(
|
||||
DataFlowCall call, boolean toReturn, ApOption returnAp, Ap ap, Configuration config
|
||||
DataFlowCall call, ReturnCtx returnCtx, ApOption returnAp, ReturnKindExt kind, Ap ap,
|
||||
Configuration config
|
||||
) {
|
||||
exists(RetNodeEx ret, FlowState state, CcCall ccc |
|
||||
revFlowOut(call, ret, state, toReturn, returnAp, ap, config) and
|
||||
fwdFlow(ret, state, ccc, apSome(_), ap, config) and
|
||||
revFlowOut(call, ret, kind, state, returnCtx, returnAp, ap, config) and
|
||||
returnFlowsThrough(ret, kind, state, ccc, _, _, ap, config) and
|
||||
matchesCall(ccc, call)
|
||||
)
|
||||
}
|
||||
@@ -1713,40 +1784,39 @@ private module MkStage<StageSig PrevStage> {
|
||||
validAp(ap, config)
|
||||
}
|
||||
|
||||
pragma[noinline]
|
||||
private predicate parameterFlow(
|
||||
ParamNodeEx p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config
|
||||
pragma[nomagic]
|
||||
private predicate parameterFlowsThroughRev(
|
||||
ParamNodeEx p, Ap ap, ReturnKindExt kind, Configuration config
|
||||
) {
|
||||
revFlow(p, _, true, apSome(ap0), ap, config) and
|
||||
c = p.getEnclosingCallable()
|
||||
revFlow(p, _, TReturnCtxMaybeFlowThrough(kind), apSome(_), ap, config) and
|
||||
parameterFlowThroughAllowed(p, kind)
|
||||
}
|
||||
|
||||
predicate parameterMayFlowThrough(ParamNodeEx p, DataFlowCallable c, Ap ap, Configuration config) {
|
||||
exists(RetNodeEx ret, FlowState state, Ap ap0, ReturnKindExt kind, ParameterPosition pos |
|
||||
parameterFlow(p, ap, ap0, c, config) and
|
||||
c = ret.getEnclosingCallable() and
|
||||
revFlow(pragma[only_bind_into](ret), pragma[only_bind_into](state), true, apSome(_),
|
||||
pragma[only_bind_into](ap0), pragma[only_bind_into](config)) and
|
||||
fwdFlow(ret, state, any(CcCall ccc), apSome(ap), ap0, config) and
|
||||
kind = ret.getKind() and
|
||||
p.getPosition() = pos and
|
||||
// we don't expect a parameter to return stored in itself, unless explicitly allowed
|
||||
(
|
||||
not kind.(ParamUpdateReturnKind).getPosition() = pos
|
||||
or
|
||||
p.allowParameterReturnInSelf()
|
||||
)
|
||||
pragma[nomagic]
|
||||
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)
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
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(
|
||||
Ap returnAp0, ArgNodeEx arg, FlowState state, boolean toReturn, ApOption returnAp, Ap ap
|
||||
ReturnKindExt returnKind0, Ap returnAp0, ArgNodeEx arg, FlowState state,
|
||||
ReturnCtx returnCtx, ApOption returnAp, Ap ap
|
||||
|
|
||||
revFlow(arg, state, toReturn, returnAp, ap, config) and
|
||||
revFlowInToReturn(call, arg, state, returnAp0, ap, config) and
|
||||
revFlowIsReturned(call, toReturn, returnAp, returnAp0, config)
|
||||
revFlow(arg, state, returnCtx, returnAp, ap, config) and
|
||||
revFlowInToReturn(call, arg, state, returnKind0, returnAp0, ap, config) and
|
||||
revFlowIsReturned(call, returnCtx, returnAp, returnKind0, returnAp0, config)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1754,14 +1824,13 @@ private module MkStage<StageSig PrevStage> {
|
||||
boolean fwd, int nodes, int fields, int conscand, int states, int tuples, Configuration config
|
||||
) {
|
||||
fwd = true and
|
||||
nodes = count(NodeEx node | fwdFlow(node, _, _, _, _, config)) and
|
||||
nodes = count(NodeEx node | fwdFlow(node, _, _, _, _, _, config)) and
|
||||
fields = count(TypedContent f0 | fwdConsCand(f0, _, config)) and
|
||||
conscand = count(TypedContent f0, Ap ap | fwdConsCand(f0, ap, config)) and
|
||||
states = count(FlowState state | fwdFlow(_, state, _, _, _, config)) and
|
||||
states = count(FlowState state | fwdFlow(_, state, _, _, _, _, config)) and
|
||||
tuples =
|
||||
count(NodeEx n, FlowState state, Cc cc, ApOption argAp, Ap ap |
|
||||
fwdFlow(n, state, cc, argAp, ap, config)
|
||||
)
|
||||
count(NodeEx n, FlowState state, Cc cc, ParameterPositionOption summaryCtx, ApOption argAp,
|
||||
Ap ap | fwdFlow(n, state, cc, summaryCtx, argAp, ap, config))
|
||||
or
|
||||
fwd = false and
|
||||
nodes = count(NodeEx node | revFlow(node, _, _, _, _, config)) and
|
||||
@@ -1769,8 +1838,8 @@ private module MkStage<StageSig PrevStage> {
|
||||
conscand = count(TypedContent f0, Ap ap | consCand(f0, ap, config)) and
|
||||
states = count(FlowState state | revFlow(_, state, _, _, _, config)) and
|
||||
tuples =
|
||||
count(NodeEx n, FlowState state, boolean b, ApOption retAp, Ap ap |
|
||||
revFlow(n, state, b, retAp, ap, config)
|
||||
count(NodeEx n, FlowState state, ReturnCtx returnCtx, ApOption retAp, Ap ap |
|
||||
revFlow(n, state, returnCtx, retAp, ap, config)
|
||||
)
|
||||
}
|
||||
/* End: Stage logic. */
|
||||
@@ -1915,7 +1984,7 @@ private module Stage2Param implements MkStage<Stage1>::StageParam {
|
||||
exists(lcc)
|
||||
}
|
||||
|
||||
predicate flowOutOfCall = flowOutOfCallNodeCand1/5;
|
||||
predicate flowOutOfCall = flowOutOfCallNodeCand1/6;
|
||||
|
||||
predicate flowIntoCall = flowIntoCallNodeCand1/5;
|
||||
|
||||
@@ -1951,9 +2020,10 @@ private module Stage2 implements StageSig {
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate flowOutOfCallNodeCand2(
|
||||
DataFlowCall call, RetNodeEx node1, NodeEx node2, boolean allowsFieldFlow, Configuration config
|
||||
DataFlowCall call, RetNodeEx node1, ReturnKindExt kind, NodeEx node2, boolean allowsFieldFlow,
|
||||
Configuration config
|
||||
) {
|
||||
flowOutOfCallNodeCand1(call, node1, node2, allowsFieldFlow, config) and
|
||||
flowOutOfCallNodeCand1(call, node1, kind, node2, allowsFieldFlow, config) and
|
||||
Stage2::revFlow(node2, pragma[only_bind_into](config)) and
|
||||
Stage2::revFlowAlias(node1, pragma[only_bind_into](config))
|
||||
}
|
||||
@@ -2021,8 +2091,8 @@ private module LocalFlowBigStep {
|
||||
exists(NodeEx next | Stage2::revFlow(next, state, config) |
|
||||
jumpStep(node, next, config) or
|
||||
additionalJumpStep(node, next, config) or
|
||||
flowIntoCallNodeCand1(_, node, next, config) or
|
||||
flowOutOfCallNodeCand1(_, node, next, config) or
|
||||
flowIntoCallNodeCand2(_, node, next, _, config) or
|
||||
flowOutOfCallNodeCand2(_, node, _, next, _, config) or
|
||||
Stage2::storeStepCand(node, _, _, next, _, config) or
|
||||
Stage2::readStepCand(node, _, next, config)
|
||||
)
|
||||
@@ -2163,7 +2233,7 @@ private module Stage3Param implements MkStage<Stage2>::StageParam {
|
||||
localFlowBigStep(node1, state1, node2, state2, preservesValue, ap, config, _) and exists(lcc)
|
||||
}
|
||||
|
||||
predicate flowOutOfCall = flowOutOfCallNodeCand2/5;
|
||||
predicate flowOutOfCall = flowOutOfCallNodeCand2/6;
|
||||
|
||||
predicate flowIntoCall = flowIntoCallNodeCand2/5;
|
||||
|
||||
@@ -2233,8 +2303,9 @@ private predicate flowCandSummaryCtx(
|
||||
NodeEx node, FlowState state, AccessPathFront argApf, Configuration config
|
||||
) {
|
||||
exists(AccessPathFront apf |
|
||||
Stage3::revFlow(node, state, true, _, apf, config) and
|
||||
Stage3::fwdFlow(node, state, any(Stage3::CcCall ccc), TAccessPathFrontSome(argApf), apf, config)
|
||||
Stage3::revFlow(node, state, TReturnCtxMaybeFlowThrough(_), _, apf, config) and
|
||||
Stage3::fwdFlow(node, state, any(Stage3::CcCall ccc), _, TAccessPathFrontSome(argApf), apf,
|
||||
config)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -2468,10 +2539,11 @@ private module Stage4Param implements MkStage<Stage3>::StageParam {
|
||||
|
||||
pragma[nomagic]
|
||||
predicate flowOutOfCall(
|
||||
DataFlowCall call, RetNodeEx node1, NodeEx node2, boolean allowsFieldFlow, Configuration config
|
||||
DataFlowCall call, RetNodeEx node1, ReturnKindExt kind, NodeEx node2, boolean allowsFieldFlow,
|
||||
Configuration config
|
||||
) {
|
||||
exists(FlowState state |
|
||||
flowOutOfCallNodeCand2(call, node1, node2, allowsFieldFlow, config) and
|
||||
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))
|
||||
@@ -2508,13 +2580,14 @@ private Configuration unbindConf(Configuration conf) {
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate nodeMayUseSummary0(
|
||||
NodeEx n, DataFlowCallable c, FlowState state, AccessPathApprox apa, Configuration config
|
||||
NodeEx n, DataFlowCallable c, ParameterPosition pos, FlowState state, AccessPathApprox apa,
|
||||
Configuration config
|
||||
) {
|
||||
exists(AccessPathApprox apa0 |
|
||||
Stage4::parameterMayFlowThrough(_, c, _, _) and
|
||||
Stage4::revFlow(n, state, true, _, apa0, config) and
|
||||
Stage4::fwdFlow(n, state, any(CallContextCall ccc), TAccessPathApproxSome(apa), apa0, config) and
|
||||
n.getEnclosingCallable() = c
|
||||
c = n.getEnclosingCallable() and
|
||||
Stage4::revFlow(n, state, TReturnCtxMaybeFlowThrough(_), _, apa0, config) and
|
||||
Stage4::fwdFlow(n, state, any(CallContextCall ccc), TParameterPositionSome(pos),
|
||||
TAccessPathApproxSome(apa), apa0, config)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -2522,9 +2595,10 @@ pragma[nomagic]
|
||||
private predicate nodeMayUseSummary(
|
||||
NodeEx n, FlowState state, AccessPathApprox apa, Configuration config
|
||||
) {
|
||||
exists(DataFlowCallable c |
|
||||
Stage4::parameterMayFlowThrough(_, c, apa, config) and
|
||||
nodeMayUseSummary0(n, c, state, apa, config)
|
||||
exists(DataFlowCallable c, ParameterPosition pos, ParamNodeEx p |
|
||||
Stage4::parameterMayFlowThrough(p, apa, config) and
|
||||
nodeMayUseSummary0(n, c, pos, state, apa, config) and
|
||||
p.isParameterOf(c, pos)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -2532,7 +2606,7 @@ private newtype TSummaryCtx =
|
||||
TSummaryCtxNone() or
|
||||
TSummaryCtxSome(ParamNodeEx p, FlowState state, AccessPath ap) {
|
||||
exists(Configuration config |
|
||||
Stage4::parameterMayFlowThrough(p, _, ap.getApprox(), config) and
|
||||
Stage4::parameterMayFlowThrough(p, ap.getApprox(), config) and
|
||||
Stage4::revFlow(p, state, _, config)
|
||||
)
|
||||
}
|
||||
@@ -3453,17 +3527,11 @@ private predicate paramFlowsThrough(
|
||||
ReturnKindExt kind, FlowState state, CallContextCall cc, SummaryCtxSome sc, AccessPath ap,
|
||||
AccessPathApprox apa, Configuration config
|
||||
) {
|
||||
exists(PathNodeMid mid, RetNodeEx ret, ParameterPosition pos |
|
||||
exists(PathNodeMid mid, RetNodeEx ret |
|
||||
pathNode(mid, ret, state, cc, sc, ap, config, _) and
|
||||
kind = ret.getKind() and
|
||||
apa = ap.getApprox() and
|
||||
pos = sc.getParameterPos() and
|
||||
// we don't expect a parameter to return stored in itself, unless explicitly allowed
|
||||
(
|
||||
not kind.(ParamUpdateReturnKind).getPosition() = pos
|
||||
or
|
||||
sc.getParamNode().allowParameterReturnInSelf()
|
||||
)
|
||||
parameterFlowThroughAllowed(sc.getParamNode(), kind)
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@@ -915,6 +915,17 @@ private module Cached {
|
||||
TDataFlowCallNone() or
|
||||
TDataFlowCallSome(DataFlowCall call)
|
||||
|
||||
cached
|
||||
newtype TParameterPositionOption =
|
||||
TParameterPositionNone() or
|
||||
TParameterPositionSome(ParameterPosition pos)
|
||||
|
||||
cached
|
||||
newtype TReturnCtx =
|
||||
TReturnCtxNone() or
|
||||
TReturnCtxNoFlowThrough() or
|
||||
TReturnCtxMaybeFlowThrough(ReturnKindExt kind)
|
||||
|
||||
cached
|
||||
newtype TTypedContent = MkTypedContent(Content c, DataFlowType t) { store(_, c, _, _, t) }
|
||||
|
||||
@@ -1304,6 +1315,44 @@ class DataFlowCallOption extends TDataFlowCallOption {
|
||||
}
|
||||
}
|
||||
|
||||
/** An optional `ParameterPosition`. */
|
||||
class ParameterPositionOption extends TParameterPositionOption {
|
||||
string toString() {
|
||||
this = TParameterPositionNone() and
|
||||
result = "(none)"
|
||||
or
|
||||
exists(ParameterPosition pos |
|
||||
this = TParameterPositionSome(pos) and
|
||||
result = pos.toString()
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A return context used to calculate flow summaries in reverse flow.
|
||||
*
|
||||
* The possible values are:
|
||||
*
|
||||
* - `TReturnCtxNone()`: no return flow.
|
||||
* - `TReturnCtxNoFlowThrough()`: return flow, but flow through is not possible.
|
||||
* - `TReturnCtxMaybeFlowThrough(ReturnKindExt kind)`: return flow, of kind `kind`, and
|
||||
* flow through may be possible.
|
||||
*/
|
||||
class ReturnCtx extends TReturnCtx {
|
||||
string toString() {
|
||||
this = TReturnCtxNone() and
|
||||
result = "(none)"
|
||||
or
|
||||
this = TReturnCtxNoFlowThrough() and
|
||||
result = "(no flow through)"
|
||||
or
|
||||
exists(ReturnKindExt kind |
|
||||
this = TReturnCtxMaybeFlowThrough(kind) and
|
||||
result = kind.toString()
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/** A `Content` tagged with the type of a containing object. */
|
||||
class TypedContent extends MkTypedContent {
|
||||
private Content c;
|
||||
|
||||
@@ -244,4 +244,20 @@ module Consistency {
|
||||
not callable = viableCallable(call) and
|
||||
not any(ConsistencyConfiguration c).viableImplInCallContextTooLargeExclude(call, ctx, callable)
|
||||
}
|
||||
|
||||
query predicate uniqueParameterNodeAtPosition(
|
||||
DataFlowCallable c, ParameterPosition pos, Node p, string msg
|
||||
) {
|
||||
isParameterNode(p, c, pos) and
|
||||
not exists(unique(Node p0 | isParameterNode(p0, c, pos))) and
|
||||
msg = "Parameters with overlapping positions."
|
||||
}
|
||||
|
||||
query predicate uniqueParameterNodePosition(
|
||||
DataFlowCallable c, ParameterPosition pos, Node p, string msg
|
||||
) {
|
||||
isParameterNode(p, c, pos) and
|
||||
not exists(unique(ParameterPosition pos0 | isParameterNode(p, c, pos0))) and
|
||||
msg = "Parameter node with multiple positions."
|
||||
}
|
||||
}
|
||||
|
||||
@@ -70,7 +70,7 @@ abstract class Configuration extends string {
|
||||
/**
|
||||
* Holds if `sink` is a relevant data flow sink accepting `state`.
|
||||
*/
|
||||
predicate isSink(Node source, FlowState state) { none() }
|
||||
predicate isSink(Node sink, FlowState state) { none() }
|
||||
|
||||
/**
|
||||
* Holds if data flow through `node` is prohibited. This completely removes
|
||||
@@ -319,8 +319,6 @@ private class ParamNodeEx extends NodeEx {
|
||||
}
|
||||
|
||||
ParameterPosition getPosition() { this.isParameterOf(_, result) }
|
||||
|
||||
predicate allowParameterReturnInSelf() { allowParameterReturnInSelfCached(this.asNode()) }
|
||||
}
|
||||
|
||||
private class RetNodeEx extends NodeEx {
|
||||
@@ -608,6 +606,38 @@ private predicate hasSinkCallCtx(Configuration config) {
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if flow from `p` to a return node of kind `kind` is allowed.
|
||||
*
|
||||
* We don't expect a parameter to return stored in itself, unless
|
||||
* explicitly allowed
|
||||
*/
|
||||
bindingset[p, kind]
|
||||
private predicate parameterFlowThroughAllowed(ParamNodeEx p, ReturnKindExt kind) {
|
||||
exists(ParameterPosition pos | p.isParameterOf(_, pos) |
|
||||
not kind.(ParamUpdateReturnKind).getPosition() = pos
|
||||
or
|
||||
allowParameterReturnInSelfCached(p.asNode())
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* 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;
|
||||
|
||||
@@ -981,21 +1011,22 @@ private module Stage1 implements StageSig {
|
||||
* candidate for the origin of a summary.
|
||||
*/
|
||||
pragma[nomagic]
|
||||
predicate parameterMayFlowThrough(ParamNodeEx p, DataFlowCallable c, Ap ap, Configuration config) {
|
||||
exists(ReturnKindExt kind |
|
||||
predicate parameterMayFlowThrough(ParamNodeEx p, Ap ap, Configuration config) {
|
||||
exists(DataFlowCallable c, ReturnKindExt kind |
|
||||
throughFlowNodeCand(p, config) and
|
||||
returnFlowCallableNodeCand(c, kind, config) and
|
||||
p.getEnclosingCallable() = c and
|
||||
exists(ap) and
|
||||
// we don't expect a parameter to return stored in itself, unless explicitly allowed
|
||||
(
|
||||
not kind.(ParamUpdateReturnKind).getPosition() = p.getPosition()
|
||||
or
|
||||
p.allowParameterReturnInSelf()
|
||||
)
|
||||
parameterFlowThroughAllowed(p, kind)
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
predicate returnMayFlowThrough(RetNodeEx ret, ReturnKindExt kind, Configuration config) {
|
||||
throughFlowNodeCand(ret, config) and
|
||||
kind = ret.getKind()
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
predicate callMayFlowThroughRev(DataFlowCall call, Configuration config) {
|
||||
exists(ArgNodeEx arg, boolean toReturn |
|
||||
@@ -1052,12 +1083,16 @@ private predicate viableReturnPosOutNodeCand1(
|
||||
*/
|
||||
pragma[nomagic]
|
||||
private predicate flowOutOfCallNodeCand1(
|
||||
DataFlowCall call, RetNodeEx ret, NodeEx out, Configuration config
|
||||
DataFlowCall call, RetNodeEx ret, ReturnKindExt kind, NodeEx out, Configuration config
|
||||
) {
|
||||
viableReturnPosOutNodeCand1(call, ret.getReturnPosition(), out, config) and
|
||||
Stage1::revFlow(ret, config) and
|
||||
not outBarrier(ret, config) and
|
||||
not inBarrier(out, config)
|
||||
exists(ReturnPosition pos |
|
||||
viableReturnPosOutNodeCand1(call, pos, out, config) and
|
||||
pos = ret.getReturnPosition() and
|
||||
kind = pos.getKind() and
|
||||
Stage1::revFlow(ret, config) and
|
||||
not outBarrier(ret, config) and
|
||||
not inBarrier(out, config)
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
@@ -1087,10 +1122,11 @@ private predicate flowIntoCallNodeCand1(
|
||||
* edge in the graph of paths between sources and sinks that ignores call
|
||||
* contexts.
|
||||
*/
|
||||
pragma[nomagic]
|
||||
private int branch(NodeEx n1, Configuration conf) {
|
||||
result =
|
||||
strictcount(NodeEx n |
|
||||
flowOutOfCallNodeCand1(_, n1, n, conf) or flowIntoCallNodeCand1(_, n1, n, conf)
|
||||
flowOutOfCallNodeCand1(_, n1, _, n, conf) or flowIntoCallNodeCand1(_, n1, n, conf)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1099,10 +1135,11 @@ private int branch(NodeEx n1, Configuration conf) {
|
||||
* edge in the graph of paths between sources and sinks that ignores call
|
||||
* contexts.
|
||||
*/
|
||||
pragma[nomagic]
|
||||
private int join(NodeEx n2, Configuration conf) {
|
||||
result =
|
||||
strictcount(NodeEx n |
|
||||
flowOutOfCallNodeCand1(_, n, n2, conf) or flowIntoCallNodeCand1(_, n, n2, conf)
|
||||
flowOutOfCallNodeCand1(_, n, _, n2, conf) or flowIntoCallNodeCand1(_, n, n2, conf)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1115,12 +1152,13 @@ private int join(NodeEx n2, Configuration conf) {
|
||||
*/
|
||||
pragma[nomagic]
|
||||
private predicate flowOutOfCallNodeCand1(
|
||||
DataFlowCall call, RetNodeEx ret, NodeEx out, boolean allowsFieldFlow, Configuration config
|
||||
DataFlowCall call, RetNodeEx ret, ReturnKindExt kind, NodeEx out, boolean allowsFieldFlow,
|
||||
Configuration config
|
||||
) {
|
||||
flowOutOfCallNodeCand1(call, ret, out, config) and
|
||||
flowOutOfCallNodeCand1(call, ret, kind, out, pragma[only_bind_into](config)) and
|
||||
exists(int b, int j |
|
||||
b = branch(ret, config) and
|
||||
j = join(out, config) and
|
||||
b = branch(ret, pragma[only_bind_into](config)) and
|
||||
j = join(out, pragma[only_bind_into](config)) and
|
||||
if b.minimum(j) <= config.fieldFlowBranchLimit()
|
||||
then allowsFieldFlow = true
|
||||
else allowsFieldFlow = false
|
||||
@@ -1136,10 +1174,10 @@ pragma[nomagic]
|
||||
private predicate flowIntoCallNodeCand1(
|
||||
DataFlowCall call, ArgNodeEx arg, ParamNodeEx p, boolean allowsFieldFlow, Configuration config
|
||||
) {
|
||||
flowIntoCallNodeCand1(call, arg, p, config) and
|
||||
flowIntoCallNodeCand1(call, arg, p, pragma[only_bind_into](config)) and
|
||||
exists(int b, int j |
|
||||
b = branch(arg, config) and
|
||||
j = join(p, config) and
|
||||
b = branch(arg, pragma[only_bind_into](config)) and
|
||||
j = join(p, pragma[only_bind_into](config)) and
|
||||
if b.minimum(j) <= config.fieldFlowBranchLimit()
|
||||
then allowsFieldFlow = true
|
||||
else allowsFieldFlow = false
|
||||
@@ -1156,7 +1194,9 @@ private signature module StageSig {
|
||||
|
||||
predicate callMayFlowThroughRev(DataFlowCall call, Configuration config);
|
||||
|
||||
predicate parameterMayFlowThrough(ParamNodeEx p, DataFlowCallable c, Ap ap, Configuration config);
|
||||
predicate parameterMayFlowThrough(ParamNodeEx p, Ap ap, Configuration config);
|
||||
|
||||
predicate returnMayFlowThrough(RetNodeEx ret, ReturnKindExt kind, Configuration config);
|
||||
|
||||
predicate storeStepCand(
|
||||
NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, DataFlowType contentType,
|
||||
@@ -1222,7 +1262,8 @@ private module MkStage<StageSig PrevStage> {
|
||||
);
|
||||
|
||||
predicate flowOutOfCall(
|
||||
DataFlowCall call, RetNodeEx ret, NodeEx out, boolean allowsFieldFlow, Configuration config
|
||||
DataFlowCall call, RetNodeEx ret, ReturnKindExt kind, NodeEx out, boolean allowsFieldFlow,
|
||||
Configuration config
|
||||
);
|
||||
|
||||
predicate flowIntoCall(
|
||||
@@ -1247,14 +1288,14 @@ private module MkStage<StageSig PrevStage> {
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate flowThroughOutOfCall(
|
||||
DataFlowCall call, CcCall ccc, RetNodeEx ret, NodeEx out, boolean allowsFieldFlow,
|
||||
Configuration config
|
||||
DataFlowCall call, DataFlowCallable c, CcCall ccc, RetNodeEx ret, ReturnKindExt kind,
|
||||
NodeEx out, boolean allowsFieldFlow, Configuration config
|
||||
) {
|
||||
flowOutOfCall(call, ret, out, allowsFieldFlow, pragma[only_bind_into](config)) and
|
||||
flowOutOfCall(call, ret, kind, out, allowsFieldFlow, pragma[only_bind_into](config)) and
|
||||
PrevStage::callMayFlowThroughRev(call, pragma[only_bind_into](config)) and
|
||||
PrevStage::parameterMayFlowThrough(_, ret.getEnclosingCallable(), _,
|
||||
pragma[only_bind_into](config)) and
|
||||
matchesCall(ccc, call)
|
||||
PrevStage::returnMayFlowThrough(ret, kind, pragma[only_bind_into](config)) and
|
||||
matchesCall(ccc, call) and
|
||||
c = ret.getEnclosingCallable()
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1262,29 +1303,32 @@ private module MkStage<StageSig PrevStage> {
|
||||
* configuration `config`.
|
||||
*
|
||||
* The call context `cc` records whether the node is reached through an
|
||||
* argument in a call, and if so, `argAp` records the access path of that
|
||||
* argument.
|
||||
* argument in a call, and if so, `summaryCtx` and `argAp` record the
|
||||
* corresponding parameter position and access path of that argument, respectively.
|
||||
*/
|
||||
pragma[nomagic]
|
||||
additional predicate fwdFlow(
|
||||
NodeEx node, FlowState state, Cc cc, ApOption argAp, Ap ap, Configuration config
|
||||
NodeEx node, FlowState state, Cc cc, ParameterPositionOption summaryCtx, ApOption argAp,
|
||||
Ap ap, Configuration config
|
||||
) {
|
||||
fwdFlow0(node, state, cc, argAp, ap, config) and
|
||||
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, ApOption argAp, Ap ap, Configuration config
|
||||
NodeEx node, FlowState state, Cc cc, ParameterPositionOption summaryCtx, ApOption argAp,
|
||||
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)
|
||||
or
|
||||
exists(NodeEx mid, FlowState state0, Ap ap0, LocalCc localCc |
|
||||
fwdFlow(mid, state0, cc, argAp, ap0, config) and
|
||||
fwdFlow(mid, state0, cc, summaryCtx, argAp, ap0, config) and
|
||||
localCc = getLocalCc(mid, cc)
|
||||
|
|
||||
localStep(mid, state0, node, state, true, _, config, localCc) and
|
||||
@@ -1295,65 +1339,82 @@ private module MkStage<StageSig PrevStage> {
|
||||
)
|
||||
or
|
||||
exists(NodeEx mid |
|
||||
fwdFlow(mid, pragma[only_bind_into](state), _, _, ap, 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
|
||||
argAp = apNone()
|
||||
)
|
||||
or
|
||||
exists(NodeEx mid, ApNil nil |
|
||||
fwdFlow(mid, state, _, _, nil, pragma[only_bind_into](config)) and
|
||||
fwdFlow(mid, state, _, _, _, nil, pragma[only_bind_into](config)) and
|
||||
additionalJumpStep(mid, node, config) and
|
||||
cc = ccNone() and
|
||||
summaryCtx = TParameterPositionNone() and
|
||||
argAp = apNone() and
|
||||
ap = getApNil(node)
|
||||
)
|
||||
or
|
||||
exists(NodeEx mid, FlowState state0, ApNil nil |
|
||||
fwdFlow(mid, state0, _, _, nil, pragma[only_bind_into](config)) and
|
||||
fwdFlow(mid, state0, _, _, _, nil, pragma[only_bind_into](config)) and
|
||||
additionalJumpStateStep(mid, state0, node, state, config) and
|
||||
cc = ccNone() and
|
||||
summaryCtx = TParameterPositionNone() and
|
||||
argAp = apNone() and
|
||||
ap = getApNil(node)
|
||||
)
|
||||
or
|
||||
// store
|
||||
exists(TypedContent tc, Ap ap0 |
|
||||
fwdFlowStore(_, ap0, tc, node, state, cc, argAp, config) and
|
||||
fwdFlowStore(_, ap0, tc, node, state, cc, summaryCtx, argAp, config) and
|
||||
ap = apCons(tc, ap0)
|
||||
)
|
||||
or
|
||||
// read
|
||||
exists(Ap ap0, Content c |
|
||||
fwdFlowRead(ap0, c, _, node, state, cc, argAp, config) and
|
||||
fwdFlowRead(ap0, c, _, node, state, cc, summaryCtx, argAp, config) and
|
||||
fwdFlowConsCand(ap0, c, ap, config)
|
||||
)
|
||||
or
|
||||
// flow into a callable
|
||||
exists(ApApprox apa |
|
||||
fwdFlowIn(_, node, state, _, cc, _, ap, config) and
|
||||
fwdFlowIn(_, node, state, _, cc, _, _, ap, config) and
|
||||
apa = getApprox(ap) and
|
||||
if PrevStage::parameterMayFlowThrough(node, _, apa, config)
|
||||
then argAp = apSome(ap)
|
||||
else argAp = apNone()
|
||||
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
|
||||
fwdFlowOutNotFromArg(node, state, cc, argAp, ap, config)
|
||||
exists(
|
||||
DataFlowCall call, RetNodeEx ret, boolean allowsFieldFlow, CcNoCall innercc,
|
||||
DataFlowCallable inner
|
||||
|
|
||||
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()
|
||||
)
|
||||
or
|
||||
exists(DataFlowCall call, Ap argAp0 |
|
||||
fwdFlowOutFromArg(call, node, state, argAp0, ap, config) and
|
||||
fwdFlowIsEntered(call, cc, argAp, argAp0, config)
|
||||
// flow through a callable
|
||||
exists(DataFlowCall call, ParameterPosition summaryCtx0, Ap argAp0 |
|
||||
fwdFlowOutFromArg(call, node, state, summaryCtx0, argAp0, ap, config) and
|
||||
fwdFlowIsEntered(call, cc, summaryCtx, argAp, summaryCtx0, argAp0, config)
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate fwdFlowStore(
|
||||
NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, FlowState state, Cc cc, ApOption argAp,
|
||||
Configuration config
|
||||
NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, FlowState state, Cc cc,
|
||||
ParameterPositionOption summaryCtx, ApOption argAp, Configuration config
|
||||
) {
|
||||
exists(DataFlowType contentType |
|
||||
fwdFlow(node1, state, cc, argAp, ap1, config) and
|
||||
fwdFlow(node1, state, cc, summaryCtx, argAp, ap1, config) and
|
||||
PrevStage::storeStepCand(node1, unbindApa(getApprox(ap1)), tc, node2, contentType, config) and
|
||||
typecheckStore(ap1, contentType)
|
||||
)
|
||||
@@ -1366,7 +1427,7 @@ private module MkStage<StageSig PrevStage> {
|
||||
pragma[nomagic]
|
||||
private predicate fwdFlowConsCand(Ap cons, Content c, Ap tail, Configuration config) {
|
||||
exists(TypedContent tc |
|
||||
fwdFlowStore(_, tail, tc, _, _, _, _, config) and
|
||||
fwdFlowStore(_, tail, tc, _, _, _, _, _, config) and
|
||||
tc.getContent() = c and
|
||||
cons = apCons(tc, tail)
|
||||
)
|
||||
@@ -1374,21 +1435,21 @@ private module MkStage<StageSig PrevStage> {
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate fwdFlowRead(
|
||||
Ap ap, Content c, NodeEx node1, NodeEx node2, FlowState state, Cc cc, ApOption argAp,
|
||||
Configuration config
|
||||
Ap ap, Content c, NodeEx node1, NodeEx node2, FlowState state, Cc cc,
|
||||
ParameterPositionOption summaryCtx, ApOption argAp, Configuration config
|
||||
) {
|
||||
fwdFlow(node1, state, cc, 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, Cc innercc, ApOption argAp,
|
||||
Ap ap, 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, argAp, ap, 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()
|
||||
@@ -1396,29 +1457,19 @@ private module MkStage<StageSig PrevStage> {
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate fwdFlowOutNotFromArg(
|
||||
NodeEx out, FlowState state, Cc ccOut, ApOption argAp, Ap ap, Configuration config
|
||||
private predicate fwdFlowOutFromArg(
|
||||
DataFlowCall call, NodeEx out, FlowState state, ParameterPosition summaryCtx, Ap argAp, Ap ap,
|
||||
Configuration config
|
||||
) {
|
||||
exists(
|
||||
DataFlowCall call, RetNodeEx ret, boolean allowsFieldFlow, CcNoCall innercc,
|
||||
DataFlowCallable inner
|
||||
DataFlowCallable c, RetNodeEx ret, ReturnKindExt kind, boolean allowsFieldFlow, CcCall ccc
|
||||
|
|
||||
fwdFlow(ret, state, innercc, argAp, ap, config) and
|
||||
flowOutOfCall(call, ret, out, allowsFieldFlow, config) and
|
||||
inner = ret.getEnclosingCallable() and
|
||||
ccOut = getCallContextReturn(inner, call, innercc) and
|
||||
if allowsFieldFlow = false then ap instanceof ApNil else any()
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate fwdFlowOutFromArg(
|
||||
DataFlowCall call, NodeEx out, FlowState state, Ap argAp, Ap ap, Configuration config
|
||||
) {
|
||||
exists(RetNodeEx ret, boolean allowsFieldFlow, CcCall ccc |
|
||||
fwdFlow(ret, state, ccc, apSome(argAp), ap, config) and
|
||||
flowThroughOutOfCall(call, ccc, ret, out, allowsFieldFlow, config) and
|
||||
if allowsFieldFlow = false then ap instanceof ApNil else any()
|
||||
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
|
||||
(if allowsFieldFlow = false then ap instanceof ApNil else any()) and
|
||||
parameterFlowThroughAllowed(c, pragma[only_bind_into](summaryCtx), kind)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1428,11 +1479,13 @@ private module MkStage<StageSig PrevStage> {
|
||||
*/
|
||||
pragma[nomagic]
|
||||
private predicate fwdFlowIsEntered(
|
||||
DataFlowCall call, Cc cc, ApOption argAp, Ap ap, Configuration config
|
||||
DataFlowCall call, Cc cc, ParameterPositionOption summaryCtx, ApOption argAp,
|
||||
ParameterPosition pos, Ap ap, Configuration config
|
||||
) {
|
||||
exists(ParamNodeEx p |
|
||||
fwdFlowIn(call, p, _, cc, _, argAp, ap, config) and
|
||||
PrevStage::parameterMayFlowThrough(p, _, unbindApa(getApprox(ap)), config)
|
||||
exists(ParamNodeEx param |
|
||||
fwdFlowIn(call, param, _, cc, _, summaryCtx, argAp, ap, config) and
|
||||
PrevStage::parameterMayFlowThrough(param, unbindApa(getApprox(ap)), config) and
|
||||
pos = param.getPosition()
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1440,27 +1493,40 @@ private module MkStage<StageSig PrevStage> {
|
||||
private predicate storeStepFwd(
|
||||
NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, Ap ap2, Configuration config
|
||||
) {
|
||||
fwdFlowStore(node1, ap1, tc, node2, _, _, _, config) and
|
||||
fwdFlowStore(node1, ap1, tc, node2, _, _, _, _, config) and
|
||||
ap2 = apCons(tc, ap1) and
|
||||
fwdFlowRead(ap2, tc.getContent(), _, _, _, _, _, config)
|
||||
fwdFlowRead(ap2, tc.getContent(), _, _, _, _, _, _, config)
|
||||
}
|
||||
|
||||
private predicate readStepFwd(
|
||||
NodeEx n1, Ap ap1, Content c, NodeEx n2, Ap ap2, Configuration config
|
||||
) {
|
||||
fwdFlowRead(ap1, c, n1, n2, _, _, _, config) and
|
||||
fwdFlowRead(ap1, c, n1, n2, _, _, _, _, config) and
|
||||
fwdFlowConsCand(ap1, c, ap2, config)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate callMayFlowThroughFwd(DataFlowCall call, Configuration config) {
|
||||
exists(Ap argAp0, NodeEx out, FlowState state, Cc cc, ApOption argAp, Ap ap |
|
||||
fwdFlow(out, state, pragma[only_bind_into](cc), pragma[only_bind_into](argAp), ap,
|
||||
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
|
||||
fwdFlowOutFromArg(call, out, state, argAp0, ap, config) and
|
||||
fwdFlowIsEntered(pragma[only_bind_into](call), pragma[only_bind_into](cc),
|
||||
pragma[only_bind_into](argAp), pragma[only_bind_into](argAp0),
|
||||
pragma[only_bind_into](config))
|
||||
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(DataFlowCallable c, ParameterPosition ppos |
|
||||
returnFlowsThrough0(ret, kind, state, ccc, c, ppos, argAp, ap, config) and
|
||||
p.isParameterOf(c, ppos) and
|
||||
parameterFlowThroughAllowed(p, kind)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1468,118 +1534,130 @@ private module MkStage<StageSig PrevStage> {
|
||||
private predicate flowThroughIntoCall(
|
||||
DataFlowCall call, ArgNodeEx arg, ParamNodeEx p, boolean allowsFieldFlow, Configuration config
|
||||
) {
|
||||
flowIntoCall(call, arg, p, allowsFieldFlow, config) and
|
||||
fwdFlow(arg, _, _, _, _, pragma[only_bind_into](config)) and
|
||||
PrevStage::parameterMayFlowThrough(p, _, _, pragma[only_bind_into](config)) and
|
||||
callMayFlowThroughFwd(call, pragma[only_bind_into](config))
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate returnNodeMayFlowThrough(
|
||||
RetNodeEx ret, FlowState state, Ap ap, Configuration config
|
||||
) {
|
||||
fwdFlow(ret, state, any(CcCall ccc), apSome(_), ap, config)
|
||||
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))
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `node` with access path `ap` is part of a path from a source to a
|
||||
* sink in the configuration `config`.
|
||||
*
|
||||
* The Boolean `toReturn` records whether the node must be returned from the
|
||||
* enclosing callable in order to reach a sink, and if so, `returnAp` records
|
||||
* the access path of the returned value.
|
||||
* The parameter `returnCtx` records whether (and how) the node must be returned
|
||||
* from the enclosing callable in order to reach a sink, and if so, `returnAp`
|
||||
* records the access path of the returned value.
|
||||
*/
|
||||
pragma[nomagic]
|
||||
additional predicate revFlow(
|
||||
NodeEx node, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, Configuration config
|
||||
NodeEx node, FlowState state, ReturnCtx returnCtx, ApOption returnAp, Ap ap,
|
||||
Configuration config
|
||||
) {
|
||||
revFlow0(node, state, toReturn, returnAp, ap, config) and
|
||||
fwdFlow(node, state, _, _, ap, config)
|
||||
revFlow0(node, state, returnCtx, returnAp, ap, config) and
|
||||
fwdFlow(node, state, _, _, _, ap, config)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate revFlow0(
|
||||
NodeEx node, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, Configuration config
|
||||
NodeEx node, FlowState state, ReturnCtx returnCtx, ApOption returnAp, Ap ap,
|
||||
Configuration config
|
||||
) {
|
||||
fwdFlow(node, state, _, _, ap, config) and
|
||||
fwdFlow(node, state, _, _, _, ap, config) and
|
||||
sinkNode(node, state, config) and
|
||||
(if hasSinkCallCtx(config) then toReturn = true else toReturn = false) and
|
||||
(
|
||||
if hasSinkCallCtx(config)
|
||||
then returnCtx = TReturnCtxNoFlowThrough()
|
||||
else returnCtx = TReturnCtxNone()
|
||||
) and
|
||||
returnAp = apNone() and
|
||||
ap instanceof ApNil
|
||||
or
|
||||
exists(NodeEx mid, FlowState state0 |
|
||||
localStep(node, state, mid, state0, true, _, config, _) and
|
||||
revFlow(mid, state0, toReturn, returnAp, ap, config)
|
||||
revFlow(mid, state0, returnCtx, returnAp, ap, config)
|
||||
)
|
||||
or
|
||||
exists(NodeEx mid, FlowState state0, ApNil nil |
|
||||
fwdFlow(node, pragma[only_bind_into](state), _, _, ap, pragma[only_bind_into](config)) and
|
||||
fwdFlow(node, pragma[only_bind_into](state), _, _, _, ap, pragma[only_bind_into](config)) and
|
||||
localStep(node, pragma[only_bind_into](state), mid, state0, false, _, config, _) and
|
||||
revFlow(mid, state0, toReturn, returnAp, nil, pragma[only_bind_into](config)) and
|
||||
revFlow(mid, state0, returnCtx, returnAp, nil, pragma[only_bind_into](config)) and
|
||||
ap instanceof ApNil
|
||||
)
|
||||
or
|
||||
exists(NodeEx mid |
|
||||
jumpStep(node, mid, config) and
|
||||
revFlow(mid, state, _, _, ap, config) and
|
||||
toReturn = false and
|
||||
returnCtx = TReturnCtxNone() and
|
||||
returnAp = apNone()
|
||||
)
|
||||
or
|
||||
exists(NodeEx mid, ApNil nil |
|
||||
fwdFlow(node, _, _, _, ap, pragma[only_bind_into](config)) and
|
||||
fwdFlow(node, _, _, _, _, ap, pragma[only_bind_into](config)) and
|
||||
additionalJumpStep(node, mid, config) and
|
||||
revFlow(pragma[only_bind_into](mid), state, _, _, nil, pragma[only_bind_into](config)) and
|
||||
toReturn = false and
|
||||
returnCtx = TReturnCtxNone() and
|
||||
returnAp = apNone() and
|
||||
ap instanceof ApNil
|
||||
)
|
||||
or
|
||||
exists(NodeEx mid, FlowState state0, ApNil nil |
|
||||
fwdFlow(node, _, _, _, ap, pragma[only_bind_into](config)) and
|
||||
fwdFlow(node, _, _, _, _, ap, pragma[only_bind_into](config)) and
|
||||
additionalJumpStateStep(node, state, mid, state0, config) and
|
||||
revFlow(pragma[only_bind_into](mid), pragma[only_bind_into](state0), _, _, nil,
|
||||
pragma[only_bind_into](config)) and
|
||||
toReturn = false and
|
||||
returnCtx = TReturnCtxNone() and
|
||||
returnAp = apNone() and
|
||||
ap instanceof ApNil
|
||||
)
|
||||
or
|
||||
// store
|
||||
exists(Ap ap0, Content c |
|
||||
revFlowStore(ap0, c, ap, node, state, _, _, toReturn, returnAp, config) and
|
||||
revFlowStore(ap0, c, ap, node, state, _, _, returnCtx, returnAp, config) and
|
||||
revFlowConsCand(ap0, c, ap, config)
|
||||
)
|
||||
or
|
||||
// read
|
||||
exists(NodeEx mid, Ap ap0 |
|
||||
revFlow(mid, state, toReturn, returnAp, ap0, config) and
|
||||
revFlow(mid, state, returnCtx, returnAp, ap0, config) and
|
||||
readStepFwd(node, ap, _, mid, ap0, config)
|
||||
)
|
||||
or
|
||||
// flow into a callable
|
||||
revFlowInNotToReturn(node, state, returnAp, ap, config) and
|
||||
toReturn = false
|
||||
exists(ParamNodeEx p, boolean allowsFieldFlow |
|
||||
revFlow(p, state, TReturnCtxNone(), returnAp, ap, config) and
|
||||
flowIntoCall(_, node, p, allowsFieldFlow, config) and
|
||||
(if allowsFieldFlow = false then ap instanceof ApNil else any()) and
|
||||
returnCtx = TReturnCtxNone()
|
||||
)
|
||||
or
|
||||
exists(DataFlowCall call, Ap returnAp0 |
|
||||
revFlowInToReturn(call, node, state, returnAp0, ap, config) and
|
||||
revFlowIsReturned(call, toReturn, returnAp, returnAp0, config)
|
||||
// flow through a callable
|
||||
exists(DataFlowCall call, ReturnKindExt returnKind0, Ap returnAp0 |
|
||||
revFlowInToReturn(call, node, state, returnKind0, returnAp0, ap, config) and
|
||||
revFlowIsReturned(call, returnCtx, returnAp, returnKind0, returnAp0, config)
|
||||
)
|
||||
or
|
||||
// flow out of a callable
|
||||
revFlowOut(_, node, state, _, _, ap, config) and
|
||||
toReturn = true and
|
||||
if returnNodeMayFlowThrough(node, state, ap, config)
|
||||
then returnAp = apSome(ap)
|
||||
else returnAp = apNone()
|
||||
exists(ReturnKindExt kind |
|
||||
revFlowOut(_, node, kind, state, _, _, ap, config) and
|
||||
if returnFlowsThrough(node, kind, state, _, _, _, ap, config)
|
||||
then (
|
||||
returnCtx = TReturnCtxMaybeFlowThrough(kind) and
|
||||
returnAp = apSome(ap)
|
||||
) else (
|
||||
returnCtx = TReturnCtxNoFlowThrough() and returnAp = apNone()
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate revFlowStore(
|
||||
Ap ap0, Content c, Ap ap, NodeEx node, FlowState state, TypedContent tc, NodeEx mid,
|
||||
boolean toReturn, ApOption returnAp, Configuration config
|
||||
ReturnCtx returnCtx, ApOption returnAp, Configuration config
|
||||
) {
|
||||
revFlow(mid, state, toReturn, returnAp, ap0, config) and
|
||||
revFlow(mid, state, returnCtx, returnAp, ap0, config) and
|
||||
storeStepFwd(node, ap, tc, mid, ap0, config) and
|
||||
tc.getContent() = c
|
||||
}
|
||||
@@ -1599,35 +1677,27 @@ private module MkStage<StageSig PrevStage> {
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate revFlowOut(
|
||||
DataFlowCall call, RetNodeEx ret, FlowState state, boolean toReturn, ApOption returnAp, Ap ap,
|
||||
Configuration config
|
||||
DataFlowCall call, RetNodeEx ret, ReturnKindExt kind, FlowState state, ReturnCtx returnCtx,
|
||||
ApOption returnAp, Ap ap, Configuration config
|
||||
) {
|
||||
exists(NodeEx out, boolean allowsFieldFlow |
|
||||
revFlow(out, state, toReturn, returnAp, ap, config) and
|
||||
flowOutOfCall(call, ret, out, allowsFieldFlow, config) and
|
||||
if allowsFieldFlow = false then ap instanceof ApNil else any()
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate revFlowInNotToReturn(
|
||||
ArgNodeEx arg, FlowState state, ApOption returnAp, Ap ap, Configuration config
|
||||
) {
|
||||
exists(ParamNodeEx p, boolean allowsFieldFlow |
|
||||
revFlow(p, state, false, returnAp, ap, config) and
|
||||
flowIntoCall(_, arg, p, allowsFieldFlow, config) and
|
||||
revFlow(out, state, returnCtx, returnAp, ap, config) and
|
||||
flowOutOfCall(call, ret, kind, out, allowsFieldFlow, config) and
|
||||
if allowsFieldFlow = false then ap instanceof ApNil else any()
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate revFlowInToReturn(
|
||||
DataFlowCall call, ArgNodeEx arg, FlowState state, Ap returnAp, Ap ap, Configuration config
|
||||
DataFlowCall call, ArgNodeEx arg, FlowState state, ReturnKindExt kind, Ap returnAp, Ap ap,
|
||||
Configuration config
|
||||
) {
|
||||
exists(ParamNodeEx p, boolean allowsFieldFlow |
|
||||
revFlow(p, state, true, apSome(returnAp), ap, config) and
|
||||
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()
|
||||
(if allowsFieldFlow = false then ap instanceof ApNil else any()) and
|
||||
parameterFlowThroughAllowed(p, kind)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1638,11 +1708,12 @@ private module MkStage<StageSig PrevStage> {
|
||||
*/
|
||||
pragma[nomagic]
|
||||
private predicate revFlowIsReturned(
|
||||
DataFlowCall call, boolean toReturn, ApOption returnAp, Ap ap, Configuration config
|
||||
DataFlowCall call, ReturnCtx returnCtx, ApOption returnAp, ReturnKindExt kind, Ap ap,
|
||||
Configuration config
|
||||
) {
|
||||
exists(RetNodeEx ret, FlowState state, CcCall ccc |
|
||||
revFlowOut(call, ret, state, toReturn, returnAp, ap, config) and
|
||||
fwdFlow(ret, state, ccc, apSome(_), ap, config) and
|
||||
revFlowOut(call, ret, kind, state, returnCtx, returnAp, ap, config) and
|
||||
returnFlowsThrough(ret, kind, state, ccc, _, _, ap, config) and
|
||||
matchesCall(ccc, call)
|
||||
)
|
||||
}
|
||||
@@ -1713,40 +1784,39 @@ private module MkStage<StageSig PrevStage> {
|
||||
validAp(ap, config)
|
||||
}
|
||||
|
||||
pragma[noinline]
|
||||
private predicate parameterFlow(
|
||||
ParamNodeEx p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config
|
||||
pragma[nomagic]
|
||||
private predicate parameterFlowsThroughRev(
|
||||
ParamNodeEx p, Ap ap, ReturnKindExt kind, Configuration config
|
||||
) {
|
||||
revFlow(p, _, true, apSome(ap0), ap, config) and
|
||||
c = p.getEnclosingCallable()
|
||||
revFlow(p, _, TReturnCtxMaybeFlowThrough(kind), apSome(_), ap, config) and
|
||||
parameterFlowThroughAllowed(p, kind)
|
||||
}
|
||||
|
||||
predicate parameterMayFlowThrough(ParamNodeEx p, DataFlowCallable c, Ap ap, Configuration config) {
|
||||
exists(RetNodeEx ret, FlowState state, Ap ap0, ReturnKindExt kind, ParameterPosition pos |
|
||||
parameterFlow(p, ap, ap0, c, config) and
|
||||
c = ret.getEnclosingCallable() and
|
||||
revFlow(pragma[only_bind_into](ret), pragma[only_bind_into](state), true, apSome(_),
|
||||
pragma[only_bind_into](ap0), pragma[only_bind_into](config)) and
|
||||
fwdFlow(ret, state, any(CcCall ccc), apSome(ap), ap0, config) and
|
||||
kind = ret.getKind() and
|
||||
p.getPosition() = pos and
|
||||
// we don't expect a parameter to return stored in itself, unless explicitly allowed
|
||||
(
|
||||
not kind.(ParamUpdateReturnKind).getPosition() = pos
|
||||
or
|
||||
p.allowParameterReturnInSelf()
|
||||
)
|
||||
pragma[nomagic]
|
||||
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)
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
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(
|
||||
Ap returnAp0, ArgNodeEx arg, FlowState state, boolean toReturn, ApOption returnAp, Ap ap
|
||||
ReturnKindExt returnKind0, Ap returnAp0, ArgNodeEx arg, FlowState state,
|
||||
ReturnCtx returnCtx, ApOption returnAp, Ap ap
|
||||
|
|
||||
revFlow(arg, state, toReturn, returnAp, ap, config) and
|
||||
revFlowInToReturn(call, arg, state, returnAp0, ap, config) and
|
||||
revFlowIsReturned(call, toReturn, returnAp, returnAp0, config)
|
||||
revFlow(arg, state, returnCtx, returnAp, ap, config) and
|
||||
revFlowInToReturn(call, arg, state, returnKind0, returnAp0, ap, config) and
|
||||
revFlowIsReturned(call, returnCtx, returnAp, returnKind0, returnAp0, config)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1754,14 +1824,13 @@ private module MkStage<StageSig PrevStage> {
|
||||
boolean fwd, int nodes, int fields, int conscand, int states, int tuples, Configuration config
|
||||
) {
|
||||
fwd = true and
|
||||
nodes = count(NodeEx node | fwdFlow(node, _, _, _, _, config)) and
|
||||
nodes = count(NodeEx node | fwdFlow(node, _, _, _, _, _, config)) and
|
||||
fields = count(TypedContent f0 | fwdConsCand(f0, _, config)) and
|
||||
conscand = count(TypedContent f0, Ap ap | fwdConsCand(f0, ap, config)) and
|
||||
states = count(FlowState state | fwdFlow(_, state, _, _, _, config)) and
|
||||
states = count(FlowState state | fwdFlow(_, state, _, _, _, _, config)) and
|
||||
tuples =
|
||||
count(NodeEx n, FlowState state, Cc cc, ApOption argAp, Ap ap |
|
||||
fwdFlow(n, state, cc, argAp, ap, config)
|
||||
)
|
||||
count(NodeEx n, FlowState state, Cc cc, ParameterPositionOption summaryCtx, ApOption argAp,
|
||||
Ap ap | fwdFlow(n, state, cc, summaryCtx, argAp, ap, config))
|
||||
or
|
||||
fwd = false and
|
||||
nodes = count(NodeEx node | revFlow(node, _, _, _, _, config)) and
|
||||
@@ -1769,8 +1838,8 @@ private module MkStage<StageSig PrevStage> {
|
||||
conscand = count(TypedContent f0, Ap ap | consCand(f0, ap, config)) and
|
||||
states = count(FlowState state | revFlow(_, state, _, _, _, config)) and
|
||||
tuples =
|
||||
count(NodeEx n, FlowState state, boolean b, ApOption retAp, Ap ap |
|
||||
revFlow(n, state, b, retAp, ap, config)
|
||||
count(NodeEx n, FlowState state, ReturnCtx returnCtx, ApOption retAp, Ap ap |
|
||||
revFlow(n, state, returnCtx, retAp, ap, config)
|
||||
)
|
||||
}
|
||||
/* End: Stage logic. */
|
||||
@@ -1915,7 +1984,7 @@ private module Stage2Param implements MkStage<Stage1>::StageParam {
|
||||
exists(lcc)
|
||||
}
|
||||
|
||||
predicate flowOutOfCall = flowOutOfCallNodeCand1/5;
|
||||
predicate flowOutOfCall = flowOutOfCallNodeCand1/6;
|
||||
|
||||
predicate flowIntoCall = flowIntoCallNodeCand1/5;
|
||||
|
||||
@@ -1951,9 +2020,10 @@ private module Stage2 implements StageSig {
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate flowOutOfCallNodeCand2(
|
||||
DataFlowCall call, RetNodeEx node1, NodeEx node2, boolean allowsFieldFlow, Configuration config
|
||||
DataFlowCall call, RetNodeEx node1, ReturnKindExt kind, NodeEx node2, boolean allowsFieldFlow,
|
||||
Configuration config
|
||||
) {
|
||||
flowOutOfCallNodeCand1(call, node1, node2, allowsFieldFlow, config) and
|
||||
flowOutOfCallNodeCand1(call, node1, kind, node2, allowsFieldFlow, config) and
|
||||
Stage2::revFlow(node2, pragma[only_bind_into](config)) and
|
||||
Stage2::revFlowAlias(node1, pragma[only_bind_into](config))
|
||||
}
|
||||
@@ -2021,8 +2091,8 @@ private module LocalFlowBigStep {
|
||||
exists(NodeEx next | Stage2::revFlow(next, state, config) |
|
||||
jumpStep(node, next, config) or
|
||||
additionalJumpStep(node, next, config) or
|
||||
flowIntoCallNodeCand1(_, node, next, config) or
|
||||
flowOutOfCallNodeCand1(_, node, next, config) or
|
||||
flowIntoCallNodeCand2(_, node, next, _, config) or
|
||||
flowOutOfCallNodeCand2(_, node, _, next, _, config) or
|
||||
Stage2::storeStepCand(node, _, _, next, _, config) or
|
||||
Stage2::readStepCand(node, _, next, config)
|
||||
)
|
||||
@@ -2163,7 +2233,7 @@ private module Stage3Param implements MkStage<Stage2>::StageParam {
|
||||
localFlowBigStep(node1, state1, node2, state2, preservesValue, ap, config, _) and exists(lcc)
|
||||
}
|
||||
|
||||
predicate flowOutOfCall = flowOutOfCallNodeCand2/5;
|
||||
predicate flowOutOfCall = flowOutOfCallNodeCand2/6;
|
||||
|
||||
predicate flowIntoCall = flowIntoCallNodeCand2/5;
|
||||
|
||||
@@ -2233,8 +2303,9 @@ private predicate flowCandSummaryCtx(
|
||||
NodeEx node, FlowState state, AccessPathFront argApf, Configuration config
|
||||
) {
|
||||
exists(AccessPathFront apf |
|
||||
Stage3::revFlow(node, state, true, _, apf, config) and
|
||||
Stage3::fwdFlow(node, state, any(Stage3::CcCall ccc), TAccessPathFrontSome(argApf), apf, config)
|
||||
Stage3::revFlow(node, state, TReturnCtxMaybeFlowThrough(_), _, apf, config) and
|
||||
Stage3::fwdFlow(node, state, any(Stage3::CcCall ccc), _, TAccessPathFrontSome(argApf), apf,
|
||||
config)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -2468,10 +2539,11 @@ private module Stage4Param implements MkStage<Stage3>::StageParam {
|
||||
|
||||
pragma[nomagic]
|
||||
predicate flowOutOfCall(
|
||||
DataFlowCall call, RetNodeEx node1, NodeEx node2, boolean allowsFieldFlow, Configuration config
|
||||
DataFlowCall call, RetNodeEx node1, ReturnKindExt kind, NodeEx node2, boolean allowsFieldFlow,
|
||||
Configuration config
|
||||
) {
|
||||
exists(FlowState state |
|
||||
flowOutOfCallNodeCand2(call, node1, node2, allowsFieldFlow, config) and
|
||||
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))
|
||||
@@ -2508,13 +2580,14 @@ private Configuration unbindConf(Configuration conf) {
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate nodeMayUseSummary0(
|
||||
NodeEx n, DataFlowCallable c, FlowState state, AccessPathApprox apa, Configuration config
|
||||
NodeEx n, DataFlowCallable c, ParameterPosition pos, FlowState state, AccessPathApprox apa,
|
||||
Configuration config
|
||||
) {
|
||||
exists(AccessPathApprox apa0 |
|
||||
Stage4::parameterMayFlowThrough(_, c, _, _) and
|
||||
Stage4::revFlow(n, state, true, _, apa0, config) and
|
||||
Stage4::fwdFlow(n, state, any(CallContextCall ccc), TAccessPathApproxSome(apa), apa0, config) and
|
||||
n.getEnclosingCallable() = c
|
||||
c = n.getEnclosingCallable() and
|
||||
Stage4::revFlow(n, state, TReturnCtxMaybeFlowThrough(_), _, apa0, config) and
|
||||
Stage4::fwdFlow(n, state, any(CallContextCall ccc), TParameterPositionSome(pos),
|
||||
TAccessPathApproxSome(apa), apa0, config)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -2522,9 +2595,10 @@ pragma[nomagic]
|
||||
private predicate nodeMayUseSummary(
|
||||
NodeEx n, FlowState state, AccessPathApprox apa, Configuration config
|
||||
) {
|
||||
exists(DataFlowCallable c |
|
||||
Stage4::parameterMayFlowThrough(_, c, apa, config) and
|
||||
nodeMayUseSummary0(n, c, state, apa, config)
|
||||
exists(DataFlowCallable c, ParameterPosition pos, ParamNodeEx p |
|
||||
Stage4::parameterMayFlowThrough(p, apa, config) and
|
||||
nodeMayUseSummary0(n, c, pos, state, apa, config) and
|
||||
p.isParameterOf(c, pos)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -2532,7 +2606,7 @@ private newtype TSummaryCtx =
|
||||
TSummaryCtxNone() or
|
||||
TSummaryCtxSome(ParamNodeEx p, FlowState state, AccessPath ap) {
|
||||
exists(Configuration config |
|
||||
Stage4::parameterMayFlowThrough(p, _, ap.getApprox(), config) and
|
||||
Stage4::parameterMayFlowThrough(p, ap.getApprox(), config) and
|
||||
Stage4::revFlow(p, state, _, config)
|
||||
)
|
||||
}
|
||||
@@ -3453,17 +3527,11 @@ private predicate paramFlowsThrough(
|
||||
ReturnKindExt kind, FlowState state, CallContextCall cc, SummaryCtxSome sc, AccessPath ap,
|
||||
AccessPathApprox apa, Configuration config
|
||||
) {
|
||||
exists(PathNodeMid mid, RetNodeEx ret, ParameterPosition pos |
|
||||
exists(PathNodeMid mid, RetNodeEx ret |
|
||||
pathNode(mid, ret, state, cc, sc, ap, config, _) and
|
||||
kind = ret.getKind() and
|
||||
apa = ap.getApprox() and
|
||||
pos = sc.getParameterPos() and
|
||||
// we don't expect a parameter to return stored in itself, unless explicitly allowed
|
||||
(
|
||||
not kind.(ParamUpdateReturnKind).getPosition() = pos
|
||||
or
|
||||
sc.getParamNode().allowParameterReturnInSelf()
|
||||
)
|
||||
parameterFlowThroughAllowed(sc.getParamNode(), kind)
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@@ -1,642 +1,21 @@
|
||||
/**
|
||||
* 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.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
|
||||
private import semmle.code.cpp.ir.dataflow.internal.DefaultTaintTrackingImpl as DefaultTaintTrackingImpl
|
||||
|
||||
/**
|
||||
* 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 predictableOnlyFlow = DefaultTaintTrackingImpl::predictableOnlyFlow/1;
|
||||
|
||||
/**
|
||||
* 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 tainted = DefaultTaintTrackingImpl::tainted/2;
|
||||
|
||||
private DataFlow::Node getNodeForSource(Expr source) {
|
||||
isUserInput(source, _) and
|
||||
result = getNodeForExpr(source)
|
||||
}
|
||||
deprecated predicate taintedIncludingGlobalVars =
|
||||
DefaultTaintTrackingImpl::taintedIncludingGlobalVars/3;
|
||||
|
||||
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 predicate globalVarFromId = DefaultTaintTrackingImpl::globalVarFromId/1;
|
||||
|
||||
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()
|
||||
)
|
||||
}
|
||||
}
|
||||
deprecated module TaintedWithPath = DefaultTaintTrackingImpl::TaintedWithPath;
|
||||
|
||||
@@ -5,7 +5,6 @@
|
||||
*/
|
||||
|
||||
private import cpp
|
||||
import semmle.code.cpp.ir.dataflow.DataFlow
|
||||
private import semmle.code.cpp.ir.IR
|
||||
|
||||
/**
|
||||
@@ -25,18 +24,18 @@ abstract class MustFlowConfiguration extends string {
|
||||
/**
|
||||
* Holds if `source` is a relevant data flow source.
|
||||
*/
|
||||
abstract predicate isSource(DataFlow::Node source);
|
||||
abstract predicate isSource(Instruction source);
|
||||
|
||||
/**
|
||||
* Holds if `sink` is a relevant data flow sink.
|
||||
*/
|
||||
abstract predicate isSink(DataFlow::Node sink);
|
||||
abstract predicate isSink(Operand sink);
|
||||
|
||||
/**
|
||||
* Holds if the additional flow step from `node1` to `node2` must be taken
|
||||
* into account in the analysis.
|
||||
*/
|
||||
predicate isAdditionalFlowStep(DataFlow::Node node1, DataFlow::Node node2) { none() }
|
||||
predicate isAdditionalFlowStep(Operand node1, Instruction node2) { none() }
|
||||
|
||||
/** Holds if this configuration allows flow from arguments to parameters. */
|
||||
predicate allowInterproceduralFlow() { any() }
|
||||
@@ -48,17 +47,17 @@ abstract class MustFlowConfiguration extends string {
|
||||
* included in the module `PathGraph`.
|
||||
*/
|
||||
final predicate hasFlowPath(MustFlowPathNode source, MustFlowPathSink sink) {
|
||||
this.isSource(source.getNode()) and
|
||||
this.isSource(source.getInstruction()) and
|
||||
source.getASuccessor+() = sink
|
||||
}
|
||||
}
|
||||
|
||||
/** Holds if `node` flows from a source. */
|
||||
pragma[nomagic]
|
||||
private predicate flowsFromSource(DataFlow::Node node, MustFlowConfiguration config) {
|
||||
private predicate flowsFromSource(Instruction node, MustFlowConfiguration config) {
|
||||
config.isSource(node)
|
||||
or
|
||||
exists(DataFlow::Node mid |
|
||||
exists(Instruction mid |
|
||||
step(mid, node, config) and
|
||||
flowsFromSource(mid, pragma[only_bind_into](config))
|
||||
)
|
||||
@@ -66,12 +65,12 @@ private predicate flowsFromSource(DataFlow::Node node, MustFlowConfiguration con
|
||||
|
||||
/** Holds if `node` flows to a sink. */
|
||||
pragma[nomagic]
|
||||
private predicate flowsToSink(DataFlow::Node node, MustFlowConfiguration config) {
|
||||
private predicate flowsToSink(Instruction node, MustFlowConfiguration config) {
|
||||
flowsFromSource(node, pragma[only_bind_into](config)) and
|
||||
(
|
||||
config.isSink(node)
|
||||
config.isSink(node.getAUse())
|
||||
or
|
||||
exists(DataFlow::Node mid |
|
||||
exists(Instruction mid |
|
||||
step(node, mid, config) and
|
||||
flowsToSink(mid, pragma[only_bind_into](config))
|
||||
)
|
||||
@@ -198,12 +197,13 @@ private module Cached {
|
||||
}
|
||||
|
||||
cached
|
||||
predicate step(DataFlow::Node nodeFrom, DataFlow::Node nodeTo) {
|
||||
instructionToOperandStep(nodeFrom.asInstruction(), nodeTo.asOperand())
|
||||
predicate step(Instruction nodeFrom, Instruction nodeTo) {
|
||||
exists(Operand mid |
|
||||
instructionToOperandStep(nodeFrom, mid) and
|
||||
operandToInstructionStep(mid, nodeTo)
|
||||
)
|
||||
or
|
||||
flowThroughCallable(nodeFrom.asInstruction(), nodeTo.asInstruction())
|
||||
or
|
||||
operandToInstructionStep(nodeFrom.asOperand(), nodeTo.asInstruction())
|
||||
flowThroughCallable(nodeFrom, nodeTo)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -213,12 +213,12 @@ private module Cached {
|
||||
* way around.
|
||||
*/
|
||||
pragma[inline]
|
||||
private Declaration getEnclosingCallable(DataFlow::Node n) {
|
||||
pragma[only_bind_into](result) = pragma[only_bind_out](n).getEnclosingCallable()
|
||||
private IRFunction getEnclosingCallable(Instruction n) {
|
||||
pragma[only_bind_into](result) = pragma[only_bind_out](n).getEnclosingIRFunction()
|
||||
}
|
||||
|
||||
/** Holds if `nodeFrom` flows to `nodeTo`. */
|
||||
private predicate step(DataFlow::Node nodeFrom, DataFlow::Node nodeTo, MustFlowConfiguration config) {
|
||||
private predicate step(Instruction nodeFrom, Instruction nodeTo, MustFlowConfiguration config) {
|
||||
exists(config) and
|
||||
Cached::step(pragma[only_bind_into](nodeFrom), pragma[only_bind_into](nodeTo)) and
|
||||
(
|
||||
@@ -227,37 +227,37 @@ private predicate step(DataFlow::Node nodeFrom, DataFlow::Node nodeTo, MustFlowC
|
||||
getEnclosingCallable(nodeFrom) = getEnclosingCallable(nodeTo)
|
||||
)
|
||||
or
|
||||
config.isAdditionalFlowStep(nodeFrom, nodeTo)
|
||||
config.isAdditionalFlowStep(nodeFrom.getAUse(), nodeTo)
|
||||
}
|
||||
|
||||
private newtype TLocalPathNode =
|
||||
MkLocalPathNode(DataFlow::Node n, MustFlowConfiguration config) {
|
||||
MkLocalPathNode(Instruction n, MustFlowConfiguration config) {
|
||||
flowsToSink(n, config) and
|
||||
(
|
||||
config.isSource(n)
|
||||
or
|
||||
exists(MustFlowPathNode mid | step(mid.getNode(), n, config))
|
||||
exists(MustFlowPathNode mid | step(mid.getInstruction(), n, config))
|
||||
)
|
||||
}
|
||||
|
||||
/** A `Node` that is in a path from a source to a sink. */
|
||||
class MustFlowPathNode extends TLocalPathNode {
|
||||
DataFlow::Node n;
|
||||
Instruction n;
|
||||
|
||||
MustFlowPathNode() { this = MkLocalPathNode(n, _) }
|
||||
|
||||
/** Gets the underlying node. */
|
||||
DataFlow::Node getNode() { result = n }
|
||||
Instruction getInstruction() { result = n }
|
||||
|
||||
/** Gets a textual representation of this node. */
|
||||
string toString() { result = n.toString() }
|
||||
string toString() { result = n.getAst().toString() }
|
||||
|
||||
/** Gets the location of this element. */
|
||||
Location getLocation() { result = n.getLocation() }
|
||||
|
||||
/** Gets a successor node, if any. */
|
||||
MustFlowPathNode getASuccessor() {
|
||||
step(this.getNode(), result.getNode(), this.getConfiguration())
|
||||
step(this.getInstruction(), result.getInstruction(), this.getConfiguration())
|
||||
}
|
||||
|
||||
/** Gets the associated configuration. */
|
||||
@@ -265,7 +265,7 @@ class MustFlowPathNode extends TLocalPathNode {
|
||||
}
|
||||
|
||||
private class MustFlowPathSink extends MustFlowPathNode {
|
||||
MustFlowPathSink() { this.getConfiguration().isSink(this.getNode()) }
|
||||
MustFlowPathSink() { this.getConfiguration().isSink(this.getInstruction().getAUse()) }
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -70,7 +70,7 @@ abstract class Configuration extends string {
|
||||
/**
|
||||
* Holds if `sink` is a relevant data flow sink accepting `state`.
|
||||
*/
|
||||
predicate isSink(Node source, FlowState state) { none() }
|
||||
predicate isSink(Node sink, FlowState state) { none() }
|
||||
|
||||
/**
|
||||
* Holds if data flow through `node` is prohibited. This completely removes
|
||||
@@ -319,8 +319,6 @@ private class ParamNodeEx extends NodeEx {
|
||||
}
|
||||
|
||||
ParameterPosition getPosition() { this.isParameterOf(_, result) }
|
||||
|
||||
predicate allowParameterReturnInSelf() { allowParameterReturnInSelfCached(this.asNode()) }
|
||||
}
|
||||
|
||||
private class RetNodeEx extends NodeEx {
|
||||
@@ -608,6 +606,38 @@ private predicate hasSinkCallCtx(Configuration config) {
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if flow from `p` to a return node of kind `kind` is allowed.
|
||||
*
|
||||
* We don't expect a parameter to return stored in itself, unless
|
||||
* explicitly allowed
|
||||
*/
|
||||
bindingset[p, kind]
|
||||
private predicate parameterFlowThroughAllowed(ParamNodeEx p, ReturnKindExt kind) {
|
||||
exists(ParameterPosition pos | p.isParameterOf(_, pos) |
|
||||
not kind.(ParamUpdateReturnKind).getPosition() = pos
|
||||
or
|
||||
allowParameterReturnInSelfCached(p.asNode())
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* 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;
|
||||
|
||||
@@ -981,21 +1011,22 @@ private module Stage1 implements StageSig {
|
||||
* candidate for the origin of a summary.
|
||||
*/
|
||||
pragma[nomagic]
|
||||
predicate parameterMayFlowThrough(ParamNodeEx p, DataFlowCallable c, Ap ap, Configuration config) {
|
||||
exists(ReturnKindExt kind |
|
||||
predicate parameterMayFlowThrough(ParamNodeEx p, Ap ap, Configuration config) {
|
||||
exists(DataFlowCallable c, ReturnKindExt kind |
|
||||
throughFlowNodeCand(p, config) and
|
||||
returnFlowCallableNodeCand(c, kind, config) and
|
||||
p.getEnclosingCallable() = c and
|
||||
exists(ap) and
|
||||
// we don't expect a parameter to return stored in itself, unless explicitly allowed
|
||||
(
|
||||
not kind.(ParamUpdateReturnKind).getPosition() = p.getPosition()
|
||||
or
|
||||
p.allowParameterReturnInSelf()
|
||||
)
|
||||
parameterFlowThroughAllowed(p, kind)
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
predicate returnMayFlowThrough(RetNodeEx ret, ReturnKindExt kind, Configuration config) {
|
||||
throughFlowNodeCand(ret, config) and
|
||||
kind = ret.getKind()
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
predicate callMayFlowThroughRev(DataFlowCall call, Configuration config) {
|
||||
exists(ArgNodeEx arg, boolean toReturn |
|
||||
@@ -1052,12 +1083,16 @@ private predicate viableReturnPosOutNodeCand1(
|
||||
*/
|
||||
pragma[nomagic]
|
||||
private predicate flowOutOfCallNodeCand1(
|
||||
DataFlowCall call, RetNodeEx ret, NodeEx out, Configuration config
|
||||
DataFlowCall call, RetNodeEx ret, ReturnKindExt kind, NodeEx out, Configuration config
|
||||
) {
|
||||
viableReturnPosOutNodeCand1(call, ret.getReturnPosition(), out, config) and
|
||||
Stage1::revFlow(ret, config) and
|
||||
not outBarrier(ret, config) and
|
||||
not inBarrier(out, config)
|
||||
exists(ReturnPosition pos |
|
||||
viableReturnPosOutNodeCand1(call, pos, out, config) and
|
||||
pos = ret.getReturnPosition() and
|
||||
kind = pos.getKind() and
|
||||
Stage1::revFlow(ret, config) and
|
||||
not outBarrier(ret, config) and
|
||||
not inBarrier(out, config)
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
@@ -1087,10 +1122,11 @@ private predicate flowIntoCallNodeCand1(
|
||||
* edge in the graph of paths between sources and sinks that ignores call
|
||||
* contexts.
|
||||
*/
|
||||
pragma[nomagic]
|
||||
private int branch(NodeEx n1, Configuration conf) {
|
||||
result =
|
||||
strictcount(NodeEx n |
|
||||
flowOutOfCallNodeCand1(_, n1, n, conf) or flowIntoCallNodeCand1(_, n1, n, conf)
|
||||
flowOutOfCallNodeCand1(_, n1, _, n, conf) or flowIntoCallNodeCand1(_, n1, n, conf)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1099,10 +1135,11 @@ private int branch(NodeEx n1, Configuration conf) {
|
||||
* edge in the graph of paths between sources and sinks that ignores call
|
||||
* contexts.
|
||||
*/
|
||||
pragma[nomagic]
|
||||
private int join(NodeEx n2, Configuration conf) {
|
||||
result =
|
||||
strictcount(NodeEx n |
|
||||
flowOutOfCallNodeCand1(_, n, n2, conf) or flowIntoCallNodeCand1(_, n, n2, conf)
|
||||
flowOutOfCallNodeCand1(_, n, _, n2, conf) or flowIntoCallNodeCand1(_, n, n2, conf)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1115,12 +1152,13 @@ private int join(NodeEx n2, Configuration conf) {
|
||||
*/
|
||||
pragma[nomagic]
|
||||
private predicate flowOutOfCallNodeCand1(
|
||||
DataFlowCall call, RetNodeEx ret, NodeEx out, boolean allowsFieldFlow, Configuration config
|
||||
DataFlowCall call, RetNodeEx ret, ReturnKindExt kind, NodeEx out, boolean allowsFieldFlow,
|
||||
Configuration config
|
||||
) {
|
||||
flowOutOfCallNodeCand1(call, ret, out, config) and
|
||||
flowOutOfCallNodeCand1(call, ret, kind, out, pragma[only_bind_into](config)) and
|
||||
exists(int b, int j |
|
||||
b = branch(ret, config) and
|
||||
j = join(out, config) and
|
||||
b = branch(ret, pragma[only_bind_into](config)) and
|
||||
j = join(out, pragma[only_bind_into](config)) and
|
||||
if b.minimum(j) <= config.fieldFlowBranchLimit()
|
||||
then allowsFieldFlow = true
|
||||
else allowsFieldFlow = false
|
||||
@@ -1136,10 +1174,10 @@ pragma[nomagic]
|
||||
private predicate flowIntoCallNodeCand1(
|
||||
DataFlowCall call, ArgNodeEx arg, ParamNodeEx p, boolean allowsFieldFlow, Configuration config
|
||||
) {
|
||||
flowIntoCallNodeCand1(call, arg, p, config) and
|
||||
flowIntoCallNodeCand1(call, arg, p, pragma[only_bind_into](config)) and
|
||||
exists(int b, int j |
|
||||
b = branch(arg, config) and
|
||||
j = join(p, config) and
|
||||
b = branch(arg, pragma[only_bind_into](config)) and
|
||||
j = join(p, pragma[only_bind_into](config)) and
|
||||
if b.minimum(j) <= config.fieldFlowBranchLimit()
|
||||
then allowsFieldFlow = true
|
||||
else allowsFieldFlow = false
|
||||
@@ -1156,7 +1194,9 @@ private signature module StageSig {
|
||||
|
||||
predicate callMayFlowThroughRev(DataFlowCall call, Configuration config);
|
||||
|
||||
predicate parameterMayFlowThrough(ParamNodeEx p, DataFlowCallable c, Ap ap, Configuration config);
|
||||
predicate parameterMayFlowThrough(ParamNodeEx p, Ap ap, Configuration config);
|
||||
|
||||
predicate returnMayFlowThrough(RetNodeEx ret, ReturnKindExt kind, Configuration config);
|
||||
|
||||
predicate storeStepCand(
|
||||
NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, DataFlowType contentType,
|
||||
@@ -1222,7 +1262,8 @@ private module MkStage<StageSig PrevStage> {
|
||||
);
|
||||
|
||||
predicate flowOutOfCall(
|
||||
DataFlowCall call, RetNodeEx ret, NodeEx out, boolean allowsFieldFlow, Configuration config
|
||||
DataFlowCall call, RetNodeEx ret, ReturnKindExt kind, NodeEx out, boolean allowsFieldFlow,
|
||||
Configuration config
|
||||
);
|
||||
|
||||
predicate flowIntoCall(
|
||||
@@ -1247,14 +1288,14 @@ private module MkStage<StageSig PrevStage> {
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate flowThroughOutOfCall(
|
||||
DataFlowCall call, CcCall ccc, RetNodeEx ret, NodeEx out, boolean allowsFieldFlow,
|
||||
Configuration config
|
||||
DataFlowCall call, DataFlowCallable c, CcCall ccc, RetNodeEx ret, ReturnKindExt kind,
|
||||
NodeEx out, boolean allowsFieldFlow, Configuration config
|
||||
) {
|
||||
flowOutOfCall(call, ret, out, allowsFieldFlow, pragma[only_bind_into](config)) and
|
||||
flowOutOfCall(call, ret, kind, out, allowsFieldFlow, pragma[only_bind_into](config)) and
|
||||
PrevStage::callMayFlowThroughRev(call, pragma[only_bind_into](config)) and
|
||||
PrevStage::parameterMayFlowThrough(_, ret.getEnclosingCallable(), _,
|
||||
pragma[only_bind_into](config)) and
|
||||
matchesCall(ccc, call)
|
||||
PrevStage::returnMayFlowThrough(ret, kind, pragma[only_bind_into](config)) and
|
||||
matchesCall(ccc, call) and
|
||||
c = ret.getEnclosingCallable()
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1262,29 +1303,32 @@ private module MkStage<StageSig PrevStage> {
|
||||
* configuration `config`.
|
||||
*
|
||||
* The call context `cc` records whether the node is reached through an
|
||||
* argument in a call, and if so, `argAp` records the access path of that
|
||||
* argument.
|
||||
* argument in a call, and if so, `summaryCtx` and `argAp` record the
|
||||
* corresponding parameter position and access path of that argument, respectively.
|
||||
*/
|
||||
pragma[nomagic]
|
||||
additional predicate fwdFlow(
|
||||
NodeEx node, FlowState state, Cc cc, ApOption argAp, Ap ap, Configuration config
|
||||
NodeEx node, FlowState state, Cc cc, ParameterPositionOption summaryCtx, ApOption argAp,
|
||||
Ap ap, Configuration config
|
||||
) {
|
||||
fwdFlow0(node, state, cc, argAp, ap, config) and
|
||||
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, ApOption argAp, Ap ap, Configuration config
|
||||
NodeEx node, FlowState state, Cc cc, ParameterPositionOption summaryCtx, ApOption argAp,
|
||||
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)
|
||||
or
|
||||
exists(NodeEx mid, FlowState state0, Ap ap0, LocalCc localCc |
|
||||
fwdFlow(mid, state0, cc, argAp, ap0, config) and
|
||||
fwdFlow(mid, state0, cc, summaryCtx, argAp, ap0, config) and
|
||||
localCc = getLocalCc(mid, cc)
|
||||
|
|
||||
localStep(mid, state0, node, state, true, _, config, localCc) and
|
||||
@@ -1295,65 +1339,82 @@ private module MkStage<StageSig PrevStage> {
|
||||
)
|
||||
or
|
||||
exists(NodeEx mid |
|
||||
fwdFlow(mid, pragma[only_bind_into](state), _, _, ap, 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
|
||||
argAp = apNone()
|
||||
)
|
||||
or
|
||||
exists(NodeEx mid, ApNil nil |
|
||||
fwdFlow(mid, state, _, _, nil, pragma[only_bind_into](config)) and
|
||||
fwdFlow(mid, state, _, _, _, nil, pragma[only_bind_into](config)) and
|
||||
additionalJumpStep(mid, node, config) and
|
||||
cc = ccNone() and
|
||||
summaryCtx = TParameterPositionNone() and
|
||||
argAp = apNone() and
|
||||
ap = getApNil(node)
|
||||
)
|
||||
or
|
||||
exists(NodeEx mid, FlowState state0, ApNil nil |
|
||||
fwdFlow(mid, state0, _, _, nil, pragma[only_bind_into](config)) and
|
||||
fwdFlow(mid, state0, _, _, _, nil, pragma[only_bind_into](config)) and
|
||||
additionalJumpStateStep(mid, state0, node, state, config) and
|
||||
cc = ccNone() and
|
||||
summaryCtx = TParameterPositionNone() and
|
||||
argAp = apNone() and
|
||||
ap = getApNil(node)
|
||||
)
|
||||
or
|
||||
// store
|
||||
exists(TypedContent tc, Ap ap0 |
|
||||
fwdFlowStore(_, ap0, tc, node, state, cc, argAp, config) and
|
||||
fwdFlowStore(_, ap0, tc, node, state, cc, summaryCtx, argAp, config) and
|
||||
ap = apCons(tc, ap0)
|
||||
)
|
||||
or
|
||||
// read
|
||||
exists(Ap ap0, Content c |
|
||||
fwdFlowRead(ap0, c, _, node, state, cc, argAp, config) and
|
||||
fwdFlowRead(ap0, c, _, node, state, cc, summaryCtx, argAp, config) and
|
||||
fwdFlowConsCand(ap0, c, ap, config)
|
||||
)
|
||||
or
|
||||
// flow into a callable
|
||||
exists(ApApprox apa |
|
||||
fwdFlowIn(_, node, state, _, cc, _, ap, config) and
|
||||
fwdFlowIn(_, node, state, _, cc, _, _, ap, config) and
|
||||
apa = getApprox(ap) and
|
||||
if PrevStage::parameterMayFlowThrough(node, _, apa, config)
|
||||
then argAp = apSome(ap)
|
||||
else argAp = apNone()
|
||||
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
|
||||
fwdFlowOutNotFromArg(node, state, cc, argAp, ap, config)
|
||||
exists(
|
||||
DataFlowCall call, RetNodeEx ret, boolean allowsFieldFlow, CcNoCall innercc,
|
||||
DataFlowCallable inner
|
||||
|
|
||||
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()
|
||||
)
|
||||
or
|
||||
exists(DataFlowCall call, Ap argAp0 |
|
||||
fwdFlowOutFromArg(call, node, state, argAp0, ap, config) and
|
||||
fwdFlowIsEntered(call, cc, argAp, argAp0, config)
|
||||
// flow through a callable
|
||||
exists(DataFlowCall call, ParameterPosition summaryCtx0, Ap argAp0 |
|
||||
fwdFlowOutFromArg(call, node, state, summaryCtx0, argAp0, ap, config) and
|
||||
fwdFlowIsEntered(call, cc, summaryCtx, argAp, summaryCtx0, argAp0, config)
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate fwdFlowStore(
|
||||
NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, FlowState state, Cc cc, ApOption argAp,
|
||||
Configuration config
|
||||
NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, FlowState state, Cc cc,
|
||||
ParameterPositionOption summaryCtx, ApOption argAp, Configuration config
|
||||
) {
|
||||
exists(DataFlowType contentType |
|
||||
fwdFlow(node1, state, cc, argAp, ap1, config) and
|
||||
fwdFlow(node1, state, cc, summaryCtx, argAp, ap1, config) and
|
||||
PrevStage::storeStepCand(node1, unbindApa(getApprox(ap1)), tc, node2, contentType, config) and
|
||||
typecheckStore(ap1, contentType)
|
||||
)
|
||||
@@ -1366,7 +1427,7 @@ private module MkStage<StageSig PrevStage> {
|
||||
pragma[nomagic]
|
||||
private predicate fwdFlowConsCand(Ap cons, Content c, Ap tail, Configuration config) {
|
||||
exists(TypedContent tc |
|
||||
fwdFlowStore(_, tail, tc, _, _, _, _, config) and
|
||||
fwdFlowStore(_, tail, tc, _, _, _, _, _, config) and
|
||||
tc.getContent() = c and
|
||||
cons = apCons(tc, tail)
|
||||
)
|
||||
@@ -1374,21 +1435,21 @@ private module MkStage<StageSig PrevStage> {
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate fwdFlowRead(
|
||||
Ap ap, Content c, NodeEx node1, NodeEx node2, FlowState state, Cc cc, ApOption argAp,
|
||||
Configuration config
|
||||
Ap ap, Content c, NodeEx node1, NodeEx node2, FlowState state, Cc cc,
|
||||
ParameterPositionOption summaryCtx, ApOption argAp, Configuration config
|
||||
) {
|
||||
fwdFlow(node1, state, cc, 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, Cc innercc, ApOption argAp,
|
||||
Ap ap, 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, argAp, ap, 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()
|
||||
@@ -1396,29 +1457,19 @@ private module MkStage<StageSig PrevStage> {
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate fwdFlowOutNotFromArg(
|
||||
NodeEx out, FlowState state, Cc ccOut, ApOption argAp, Ap ap, Configuration config
|
||||
private predicate fwdFlowOutFromArg(
|
||||
DataFlowCall call, NodeEx out, FlowState state, ParameterPosition summaryCtx, Ap argAp, Ap ap,
|
||||
Configuration config
|
||||
) {
|
||||
exists(
|
||||
DataFlowCall call, RetNodeEx ret, boolean allowsFieldFlow, CcNoCall innercc,
|
||||
DataFlowCallable inner
|
||||
DataFlowCallable c, RetNodeEx ret, ReturnKindExt kind, boolean allowsFieldFlow, CcCall ccc
|
||||
|
|
||||
fwdFlow(ret, state, innercc, argAp, ap, config) and
|
||||
flowOutOfCall(call, ret, out, allowsFieldFlow, config) and
|
||||
inner = ret.getEnclosingCallable() and
|
||||
ccOut = getCallContextReturn(inner, call, innercc) and
|
||||
if allowsFieldFlow = false then ap instanceof ApNil else any()
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate fwdFlowOutFromArg(
|
||||
DataFlowCall call, NodeEx out, FlowState state, Ap argAp, Ap ap, Configuration config
|
||||
) {
|
||||
exists(RetNodeEx ret, boolean allowsFieldFlow, CcCall ccc |
|
||||
fwdFlow(ret, state, ccc, apSome(argAp), ap, config) and
|
||||
flowThroughOutOfCall(call, ccc, ret, out, allowsFieldFlow, config) and
|
||||
if allowsFieldFlow = false then ap instanceof ApNil else any()
|
||||
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
|
||||
(if allowsFieldFlow = false then ap instanceof ApNil else any()) and
|
||||
parameterFlowThroughAllowed(c, pragma[only_bind_into](summaryCtx), kind)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1428,11 +1479,13 @@ private module MkStage<StageSig PrevStage> {
|
||||
*/
|
||||
pragma[nomagic]
|
||||
private predicate fwdFlowIsEntered(
|
||||
DataFlowCall call, Cc cc, ApOption argAp, Ap ap, Configuration config
|
||||
DataFlowCall call, Cc cc, ParameterPositionOption summaryCtx, ApOption argAp,
|
||||
ParameterPosition pos, Ap ap, Configuration config
|
||||
) {
|
||||
exists(ParamNodeEx p |
|
||||
fwdFlowIn(call, p, _, cc, _, argAp, ap, config) and
|
||||
PrevStage::parameterMayFlowThrough(p, _, unbindApa(getApprox(ap)), config)
|
||||
exists(ParamNodeEx param |
|
||||
fwdFlowIn(call, param, _, cc, _, summaryCtx, argAp, ap, config) and
|
||||
PrevStage::parameterMayFlowThrough(param, unbindApa(getApprox(ap)), config) and
|
||||
pos = param.getPosition()
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1440,27 +1493,40 @@ private module MkStage<StageSig PrevStage> {
|
||||
private predicate storeStepFwd(
|
||||
NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, Ap ap2, Configuration config
|
||||
) {
|
||||
fwdFlowStore(node1, ap1, tc, node2, _, _, _, config) and
|
||||
fwdFlowStore(node1, ap1, tc, node2, _, _, _, _, config) and
|
||||
ap2 = apCons(tc, ap1) and
|
||||
fwdFlowRead(ap2, tc.getContent(), _, _, _, _, _, config)
|
||||
fwdFlowRead(ap2, tc.getContent(), _, _, _, _, _, _, config)
|
||||
}
|
||||
|
||||
private predicate readStepFwd(
|
||||
NodeEx n1, Ap ap1, Content c, NodeEx n2, Ap ap2, Configuration config
|
||||
) {
|
||||
fwdFlowRead(ap1, c, n1, n2, _, _, _, config) and
|
||||
fwdFlowRead(ap1, c, n1, n2, _, _, _, _, config) and
|
||||
fwdFlowConsCand(ap1, c, ap2, config)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate callMayFlowThroughFwd(DataFlowCall call, Configuration config) {
|
||||
exists(Ap argAp0, NodeEx out, FlowState state, Cc cc, ApOption argAp, Ap ap |
|
||||
fwdFlow(out, state, pragma[only_bind_into](cc), pragma[only_bind_into](argAp), ap,
|
||||
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
|
||||
fwdFlowOutFromArg(call, out, state, argAp0, ap, config) and
|
||||
fwdFlowIsEntered(pragma[only_bind_into](call), pragma[only_bind_into](cc),
|
||||
pragma[only_bind_into](argAp), pragma[only_bind_into](argAp0),
|
||||
pragma[only_bind_into](config))
|
||||
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(DataFlowCallable c, ParameterPosition ppos |
|
||||
returnFlowsThrough0(ret, kind, state, ccc, c, ppos, argAp, ap, config) and
|
||||
p.isParameterOf(c, ppos) and
|
||||
parameterFlowThroughAllowed(p, kind)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1468,118 +1534,130 @@ private module MkStage<StageSig PrevStage> {
|
||||
private predicate flowThroughIntoCall(
|
||||
DataFlowCall call, ArgNodeEx arg, ParamNodeEx p, boolean allowsFieldFlow, Configuration config
|
||||
) {
|
||||
flowIntoCall(call, arg, p, allowsFieldFlow, config) and
|
||||
fwdFlow(arg, _, _, _, _, pragma[only_bind_into](config)) and
|
||||
PrevStage::parameterMayFlowThrough(p, _, _, pragma[only_bind_into](config)) and
|
||||
callMayFlowThroughFwd(call, pragma[only_bind_into](config))
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate returnNodeMayFlowThrough(
|
||||
RetNodeEx ret, FlowState state, Ap ap, Configuration config
|
||||
) {
|
||||
fwdFlow(ret, state, any(CcCall ccc), apSome(_), ap, config)
|
||||
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))
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `node` with access path `ap` is part of a path from a source to a
|
||||
* sink in the configuration `config`.
|
||||
*
|
||||
* The Boolean `toReturn` records whether the node must be returned from the
|
||||
* enclosing callable in order to reach a sink, and if so, `returnAp` records
|
||||
* the access path of the returned value.
|
||||
* The parameter `returnCtx` records whether (and how) the node must be returned
|
||||
* from the enclosing callable in order to reach a sink, and if so, `returnAp`
|
||||
* records the access path of the returned value.
|
||||
*/
|
||||
pragma[nomagic]
|
||||
additional predicate revFlow(
|
||||
NodeEx node, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, Configuration config
|
||||
NodeEx node, FlowState state, ReturnCtx returnCtx, ApOption returnAp, Ap ap,
|
||||
Configuration config
|
||||
) {
|
||||
revFlow0(node, state, toReturn, returnAp, ap, config) and
|
||||
fwdFlow(node, state, _, _, ap, config)
|
||||
revFlow0(node, state, returnCtx, returnAp, ap, config) and
|
||||
fwdFlow(node, state, _, _, _, ap, config)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate revFlow0(
|
||||
NodeEx node, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, Configuration config
|
||||
NodeEx node, FlowState state, ReturnCtx returnCtx, ApOption returnAp, Ap ap,
|
||||
Configuration config
|
||||
) {
|
||||
fwdFlow(node, state, _, _, ap, config) and
|
||||
fwdFlow(node, state, _, _, _, ap, config) and
|
||||
sinkNode(node, state, config) and
|
||||
(if hasSinkCallCtx(config) then toReturn = true else toReturn = false) and
|
||||
(
|
||||
if hasSinkCallCtx(config)
|
||||
then returnCtx = TReturnCtxNoFlowThrough()
|
||||
else returnCtx = TReturnCtxNone()
|
||||
) and
|
||||
returnAp = apNone() and
|
||||
ap instanceof ApNil
|
||||
or
|
||||
exists(NodeEx mid, FlowState state0 |
|
||||
localStep(node, state, mid, state0, true, _, config, _) and
|
||||
revFlow(mid, state0, toReturn, returnAp, ap, config)
|
||||
revFlow(mid, state0, returnCtx, returnAp, ap, config)
|
||||
)
|
||||
or
|
||||
exists(NodeEx mid, FlowState state0, ApNil nil |
|
||||
fwdFlow(node, pragma[only_bind_into](state), _, _, ap, pragma[only_bind_into](config)) and
|
||||
fwdFlow(node, pragma[only_bind_into](state), _, _, _, ap, pragma[only_bind_into](config)) and
|
||||
localStep(node, pragma[only_bind_into](state), mid, state0, false, _, config, _) and
|
||||
revFlow(mid, state0, toReturn, returnAp, nil, pragma[only_bind_into](config)) and
|
||||
revFlow(mid, state0, returnCtx, returnAp, nil, pragma[only_bind_into](config)) and
|
||||
ap instanceof ApNil
|
||||
)
|
||||
or
|
||||
exists(NodeEx mid |
|
||||
jumpStep(node, mid, config) and
|
||||
revFlow(mid, state, _, _, ap, config) and
|
||||
toReturn = false and
|
||||
returnCtx = TReturnCtxNone() and
|
||||
returnAp = apNone()
|
||||
)
|
||||
or
|
||||
exists(NodeEx mid, ApNil nil |
|
||||
fwdFlow(node, _, _, _, ap, pragma[only_bind_into](config)) and
|
||||
fwdFlow(node, _, _, _, _, ap, pragma[only_bind_into](config)) and
|
||||
additionalJumpStep(node, mid, config) and
|
||||
revFlow(pragma[only_bind_into](mid), state, _, _, nil, pragma[only_bind_into](config)) and
|
||||
toReturn = false and
|
||||
returnCtx = TReturnCtxNone() and
|
||||
returnAp = apNone() and
|
||||
ap instanceof ApNil
|
||||
)
|
||||
or
|
||||
exists(NodeEx mid, FlowState state0, ApNil nil |
|
||||
fwdFlow(node, _, _, _, ap, pragma[only_bind_into](config)) and
|
||||
fwdFlow(node, _, _, _, _, ap, pragma[only_bind_into](config)) and
|
||||
additionalJumpStateStep(node, state, mid, state0, config) and
|
||||
revFlow(pragma[only_bind_into](mid), pragma[only_bind_into](state0), _, _, nil,
|
||||
pragma[only_bind_into](config)) and
|
||||
toReturn = false and
|
||||
returnCtx = TReturnCtxNone() and
|
||||
returnAp = apNone() and
|
||||
ap instanceof ApNil
|
||||
)
|
||||
or
|
||||
// store
|
||||
exists(Ap ap0, Content c |
|
||||
revFlowStore(ap0, c, ap, node, state, _, _, toReturn, returnAp, config) and
|
||||
revFlowStore(ap0, c, ap, node, state, _, _, returnCtx, returnAp, config) and
|
||||
revFlowConsCand(ap0, c, ap, config)
|
||||
)
|
||||
or
|
||||
// read
|
||||
exists(NodeEx mid, Ap ap0 |
|
||||
revFlow(mid, state, toReturn, returnAp, ap0, config) and
|
||||
revFlow(mid, state, returnCtx, returnAp, ap0, config) and
|
||||
readStepFwd(node, ap, _, mid, ap0, config)
|
||||
)
|
||||
or
|
||||
// flow into a callable
|
||||
revFlowInNotToReturn(node, state, returnAp, ap, config) and
|
||||
toReturn = false
|
||||
exists(ParamNodeEx p, boolean allowsFieldFlow |
|
||||
revFlow(p, state, TReturnCtxNone(), returnAp, ap, config) and
|
||||
flowIntoCall(_, node, p, allowsFieldFlow, config) and
|
||||
(if allowsFieldFlow = false then ap instanceof ApNil else any()) and
|
||||
returnCtx = TReturnCtxNone()
|
||||
)
|
||||
or
|
||||
exists(DataFlowCall call, Ap returnAp0 |
|
||||
revFlowInToReturn(call, node, state, returnAp0, ap, config) and
|
||||
revFlowIsReturned(call, toReturn, returnAp, returnAp0, config)
|
||||
// flow through a callable
|
||||
exists(DataFlowCall call, ReturnKindExt returnKind0, Ap returnAp0 |
|
||||
revFlowInToReturn(call, node, state, returnKind0, returnAp0, ap, config) and
|
||||
revFlowIsReturned(call, returnCtx, returnAp, returnKind0, returnAp0, config)
|
||||
)
|
||||
or
|
||||
// flow out of a callable
|
||||
revFlowOut(_, node, state, _, _, ap, config) and
|
||||
toReturn = true and
|
||||
if returnNodeMayFlowThrough(node, state, ap, config)
|
||||
then returnAp = apSome(ap)
|
||||
else returnAp = apNone()
|
||||
exists(ReturnKindExt kind |
|
||||
revFlowOut(_, node, kind, state, _, _, ap, config) and
|
||||
if returnFlowsThrough(node, kind, state, _, _, _, ap, config)
|
||||
then (
|
||||
returnCtx = TReturnCtxMaybeFlowThrough(kind) and
|
||||
returnAp = apSome(ap)
|
||||
) else (
|
||||
returnCtx = TReturnCtxNoFlowThrough() and returnAp = apNone()
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate revFlowStore(
|
||||
Ap ap0, Content c, Ap ap, NodeEx node, FlowState state, TypedContent tc, NodeEx mid,
|
||||
boolean toReturn, ApOption returnAp, Configuration config
|
||||
ReturnCtx returnCtx, ApOption returnAp, Configuration config
|
||||
) {
|
||||
revFlow(mid, state, toReturn, returnAp, ap0, config) and
|
||||
revFlow(mid, state, returnCtx, returnAp, ap0, config) and
|
||||
storeStepFwd(node, ap, tc, mid, ap0, config) and
|
||||
tc.getContent() = c
|
||||
}
|
||||
@@ -1599,35 +1677,27 @@ private module MkStage<StageSig PrevStage> {
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate revFlowOut(
|
||||
DataFlowCall call, RetNodeEx ret, FlowState state, boolean toReturn, ApOption returnAp, Ap ap,
|
||||
Configuration config
|
||||
DataFlowCall call, RetNodeEx ret, ReturnKindExt kind, FlowState state, ReturnCtx returnCtx,
|
||||
ApOption returnAp, Ap ap, Configuration config
|
||||
) {
|
||||
exists(NodeEx out, boolean allowsFieldFlow |
|
||||
revFlow(out, state, toReturn, returnAp, ap, config) and
|
||||
flowOutOfCall(call, ret, out, allowsFieldFlow, config) and
|
||||
if allowsFieldFlow = false then ap instanceof ApNil else any()
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate revFlowInNotToReturn(
|
||||
ArgNodeEx arg, FlowState state, ApOption returnAp, Ap ap, Configuration config
|
||||
) {
|
||||
exists(ParamNodeEx p, boolean allowsFieldFlow |
|
||||
revFlow(p, state, false, returnAp, ap, config) and
|
||||
flowIntoCall(_, arg, p, allowsFieldFlow, config) and
|
||||
revFlow(out, state, returnCtx, returnAp, ap, config) and
|
||||
flowOutOfCall(call, ret, kind, out, allowsFieldFlow, config) and
|
||||
if allowsFieldFlow = false then ap instanceof ApNil else any()
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate revFlowInToReturn(
|
||||
DataFlowCall call, ArgNodeEx arg, FlowState state, Ap returnAp, Ap ap, Configuration config
|
||||
DataFlowCall call, ArgNodeEx arg, FlowState state, ReturnKindExt kind, Ap returnAp, Ap ap,
|
||||
Configuration config
|
||||
) {
|
||||
exists(ParamNodeEx p, boolean allowsFieldFlow |
|
||||
revFlow(p, state, true, apSome(returnAp), ap, config) and
|
||||
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()
|
||||
(if allowsFieldFlow = false then ap instanceof ApNil else any()) and
|
||||
parameterFlowThroughAllowed(p, kind)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1638,11 +1708,12 @@ private module MkStage<StageSig PrevStage> {
|
||||
*/
|
||||
pragma[nomagic]
|
||||
private predicate revFlowIsReturned(
|
||||
DataFlowCall call, boolean toReturn, ApOption returnAp, Ap ap, Configuration config
|
||||
DataFlowCall call, ReturnCtx returnCtx, ApOption returnAp, ReturnKindExt kind, Ap ap,
|
||||
Configuration config
|
||||
) {
|
||||
exists(RetNodeEx ret, FlowState state, CcCall ccc |
|
||||
revFlowOut(call, ret, state, toReturn, returnAp, ap, config) and
|
||||
fwdFlow(ret, state, ccc, apSome(_), ap, config) and
|
||||
revFlowOut(call, ret, kind, state, returnCtx, returnAp, ap, config) and
|
||||
returnFlowsThrough(ret, kind, state, ccc, _, _, ap, config) and
|
||||
matchesCall(ccc, call)
|
||||
)
|
||||
}
|
||||
@@ -1713,40 +1784,39 @@ private module MkStage<StageSig PrevStage> {
|
||||
validAp(ap, config)
|
||||
}
|
||||
|
||||
pragma[noinline]
|
||||
private predicate parameterFlow(
|
||||
ParamNodeEx p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config
|
||||
pragma[nomagic]
|
||||
private predicate parameterFlowsThroughRev(
|
||||
ParamNodeEx p, Ap ap, ReturnKindExt kind, Configuration config
|
||||
) {
|
||||
revFlow(p, _, true, apSome(ap0), ap, config) and
|
||||
c = p.getEnclosingCallable()
|
||||
revFlow(p, _, TReturnCtxMaybeFlowThrough(kind), apSome(_), ap, config) and
|
||||
parameterFlowThroughAllowed(p, kind)
|
||||
}
|
||||
|
||||
predicate parameterMayFlowThrough(ParamNodeEx p, DataFlowCallable c, Ap ap, Configuration config) {
|
||||
exists(RetNodeEx ret, FlowState state, Ap ap0, ReturnKindExt kind, ParameterPosition pos |
|
||||
parameterFlow(p, ap, ap0, c, config) and
|
||||
c = ret.getEnclosingCallable() and
|
||||
revFlow(pragma[only_bind_into](ret), pragma[only_bind_into](state), true, apSome(_),
|
||||
pragma[only_bind_into](ap0), pragma[only_bind_into](config)) and
|
||||
fwdFlow(ret, state, any(CcCall ccc), apSome(ap), ap0, config) and
|
||||
kind = ret.getKind() and
|
||||
p.getPosition() = pos and
|
||||
// we don't expect a parameter to return stored in itself, unless explicitly allowed
|
||||
(
|
||||
not kind.(ParamUpdateReturnKind).getPosition() = pos
|
||||
or
|
||||
p.allowParameterReturnInSelf()
|
||||
)
|
||||
pragma[nomagic]
|
||||
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)
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
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(
|
||||
Ap returnAp0, ArgNodeEx arg, FlowState state, boolean toReturn, ApOption returnAp, Ap ap
|
||||
ReturnKindExt returnKind0, Ap returnAp0, ArgNodeEx arg, FlowState state,
|
||||
ReturnCtx returnCtx, ApOption returnAp, Ap ap
|
||||
|
|
||||
revFlow(arg, state, toReturn, returnAp, ap, config) and
|
||||
revFlowInToReturn(call, arg, state, returnAp0, ap, config) and
|
||||
revFlowIsReturned(call, toReturn, returnAp, returnAp0, config)
|
||||
revFlow(arg, state, returnCtx, returnAp, ap, config) and
|
||||
revFlowInToReturn(call, arg, state, returnKind0, returnAp0, ap, config) and
|
||||
revFlowIsReturned(call, returnCtx, returnAp, returnKind0, returnAp0, config)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1754,14 +1824,13 @@ private module MkStage<StageSig PrevStage> {
|
||||
boolean fwd, int nodes, int fields, int conscand, int states, int tuples, Configuration config
|
||||
) {
|
||||
fwd = true and
|
||||
nodes = count(NodeEx node | fwdFlow(node, _, _, _, _, config)) and
|
||||
nodes = count(NodeEx node | fwdFlow(node, _, _, _, _, _, config)) and
|
||||
fields = count(TypedContent f0 | fwdConsCand(f0, _, config)) and
|
||||
conscand = count(TypedContent f0, Ap ap | fwdConsCand(f0, ap, config)) and
|
||||
states = count(FlowState state | fwdFlow(_, state, _, _, _, config)) and
|
||||
states = count(FlowState state | fwdFlow(_, state, _, _, _, _, config)) and
|
||||
tuples =
|
||||
count(NodeEx n, FlowState state, Cc cc, ApOption argAp, Ap ap |
|
||||
fwdFlow(n, state, cc, argAp, ap, config)
|
||||
)
|
||||
count(NodeEx n, FlowState state, Cc cc, ParameterPositionOption summaryCtx, ApOption argAp,
|
||||
Ap ap | fwdFlow(n, state, cc, summaryCtx, argAp, ap, config))
|
||||
or
|
||||
fwd = false and
|
||||
nodes = count(NodeEx node | revFlow(node, _, _, _, _, config)) and
|
||||
@@ -1769,8 +1838,8 @@ private module MkStage<StageSig PrevStage> {
|
||||
conscand = count(TypedContent f0, Ap ap | consCand(f0, ap, config)) and
|
||||
states = count(FlowState state | revFlow(_, state, _, _, _, config)) and
|
||||
tuples =
|
||||
count(NodeEx n, FlowState state, boolean b, ApOption retAp, Ap ap |
|
||||
revFlow(n, state, b, retAp, ap, config)
|
||||
count(NodeEx n, FlowState state, ReturnCtx returnCtx, ApOption retAp, Ap ap |
|
||||
revFlow(n, state, returnCtx, retAp, ap, config)
|
||||
)
|
||||
}
|
||||
/* End: Stage logic. */
|
||||
@@ -1915,7 +1984,7 @@ private module Stage2Param implements MkStage<Stage1>::StageParam {
|
||||
exists(lcc)
|
||||
}
|
||||
|
||||
predicate flowOutOfCall = flowOutOfCallNodeCand1/5;
|
||||
predicate flowOutOfCall = flowOutOfCallNodeCand1/6;
|
||||
|
||||
predicate flowIntoCall = flowIntoCallNodeCand1/5;
|
||||
|
||||
@@ -1951,9 +2020,10 @@ private module Stage2 implements StageSig {
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate flowOutOfCallNodeCand2(
|
||||
DataFlowCall call, RetNodeEx node1, NodeEx node2, boolean allowsFieldFlow, Configuration config
|
||||
DataFlowCall call, RetNodeEx node1, ReturnKindExt kind, NodeEx node2, boolean allowsFieldFlow,
|
||||
Configuration config
|
||||
) {
|
||||
flowOutOfCallNodeCand1(call, node1, node2, allowsFieldFlow, config) and
|
||||
flowOutOfCallNodeCand1(call, node1, kind, node2, allowsFieldFlow, config) and
|
||||
Stage2::revFlow(node2, pragma[only_bind_into](config)) and
|
||||
Stage2::revFlowAlias(node1, pragma[only_bind_into](config))
|
||||
}
|
||||
@@ -2021,8 +2091,8 @@ private module LocalFlowBigStep {
|
||||
exists(NodeEx next | Stage2::revFlow(next, state, config) |
|
||||
jumpStep(node, next, config) or
|
||||
additionalJumpStep(node, next, config) or
|
||||
flowIntoCallNodeCand1(_, node, next, config) or
|
||||
flowOutOfCallNodeCand1(_, node, next, config) or
|
||||
flowIntoCallNodeCand2(_, node, next, _, config) or
|
||||
flowOutOfCallNodeCand2(_, node, _, next, _, config) or
|
||||
Stage2::storeStepCand(node, _, _, next, _, config) or
|
||||
Stage2::readStepCand(node, _, next, config)
|
||||
)
|
||||
@@ -2163,7 +2233,7 @@ private module Stage3Param implements MkStage<Stage2>::StageParam {
|
||||
localFlowBigStep(node1, state1, node2, state2, preservesValue, ap, config, _) and exists(lcc)
|
||||
}
|
||||
|
||||
predicate flowOutOfCall = flowOutOfCallNodeCand2/5;
|
||||
predicate flowOutOfCall = flowOutOfCallNodeCand2/6;
|
||||
|
||||
predicate flowIntoCall = flowIntoCallNodeCand2/5;
|
||||
|
||||
@@ -2233,8 +2303,9 @@ private predicate flowCandSummaryCtx(
|
||||
NodeEx node, FlowState state, AccessPathFront argApf, Configuration config
|
||||
) {
|
||||
exists(AccessPathFront apf |
|
||||
Stage3::revFlow(node, state, true, _, apf, config) and
|
||||
Stage3::fwdFlow(node, state, any(Stage3::CcCall ccc), TAccessPathFrontSome(argApf), apf, config)
|
||||
Stage3::revFlow(node, state, TReturnCtxMaybeFlowThrough(_), _, apf, config) and
|
||||
Stage3::fwdFlow(node, state, any(Stage3::CcCall ccc), _, TAccessPathFrontSome(argApf), apf,
|
||||
config)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -2468,10 +2539,11 @@ private module Stage4Param implements MkStage<Stage3>::StageParam {
|
||||
|
||||
pragma[nomagic]
|
||||
predicate flowOutOfCall(
|
||||
DataFlowCall call, RetNodeEx node1, NodeEx node2, boolean allowsFieldFlow, Configuration config
|
||||
DataFlowCall call, RetNodeEx node1, ReturnKindExt kind, NodeEx node2, boolean allowsFieldFlow,
|
||||
Configuration config
|
||||
) {
|
||||
exists(FlowState state |
|
||||
flowOutOfCallNodeCand2(call, node1, node2, allowsFieldFlow, config) and
|
||||
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))
|
||||
@@ -2508,13 +2580,14 @@ private Configuration unbindConf(Configuration conf) {
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate nodeMayUseSummary0(
|
||||
NodeEx n, DataFlowCallable c, FlowState state, AccessPathApprox apa, Configuration config
|
||||
NodeEx n, DataFlowCallable c, ParameterPosition pos, FlowState state, AccessPathApprox apa,
|
||||
Configuration config
|
||||
) {
|
||||
exists(AccessPathApprox apa0 |
|
||||
Stage4::parameterMayFlowThrough(_, c, _, _) and
|
||||
Stage4::revFlow(n, state, true, _, apa0, config) and
|
||||
Stage4::fwdFlow(n, state, any(CallContextCall ccc), TAccessPathApproxSome(apa), apa0, config) and
|
||||
n.getEnclosingCallable() = c
|
||||
c = n.getEnclosingCallable() and
|
||||
Stage4::revFlow(n, state, TReturnCtxMaybeFlowThrough(_), _, apa0, config) and
|
||||
Stage4::fwdFlow(n, state, any(CallContextCall ccc), TParameterPositionSome(pos),
|
||||
TAccessPathApproxSome(apa), apa0, config)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -2522,9 +2595,10 @@ pragma[nomagic]
|
||||
private predicate nodeMayUseSummary(
|
||||
NodeEx n, FlowState state, AccessPathApprox apa, Configuration config
|
||||
) {
|
||||
exists(DataFlowCallable c |
|
||||
Stage4::parameterMayFlowThrough(_, c, apa, config) and
|
||||
nodeMayUseSummary0(n, c, state, apa, config)
|
||||
exists(DataFlowCallable c, ParameterPosition pos, ParamNodeEx p |
|
||||
Stage4::parameterMayFlowThrough(p, apa, config) and
|
||||
nodeMayUseSummary0(n, c, pos, state, apa, config) and
|
||||
p.isParameterOf(c, pos)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -2532,7 +2606,7 @@ private newtype TSummaryCtx =
|
||||
TSummaryCtxNone() or
|
||||
TSummaryCtxSome(ParamNodeEx p, FlowState state, AccessPath ap) {
|
||||
exists(Configuration config |
|
||||
Stage4::parameterMayFlowThrough(p, _, ap.getApprox(), config) and
|
||||
Stage4::parameterMayFlowThrough(p, ap.getApprox(), config) and
|
||||
Stage4::revFlow(p, state, _, config)
|
||||
)
|
||||
}
|
||||
@@ -3453,17 +3527,11 @@ private predicate paramFlowsThrough(
|
||||
ReturnKindExt kind, FlowState state, CallContextCall cc, SummaryCtxSome sc, AccessPath ap,
|
||||
AccessPathApprox apa, Configuration config
|
||||
) {
|
||||
exists(PathNodeMid mid, RetNodeEx ret, ParameterPosition pos |
|
||||
exists(PathNodeMid mid, RetNodeEx ret |
|
||||
pathNode(mid, ret, state, cc, sc, ap, config, _) and
|
||||
kind = ret.getKind() and
|
||||
apa = ap.getApprox() and
|
||||
pos = sc.getParameterPos() and
|
||||
// we don't expect a parameter to return stored in itself, unless explicitly allowed
|
||||
(
|
||||
not kind.(ParamUpdateReturnKind).getPosition() = pos
|
||||
or
|
||||
sc.getParamNode().allowParameterReturnInSelf()
|
||||
)
|
||||
parameterFlowThroughAllowed(sc.getParamNode(), kind)
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@@ -70,7 +70,7 @@ abstract class Configuration extends string {
|
||||
/**
|
||||
* Holds if `sink` is a relevant data flow sink accepting `state`.
|
||||
*/
|
||||
predicate isSink(Node source, FlowState state) { none() }
|
||||
predicate isSink(Node sink, FlowState state) { none() }
|
||||
|
||||
/**
|
||||
* Holds if data flow through `node` is prohibited. This completely removes
|
||||
@@ -319,8 +319,6 @@ private class ParamNodeEx extends NodeEx {
|
||||
}
|
||||
|
||||
ParameterPosition getPosition() { this.isParameterOf(_, result) }
|
||||
|
||||
predicate allowParameterReturnInSelf() { allowParameterReturnInSelfCached(this.asNode()) }
|
||||
}
|
||||
|
||||
private class RetNodeEx extends NodeEx {
|
||||
@@ -608,6 +606,38 @@ private predicate hasSinkCallCtx(Configuration config) {
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if flow from `p` to a return node of kind `kind` is allowed.
|
||||
*
|
||||
* We don't expect a parameter to return stored in itself, unless
|
||||
* explicitly allowed
|
||||
*/
|
||||
bindingset[p, kind]
|
||||
private predicate parameterFlowThroughAllowed(ParamNodeEx p, ReturnKindExt kind) {
|
||||
exists(ParameterPosition pos | p.isParameterOf(_, pos) |
|
||||
not kind.(ParamUpdateReturnKind).getPosition() = pos
|
||||
or
|
||||
allowParameterReturnInSelfCached(p.asNode())
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* 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;
|
||||
|
||||
@@ -981,21 +1011,22 @@ private module Stage1 implements StageSig {
|
||||
* candidate for the origin of a summary.
|
||||
*/
|
||||
pragma[nomagic]
|
||||
predicate parameterMayFlowThrough(ParamNodeEx p, DataFlowCallable c, Ap ap, Configuration config) {
|
||||
exists(ReturnKindExt kind |
|
||||
predicate parameterMayFlowThrough(ParamNodeEx p, Ap ap, Configuration config) {
|
||||
exists(DataFlowCallable c, ReturnKindExt kind |
|
||||
throughFlowNodeCand(p, config) and
|
||||
returnFlowCallableNodeCand(c, kind, config) and
|
||||
p.getEnclosingCallable() = c and
|
||||
exists(ap) and
|
||||
// we don't expect a parameter to return stored in itself, unless explicitly allowed
|
||||
(
|
||||
not kind.(ParamUpdateReturnKind).getPosition() = p.getPosition()
|
||||
or
|
||||
p.allowParameterReturnInSelf()
|
||||
)
|
||||
parameterFlowThroughAllowed(p, kind)
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
predicate returnMayFlowThrough(RetNodeEx ret, ReturnKindExt kind, Configuration config) {
|
||||
throughFlowNodeCand(ret, config) and
|
||||
kind = ret.getKind()
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
predicate callMayFlowThroughRev(DataFlowCall call, Configuration config) {
|
||||
exists(ArgNodeEx arg, boolean toReturn |
|
||||
@@ -1052,12 +1083,16 @@ private predicate viableReturnPosOutNodeCand1(
|
||||
*/
|
||||
pragma[nomagic]
|
||||
private predicate flowOutOfCallNodeCand1(
|
||||
DataFlowCall call, RetNodeEx ret, NodeEx out, Configuration config
|
||||
DataFlowCall call, RetNodeEx ret, ReturnKindExt kind, NodeEx out, Configuration config
|
||||
) {
|
||||
viableReturnPosOutNodeCand1(call, ret.getReturnPosition(), out, config) and
|
||||
Stage1::revFlow(ret, config) and
|
||||
not outBarrier(ret, config) and
|
||||
not inBarrier(out, config)
|
||||
exists(ReturnPosition pos |
|
||||
viableReturnPosOutNodeCand1(call, pos, out, config) and
|
||||
pos = ret.getReturnPosition() and
|
||||
kind = pos.getKind() and
|
||||
Stage1::revFlow(ret, config) and
|
||||
not outBarrier(ret, config) and
|
||||
not inBarrier(out, config)
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
@@ -1087,10 +1122,11 @@ private predicate flowIntoCallNodeCand1(
|
||||
* edge in the graph of paths between sources and sinks that ignores call
|
||||
* contexts.
|
||||
*/
|
||||
pragma[nomagic]
|
||||
private int branch(NodeEx n1, Configuration conf) {
|
||||
result =
|
||||
strictcount(NodeEx n |
|
||||
flowOutOfCallNodeCand1(_, n1, n, conf) or flowIntoCallNodeCand1(_, n1, n, conf)
|
||||
flowOutOfCallNodeCand1(_, n1, _, n, conf) or flowIntoCallNodeCand1(_, n1, n, conf)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1099,10 +1135,11 @@ private int branch(NodeEx n1, Configuration conf) {
|
||||
* edge in the graph of paths between sources and sinks that ignores call
|
||||
* contexts.
|
||||
*/
|
||||
pragma[nomagic]
|
||||
private int join(NodeEx n2, Configuration conf) {
|
||||
result =
|
||||
strictcount(NodeEx n |
|
||||
flowOutOfCallNodeCand1(_, n, n2, conf) or flowIntoCallNodeCand1(_, n, n2, conf)
|
||||
flowOutOfCallNodeCand1(_, n, _, n2, conf) or flowIntoCallNodeCand1(_, n, n2, conf)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1115,12 +1152,13 @@ private int join(NodeEx n2, Configuration conf) {
|
||||
*/
|
||||
pragma[nomagic]
|
||||
private predicate flowOutOfCallNodeCand1(
|
||||
DataFlowCall call, RetNodeEx ret, NodeEx out, boolean allowsFieldFlow, Configuration config
|
||||
DataFlowCall call, RetNodeEx ret, ReturnKindExt kind, NodeEx out, boolean allowsFieldFlow,
|
||||
Configuration config
|
||||
) {
|
||||
flowOutOfCallNodeCand1(call, ret, out, config) and
|
||||
flowOutOfCallNodeCand1(call, ret, kind, out, pragma[only_bind_into](config)) and
|
||||
exists(int b, int j |
|
||||
b = branch(ret, config) and
|
||||
j = join(out, config) and
|
||||
b = branch(ret, pragma[only_bind_into](config)) and
|
||||
j = join(out, pragma[only_bind_into](config)) and
|
||||
if b.minimum(j) <= config.fieldFlowBranchLimit()
|
||||
then allowsFieldFlow = true
|
||||
else allowsFieldFlow = false
|
||||
@@ -1136,10 +1174,10 @@ pragma[nomagic]
|
||||
private predicate flowIntoCallNodeCand1(
|
||||
DataFlowCall call, ArgNodeEx arg, ParamNodeEx p, boolean allowsFieldFlow, Configuration config
|
||||
) {
|
||||
flowIntoCallNodeCand1(call, arg, p, config) and
|
||||
flowIntoCallNodeCand1(call, arg, p, pragma[only_bind_into](config)) and
|
||||
exists(int b, int j |
|
||||
b = branch(arg, config) and
|
||||
j = join(p, config) and
|
||||
b = branch(arg, pragma[only_bind_into](config)) and
|
||||
j = join(p, pragma[only_bind_into](config)) and
|
||||
if b.minimum(j) <= config.fieldFlowBranchLimit()
|
||||
then allowsFieldFlow = true
|
||||
else allowsFieldFlow = false
|
||||
@@ -1156,7 +1194,9 @@ private signature module StageSig {
|
||||
|
||||
predicate callMayFlowThroughRev(DataFlowCall call, Configuration config);
|
||||
|
||||
predicate parameterMayFlowThrough(ParamNodeEx p, DataFlowCallable c, Ap ap, Configuration config);
|
||||
predicate parameterMayFlowThrough(ParamNodeEx p, Ap ap, Configuration config);
|
||||
|
||||
predicate returnMayFlowThrough(RetNodeEx ret, ReturnKindExt kind, Configuration config);
|
||||
|
||||
predicate storeStepCand(
|
||||
NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, DataFlowType contentType,
|
||||
@@ -1222,7 +1262,8 @@ private module MkStage<StageSig PrevStage> {
|
||||
);
|
||||
|
||||
predicate flowOutOfCall(
|
||||
DataFlowCall call, RetNodeEx ret, NodeEx out, boolean allowsFieldFlow, Configuration config
|
||||
DataFlowCall call, RetNodeEx ret, ReturnKindExt kind, NodeEx out, boolean allowsFieldFlow,
|
||||
Configuration config
|
||||
);
|
||||
|
||||
predicate flowIntoCall(
|
||||
@@ -1247,14 +1288,14 @@ private module MkStage<StageSig PrevStage> {
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate flowThroughOutOfCall(
|
||||
DataFlowCall call, CcCall ccc, RetNodeEx ret, NodeEx out, boolean allowsFieldFlow,
|
||||
Configuration config
|
||||
DataFlowCall call, DataFlowCallable c, CcCall ccc, RetNodeEx ret, ReturnKindExt kind,
|
||||
NodeEx out, boolean allowsFieldFlow, Configuration config
|
||||
) {
|
||||
flowOutOfCall(call, ret, out, allowsFieldFlow, pragma[only_bind_into](config)) and
|
||||
flowOutOfCall(call, ret, kind, out, allowsFieldFlow, pragma[only_bind_into](config)) and
|
||||
PrevStage::callMayFlowThroughRev(call, pragma[only_bind_into](config)) and
|
||||
PrevStage::parameterMayFlowThrough(_, ret.getEnclosingCallable(), _,
|
||||
pragma[only_bind_into](config)) and
|
||||
matchesCall(ccc, call)
|
||||
PrevStage::returnMayFlowThrough(ret, kind, pragma[only_bind_into](config)) and
|
||||
matchesCall(ccc, call) and
|
||||
c = ret.getEnclosingCallable()
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1262,29 +1303,32 @@ private module MkStage<StageSig PrevStage> {
|
||||
* configuration `config`.
|
||||
*
|
||||
* The call context `cc` records whether the node is reached through an
|
||||
* argument in a call, and if so, `argAp` records the access path of that
|
||||
* argument.
|
||||
* argument in a call, and if so, `summaryCtx` and `argAp` record the
|
||||
* corresponding parameter position and access path of that argument, respectively.
|
||||
*/
|
||||
pragma[nomagic]
|
||||
additional predicate fwdFlow(
|
||||
NodeEx node, FlowState state, Cc cc, ApOption argAp, Ap ap, Configuration config
|
||||
NodeEx node, FlowState state, Cc cc, ParameterPositionOption summaryCtx, ApOption argAp,
|
||||
Ap ap, Configuration config
|
||||
) {
|
||||
fwdFlow0(node, state, cc, argAp, ap, config) and
|
||||
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, ApOption argAp, Ap ap, Configuration config
|
||||
NodeEx node, FlowState state, Cc cc, ParameterPositionOption summaryCtx, ApOption argAp,
|
||||
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)
|
||||
or
|
||||
exists(NodeEx mid, FlowState state0, Ap ap0, LocalCc localCc |
|
||||
fwdFlow(mid, state0, cc, argAp, ap0, config) and
|
||||
fwdFlow(mid, state0, cc, summaryCtx, argAp, ap0, config) and
|
||||
localCc = getLocalCc(mid, cc)
|
||||
|
|
||||
localStep(mid, state0, node, state, true, _, config, localCc) and
|
||||
@@ -1295,65 +1339,82 @@ private module MkStage<StageSig PrevStage> {
|
||||
)
|
||||
or
|
||||
exists(NodeEx mid |
|
||||
fwdFlow(mid, pragma[only_bind_into](state), _, _, ap, 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
|
||||
argAp = apNone()
|
||||
)
|
||||
or
|
||||
exists(NodeEx mid, ApNil nil |
|
||||
fwdFlow(mid, state, _, _, nil, pragma[only_bind_into](config)) and
|
||||
fwdFlow(mid, state, _, _, _, nil, pragma[only_bind_into](config)) and
|
||||
additionalJumpStep(mid, node, config) and
|
||||
cc = ccNone() and
|
||||
summaryCtx = TParameterPositionNone() and
|
||||
argAp = apNone() and
|
||||
ap = getApNil(node)
|
||||
)
|
||||
or
|
||||
exists(NodeEx mid, FlowState state0, ApNil nil |
|
||||
fwdFlow(mid, state0, _, _, nil, pragma[only_bind_into](config)) and
|
||||
fwdFlow(mid, state0, _, _, _, nil, pragma[only_bind_into](config)) and
|
||||
additionalJumpStateStep(mid, state0, node, state, config) and
|
||||
cc = ccNone() and
|
||||
summaryCtx = TParameterPositionNone() and
|
||||
argAp = apNone() and
|
||||
ap = getApNil(node)
|
||||
)
|
||||
or
|
||||
// store
|
||||
exists(TypedContent tc, Ap ap0 |
|
||||
fwdFlowStore(_, ap0, tc, node, state, cc, argAp, config) and
|
||||
fwdFlowStore(_, ap0, tc, node, state, cc, summaryCtx, argAp, config) and
|
||||
ap = apCons(tc, ap0)
|
||||
)
|
||||
or
|
||||
// read
|
||||
exists(Ap ap0, Content c |
|
||||
fwdFlowRead(ap0, c, _, node, state, cc, argAp, config) and
|
||||
fwdFlowRead(ap0, c, _, node, state, cc, summaryCtx, argAp, config) and
|
||||
fwdFlowConsCand(ap0, c, ap, config)
|
||||
)
|
||||
or
|
||||
// flow into a callable
|
||||
exists(ApApprox apa |
|
||||
fwdFlowIn(_, node, state, _, cc, _, ap, config) and
|
||||
fwdFlowIn(_, node, state, _, cc, _, _, ap, config) and
|
||||
apa = getApprox(ap) and
|
||||
if PrevStage::parameterMayFlowThrough(node, _, apa, config)
|
||||
then argAp = apSome(ap)
|
||||
else argAp = apNone()
|
||||
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
|
||||
fwdFlowOutNotFromArg(node, state, cc, argAp, ap, config)
|
||||
exists(
|
||||
DataFlowCall call, RetNodeEx ret, boolean allowsFieldFlow, CcNoCall innercc,
|
||||
DataFlowCallable inner
|
||||
|
|
||||
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()
|
||||
)
|
||||
or
|
||||
exists(DataFlowCall call, Ap argAp0 |
|
||||
fwdFlowOutFromArg(call, node, state, argAp0, ap, config) and
|
||||
fwdFlowIsEntered(call, cc, argAp, argAp0, config)
|
||||
// flow through a callable
|
||||
exists(DataFlowCall call, ParameterPosition summaryCtx0, Ap argAp0 |
|
||||
fwdFlowOutFromArg(call, node, state, summaryCtx0, argAp0, ap, config) and
|
||||
fwdFlowIsEntered(call, cc, summaryCtx, argAp, summaryCtx0, argAp0, config)
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate fwdFlowStore(
|
||||
NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, FlowState state, Cc cc, ApOption argAp,
|
||||
Configuration config
|
||||
NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, FlowState state, Cc cc,
|
||||
ParameterPositionOption summaryCtx, ApOption argAp, Configuration config
|
||||
) {
|
||||
exists(DataFlowType contentType |
|
||||
fwdFlow(node1, state, cc, argAp, ap1, config) and
|
||||
fwdFlow(node1, state, cc, summaryCtx, argAp, ap1, config) and
|
||||
PrevStage::storeStepCand(node1, unbindApa(getApprox(ap1)), tc, node2, contentType, config) and
|
||||
typecheckStore(ap1, contentType)
|
||||
)
|
||||
@@ -1366,7 +1427,7 @@ private module MkStage<StageSig PrevStage> {
|
||||
pragma[nomagic]
|
||||
private predicate fwdFlowConsCand(Ap cons, Content c, Ap tail, Configuration config) {
|
||||
exists(TypedContent tc |
|
||||
fwdFlowStore(_, tail, tc, _, _, _, _, config) and
|
||||
fwdFlowStore(_, tail, tc, _, _, _, _, _, config) and
|
||||
tc.getContent() = c and
|
||||
cons = apCons(tc, tail)
|
||||
)
|
||||
@@ -1374,21 +1435,21 @@ private module MkStage<StageSig PrevStage> {
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate fwdFlowRead(
|
||||
Ap ap, Content c, NodeEx node1, NodeEx node2, FlowState state, Cc cc, ApOption argAp,
|
||||
Configuration config
|
||||
Ap ap, Content c, NodeEx node1, NodeEx node2, FlowState state, Cc cc,
|
||||
ParameterPositionOption summaryCtx, ApOption argAp, Configuration config
|
||||
) {
|
||||
fwdFlow(node1, state, cc, 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, Cc innercc, ApOption argAp,
|
||||
Ap ap, 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, argAp, ap, 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()
|
||||
@@ -1396,29 +1457,19 @@ private module MkStage<StageSig PrevStage> {
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate fwdFlowOutNotFromArg(
|
||||
NodeEx out, FlowState state, Cc ccOut, ApOption argAp, Ap ap, Configuration config
|
||||
private predicate fwdFlowOutFromArg(
|
||||
DataFlowCall call, NodeEx out, FlowState state, ParameterPosition summaryCtx, Ap argAp, Ap ap,
|
||||
Configuration config
|
||||
) {
|
||||
exists(
|
||||
DataFlowCall call, RetNodeEx ret, boolean allowsFieldFlow, CcNoCall innercc,
|
||||
DataFlowCallable inner
|
||||
DataFlowCallable c, RetNodeEx ret, ReturnKindExt kind, boolean allowsFieldFlow, CcCall ccc
|
||||
|
|
||||
fwdFlow(ret, state, innercc, argAp, ap, config) and
|
||||
flowOutOfCall(call, ret, out, allowsFieldFlow, config) and
|
||||
inner = ret.getEnclosingCallable() and
|
||||
ccOut = getCallContextReturn(inner, call, innercc) and
|
||||
if allowsFieldFlow = false then ap instanceof ApNil else any()
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate fwdFlowOutFromArg(
|
||||
DataFlowCall call, NodeEx out, FlowState state, Ap argAp, Ap ap, Configuration config
|
||||
) {
|
||||
exists(RetNodeEx ret, boolean allowsFieldFlow, CcCall ccc |
|
||||
fwdFlow(ret, state, ccc, apSome(argAp), ap, config) and
|
||||
flowThroughOutOfCall(call, ccc, ret, out, allowsFieldFlow, config) and
|
||||
if allowsFieldFlow = false then ap instanceof ApNil else any()
|
||||
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
|
||||
(if allowsFieldFlow = false then ap instanceof ApNil else any()) and
|
||||
parameterFlowThroughAllowed(c, pragma[only_bind_into](summaryCtx), kind)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1428,11 +1479,13 @@ private module MkStage<StageSig PrevStage> {
|
||||
*/
|
||||
pragma[nomagic]
|
||||
private predicate fwdFlowIsEntered(
|
||||
DataFlowCall call, Cc cc, ApOption argAp, Ap ap, Configuration config
|
||||
DataFlowCall call, Cc cc, ParameterPositionOption summaryCtx, ApOption argAp,
|
||||
ParameterPosition pos, Ap ap, Configuration config
|
||||
) {
|
||||
exists(ParamNodeEx p |
|
||||
fwdFlowIn(call, p, _, cc, _, argAp, ap, config) and
|
||||
PrevStage::parameterMayFlowThrough(p, _, unbindApa(getApprox(ap)), config)
|
||||
exists(ParamNodeEx param |
|
||||
fwdFlowIn(call, param, _, cc, _, summaryCtx, argAp, ap, config) and
|
||||
PrevStage::parameterMayFlowThrough(param, unbindApa(getApprox(ap)), config) and
|
||||
pos = param.getPosition()
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1440,27 +1493,40 @@ private module MkStage<StageSig PrevStage> {
|
||||
private predicate storeStepFwd(
|
||||
NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, Ap ap2, Configuration config
|
||||
) {
|
||||
fwdFlowStore(node1, ap1, tc, node2, _, _, _, config) and
|
||||
fwdFlowStore(node1, ap1, tc, node2, _, _, _, _, config) and
|
||||
ap2 = apCons(tc, ap1) and
|
||||
fwdFlowRead(ap2, tc.getContent(), _, _, _, _, _, config)
|
||||
fwdFlowRead(ap2, tc.getContent(), _, _, _, _, _, _, config)
|
||||
}
|
||||
|
||||
private predicate readStepFwd(
|
||||
NodeEx n1, Ap ap1, Content c, NodeEx n2, Ap ap2, Configuration config
|
||||
) {
|
||||
fwdFlowRead(ap1, c, n1, n2, _, _, _, config) and
|
||||
fwdFlowRead(ap1, c, n1, n2, _, _, _, _, config) and
|
||||
fwdFlowConsCand(ap1, c, ap2, config)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate callMayFlowThroughFwd(DataFlowCall call, Configuration config) {
|
||||
exists(Ap argAp0, NodeEx out, FlowState state, Cc cc, ApOption argAp, Ap ap |
|
||||
fwdFlow(out, state, pragma[only_bind_into](cc), pragma[only_bind_into](argAp), ap,
|
||||
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
|
||||
fwdFlowOutFromArg(call, out, state, argAp0, ap, config) and
|
||||
fwdFlowIsEntered(pragma[only_bind_into](call), pragma[only_bind_into](cc),
|
||||
pragma[only_bind_into](argAp), pragma[only_bind_into](argAp0),
|
||||
pragma[only_bind_into](config))
|
||||
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(DataFlowCallable c, ParameterPosition ppos |
|
||||
returnFlowsThrough0(ret, kind, state, ccc, c, ppos, argAp, ap, config) and
|
||||
p.isParameterOf(c, ppos) and
|
||||
parameterFlowThroughAllowed(p, kind)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1468,118 +1534,130 @@ private module MkStage<StageSig PrevStage> {
|
||||
private predicate flowThroughIntoCall(
|
||||
DataFlowCall call, ArgNodeEx arg, ParamNodeEx p, boolean allowsFieldFlow, Configuration config
|
||||
) {
|
||||
flowIntoCall(call, arg, p, allowsFieldFlow, config) and
|
||||
fwdFlow(arg, _, _, _, _, pragma[only_bind_into](config)) and
|
||||
PrevStage::parameterMayFlowThrough(p, _, _, pragma[only_bind_into](config)) and
|
||||
callMayFlowThroughFwd(call, pragma[only_bind_into](config))
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate returnNodeMayFlowThrough(
|
||||
RetNodeEx ret, FlowState state, Ap ap, Configuration config
|
||||
) {
|
||||
fwdFlow(ret, state, any(CcCall ccc), apSome(_), ap, config)
|
||||
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))
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `node` with access path `ap` is part of a path from a source to a
|
||||
* sink in the configuration `config`.
|
||||
*
|
||||
* The Boolean `toReturn` records whether the node must be returned from the
|
||||
* enclosing callable in order to reach a sink, and if so, `returnAp` records
|
||||
* the access path of the returned value.
|
||||
* The parameter `returnCtx` records whether (and how) the node must be returned
|
||||
* from the enclosing callable in order to reach a sink, and if so, `returnAp`
|
||||
* records the access path of the returned value.
|
||||
*/
|
||||
pragma[nomagic]
|
||||
additional predicate revFlow(
|
||||
NodeEx node, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, Configuration config
|
||||
NodeEx node, FlowState state, ReturnCtx returnCtx, ApOption returnAp, Ap ap,
|
||||
Configuration config
|
||||
) {
|
||||
revFlow0(node, state, toReturn, returnAp, ap, config) and
|
||||
fwdFlow(node, state, _, _, ap, config)
|
||||
revFlow0(node, state, returnCtx, returnAp, ap, config) and
|
||||
fwdFlow(node, state, _, _, _, ap, config)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate revFlow0(
|
||||
NodeEx node, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, Configuration config
|
||||
NodeEx node, FlowState state, ReturnCtx returnCtx, ApOption returnAp, Ap ap,
|
||||
Configuration config
|
||||
) {
|
||||
fwdFlow(node, state, _, _, ap, config) and
|
||||
fwdFlow(node, state, _, _, _, ap, config) and
|
||||
sinkNode(node, state, config) and
|
||||
(if hasSinkCallCtx(config) then toReturn = true else toReturn = false) and
|
||||
(
|
||||
if hasSinkCallCtx(config)
|
||||
then returnCtx = TReturnCtxNoFlowThrough()
|
||||
else returnCtx = TReturnCtxNone()
|
||||
) and
|
||||
returnAp = apNone() and
|
||||
ap instanceof ApNil
|
||||
or
|
||||
exists(NodeEx mid, FlowState state0 |
|
||||
localStep(node, state, mid, state0, true, _, config, _) and
|
||||
revFlow(mid, state0, toReturn, returnAp, ap, config)
|
||||
revFlow(mid, state0, returnCtx, returnAp, ap, config)
|
||||
)
|
||||
or
|
||||
exists(NodeEx mid, FlowState state0, ApNil nil |
|
||||
fwdFlow(node, pragma[only_bind_into](state), _, _, ap, pragma[only_bind_into](config)) and
|
||||
fwdFlow(node, pragma[only_bind_into](state), _, _, _, ap, pragma[only_bind_into](config)) and
|
||||
localStep(node, pragma[only_bind_into](state), mid, state0, false, _, config, _) and
|
||||
revFlow(mid, state0, toReturn, returnAp, nil, pragma[only_bind_into](config)) and
|
||||
revFlow(mid, state0, returnCtx, returnAp, nil, pragma[only_bind_into](config)) and
|
||||
ap instanceof ApNil
|
||||
)
|
||||
or
|
||||
exists(NodeEx mid |
|
||||
jumpStep(node, mid, config) and
|
||||
revFlow(mid, state, _, _, ap, config) and
|
||||
toReturn = false and
|
||||
returnCtx = TReturnCtxNone() and
|
||||
returnAp = apNone()
|
||||
)
|
||||
or
|
||||
exists(NodeEx mid, ApNil nil |
|
||||
fwdFlow(node, _, _, _, ap, pragma[only_bind_into](config)) and
|
||||
fwdFlow(node, _, _, _, _, ap, pragma[only_bind_into](config)) and
|
||||
additionalJumpStep(node, mid, config) and
|
||||
revFlow(pragma[only_bind_into](mid), state, _, _, nil, pragma[only_bind_into](config)) and
|
||||
toReturn = false and
|
||||
returnCtx = TReturnCtxNone() and
|
||||
returnAp = apNone() and
|
||||
ap instanceof ApNil
|
||||
)
|
||||
or
|
||||
exists(NodeEx mid, FlowState state0, ApNil nil |
|
||||
fwdFlow(node, _, _, _, ap, pragma[only_bind_into](config)) and
|
||||
fwdFlow(node, _, _, _, _, ap, pragma[only_bind_into](config)) and
|
||||
additionalJumpStateStep(node, state, mid, state0, config) and
|
||||
revFlow(pragma[only_bind_into](mid), pragma[only_bind_into](state0), _, _, nil,
|
||||
pragma[only_bind_into](config)) and
|
||||
toReturn = false and
|
||||
returnCtx = TReturnCtxNone() and
|
||||
returnAp = apNone() and
|
||||
ap instanceof ApNil
|
||||
)
|
||||
or
|
||||
// store
|
||||
exists(Ap ap0, Content c |
|
||||
revFlowStore(ap0, c, ap, node, state, _, _, toReturn, returnAp, config) and
|
||||
revFlowStore(ap0, c, ap, node, state, _, _, returnCtx, returnAp, config) and
|
||||
revFlowConsCand(ap0, c, ap, config)
|
||||
)
|
||||
or
|
||||
// read
|
||||
exists(NodeEx mid, Ap ap0 |
|
||||
revFlow(mid, state, toReturn, returnAp, ap0, config) and
|
||||
revFlow(mid, state, returnCtx, returnAp, ap0, config) and
|
||||
readStepFwd(node, ap, _, mid, ap0, config)
|
||||
)
|
||||
or
|
||||
// flow into a callable
|
||||
revFlowInNotToReturn(node, state, returnAp, ap, config) and
|
||||
toReturn = false
|
||||
exists(ParamNodeEx p, boolean allowsFieldFlow |
|
||||
revFlow(p, state, TReturnCtxNone(), returnAp, ap, config) and
|
||||
flowIntoCall(_, node, p, allowsFieldFlow, config) and
|
||||
(if allowsFieldFlow = false then ap instanceof ApNil else any()) and
|
||||
returnCtx = TReturnCtxNone()
|
||||
)
|
||||
or
|
||||
exists(DataFlowCall call, Ap returnAp0 |
|
||||
revFlowInToReturn(call, node, state, returnAp0, ap, config) and
|
||||
revFlowIsReturned(call, toReturn, returnAp, returnAp0, config)
|
||||
// flow through a callable
|
||||
exists(DataFlowCall call, ReturnKindExt returnKind0, Ap returnAp0 |
|
||||
revFlowInToReturn(call, node, state, returnKind0, returnAp0, ap, config) and
|
||||
revFlowIsReturned(call, returnCtx, returnAp, returnKind0, returnAp0, config)
|
||||
)
|
||||
or
|
||||
// flow out of a callable
|
||||
revFlowOut(_, node, state, _, _, ap, config) and
|
||||
toReturn = true and
|
||||
if returnNodeMayFlowThrough(node, state, ap, config)
|
||||
then returnAp = apSome(ap)
|
||||
else returnAp = apNone()
|
||||
exists(ReturnKindExt kind |
|
||||
revFlowOut(_, node, kind, state, _, _, ap, config) and
|
||||
if returnFlowsThrough(node, kind, state, _, _, _, ap, config)
|
||||
then (
|
||||
returnCtx = TReturnCtxMaybeFlowThrough(kind) and
|
||||
returnAp = apSome(ap)
|
||||
) else (
|
||||
returnCtx = TReturnCtxNoFlowThrough() and returnAp = apNone()
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate revFlowStore(
|
||||
Ap ap0, Content c, Ap ap, NodeEx node, FlowState state, TypedContent tc, NodeEx mid,
|
||||
boolean toReturn, ApOption returnAp, Configuration config
|
||||
ReturnCtx returnCtx, ApOption returnAp, Configuration config
|
||||
) {
|
||||
revFlow(mid, state, toReturn, returnAp, ap0, config) and
|
||||
revFlow(mid, state, returnCtx, returnAp, ap0, config) and
|
||||
storeStepFwd(node, ap, tc, mid, ap0, config) and
|
||||
tc.getContent() = c
|
||||
}
|
||||
@@ -1599,35 +1677,27 @@ private module MkStage<StageSig PrevStage> {
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate revFlowOut(
|
||||
DataFlowCall call, RetNodeEx ret, FlowState state, boolean toReturn, ApOption returnAp, Ap ap,
|
||||
Configuration config
|
||||
DataFlowCall call, RetNodeEx ret, ReturnKindExt kind, FlowState state, ReturnCtx returnCtx,
|
||||
ApOption returnAp, Ap ap, Configuration config
|
||||
) {
|
||||
exists(NodeEx out, boolean allowsFieldFlow |
|
||||
revFlow(out, state, toReturn, returnAp, ap, config) and
|
||||
flowOutOfCall(call, ret, out, allowsFieldFlow, config) and
|
||||
if allowsFieldFlow = false then ap instanceof ApNil else any()
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate revFlowInNotToReturn(
|
||||
ArgNodeEx arg, FlowState state, ApOption returnAp, Ap ap, Configuration config
|
||||
) {
|
||||
exists(ParamNodeEx p, boolean allowsFieldFlow |
|
||||
revFlow(p, state, false, returnAp, ap, config) and
|
||||
flowIntoCall(_, arg, p, allowsFieldFlow, config) and
|
||||
revFlow(out, state, returnCtx, returnAp, ap, config) and
|
||||
flowOutOfCall(call, ret, kind, out, allowsFieldFlow, config) and
|
||||
if allowsFieldFlow = false then ap instanceof ApNil else any()
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate revFlowInToReturn(
|
||||
DataFlowCall call, ArgNodeEx arg, FlowState state, Ap returnAp, Ap ap, Configuration config
|
||||
DataFlowCall call, ArgNodeEx arg, FlowState state, ReturnKindExt kind, Ap returnAp, Ap ap,
|
||||
Configuration config
|
||||
) {
|
||||
exists(ParamNodeEx p, boolean allowsFieldFlow |
|
||||
revFlow(p, state, true, apSome(returnAp), ap, config) and
|
||||
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()
|
||||
(if allowsFieldFlow = false then ap instanceof ApNil else any()) and
|
||||
parameterFlowThroughAllowed(p, kind)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1638,11 +1708,12 @@ private module MkStage<StageSig PrevStage> {
|
||||
*/
|
||||
pragma[nomagic]
|
||||
private predicate revFlowIsReturned(
|
||||
DataFlowCall call, boolean toReturn, ApOption returnAp, Ap ap, Configuration config
|
||||
DataFlowCall call, ReturnCtx returnCtx, ApOption returnAp, ReturnKindExt kind, Ap ap,
|
||||
Configuration config
|
||||
) {
|
||||
exists(RetNodeEx ret, FlowState state, CcCall ccc |
|
||||
revFlowOut(call, ret, state, toReturn, returnAp, ap, config) and
|
||||
fwdFlow(ret, state, ccc, apSome(_), ap, config) and
|
||||
revFlowOut(call, ret, kind, state, returnCtx, returnAp, ap, config) and
|
||||
returnFlowsThrough(ret, kind, state, ccc, _, _, ap, config) and
|
||||
matchesCall(ccc, call)
|
||||
)
|
||||
}
|
||||
@@ -1713,40 +1784,39 @@ private module MkStage<StageSig PrevStage> {
|
||||
validAp(ap, config)
|
||||
}
|
||||
|
||||
pragma[noinline]
|
||||
private predicate parameterFlow(
|
||||
ParamNodeEx p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config
|
||||
pragma[nomagic]
|
||||
private predicate parameterFlowsThroughRev(
|
||||
ParamNodeEx p, Ap ap, ReturnKindExt kind, Configuration config
|
||||
) {
|
||||
revFlow(p, _, true, apSome(ap0), ap, config) and
|
||||
c = p.getEnclosingCallable()
|
||||
revFlow(p, _, TReturnCtxMaybeFlowThrough(kind), apSome(_), ap, config) and
|
||||
parameterFlowThroughAllowed(p, kind)
|
||||
}
|
||||
|
||||
predicate parameterMayFlowThrough(ParamNodeEx p, DataFlowCallable c, Ap ap, Configuration config) {
|
||||
exists(RetNodeEx ret, FlowState state, Ap ap0, ReturnKindExt kind, ParameterPosition pos |
|
||||
parameterFlow(p, ap, ap0, c, config) and
|
||||
c = ret.getEnclosingCallable() and
|
||||
revFlow(pragma[only_bind_into](ret), pragma[only_bind_into](state), true, apSome(_),
|
||||
pragma[only_bind_into](ap0), pragma[only_bind_into](config)) and
|
||||
fwdFlow(ret, state, any(CcCall ccc), apSome(ap), ap0, config) and
|
||||
kind = ret.getKind() and
|
||||
p.getPosition() = pos and
|
||||
// we don't expect a parameter to return stored in itself, unless explicitly allowed
|
||||
(
|
||||
not kind.(ParamUpdateReturnKind).getPosition() = pos
|
||||
or
|
||||
p.allowParameterReturnInSelf()
|
||||
)
|
||||
pragma[nomagic]
|
||||
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)
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
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(
|
||||
Ap returnAp0, ArgNodeEx arg, FlowState state, boolean toReturn, ApOption returnAp, Ap ap
|
||||
ReturnKindExt returnKind0, Ap returnAp0, ArgNodeEx arg, FlowState state,
|
||||
ReturnCtx returnCtx, ApOption returnAp, Ap ap
|
||||
|
|
||||
revFlow(arg, state, toReturn, returnAp, ap, config) and
|
||||
revFlowInToReturn(call, arg, state, returnAp0, ap, config) and
|
||||
revFlowIsReturned(call, toReturn, returnAp, returnAp0, config)
|
||||
revFlow(arg, state, returnCtx, returnAp, ap, config) and
|
||||
revFlowInToReturn(call, arg, state, returnKind0, returnAp0, ap, config) and
|
||||
revFlowIsReturned(call, returnCtx, returnAp, returnKind0, returnAp0, config)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1754,14 +1824,13 @@ private module MkStage<StageSig PrevStage> {
|
||||
boolean fwd, int nodes, int fields, int conscand, int states, int tuples, Configuration config
|
||||
) {
|
||||
fwd = true and
|
||||
nodes = count(NodeEx node | fwdFlow(node, _, _, _, _, config)) and
|
||||
nodes = count(NodeEx node | fwdFlow(node, _, _, _, _, _, config)) and
|
||||
fields = count(TypedContent f0 | fwdConsCand(f0, _, config)) and
|
||||
conscand = count(TypedContent f0, Ap ap | fwdConsCand(f0, ap, config)) and
|
||||
states = count(FlowState state | fwdFlow(_, state, _, _, _, config)) and
|
||||
states = count(FlowState state | fwdFlow(_, state, _, _, _, _, config)) and
|
||||
tuples =
|
||||
count(NodeEx n, FlowState state, Cc cc, ApOption argAp, Ap ap |
|
||||
fwdFlow(n, state, cc, argAp, ap, config)
|
||||
)
|
||||
count(NodeEx n, FlowState state, Cc cc, ParameterPositionOption summaryCtx, ApOption argAp,
|
||||
Ap ap | fwdFlow(n, state, cc, summaryCtx, argAp, ap, config))
|
||||
or
|
||||
fwd = false and
|
||||
nodes = count(NodeEx node | revFlow(node, _, _, _, _, config)) and
|
||||
@@ -1769,8 +1838,8 @@ private module MkStage<StageSig PrevStage> {
|
||||
conscand = count(TypedContent f0, Ap ap | consCand(f0, ap, config)) and
|
||||
states = count(FlowState state | revFlow(_, state, _, _, _, config)) and
|
||||
tuples =
|
||||
count(NodeEx n, FlowState state, boolean b, ApOption retAp, Ap ap |
|
||||
revFlow(n, state, b, retAp, ap, config)
|
||||
count(NodeEx n, FlowState state, ReturnCtx returnCtx, ApOption retAp, Ap ap |
|
||||
revFlow(n, state, returnCtx, retAp, ap, config)
|
||||
)
|
||||
}
|
||||
/* End: Stage logic. */
|
||||
@@ -1915,7 +1984,7 @@ private module Stage2Param implements MkStage<Stage1>::StageParam {
|
||||
exists(lcc)
|
||||
}
|
||||
|
||||
predicate flowOutOfCall = flowOutOfCallNodeCand1/5;
|
||||
predicate flowOutOfCall = flowOutOfCallNodeCand1/6;
|
||||
|
||||
predicate flowIntoCall = flowIntoCallNodeCand1/5;
|
||||
|
||||
@@ -1951,9 +2020,10 @@ private module Stage2 implements StageSig {
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate flowOutOfCallNodeCand2(
|
||||
DataFlowCall call, RetNodeEx node1, NodeEx node2, boolean allowsFieldFlow, Configuration config
|
||||
DataFlowCall call, RetNodeEx node1, ReturnKindExt kind, NodeEx node2, boolean allowsFieldFlow,
|
||||
Configuration config
|
||||
) {
|
||||
flowOutOfCallNodeCand1(call, node1, node2, allowsFieldFlow, config) and
|
||||
flowOutOfCallNodeCand1(call, node1, kind, node2, allowsFieldFlow, config) and
|
||||
Stage2::revFlow(node2, pragma[only_bind_into](config)) and
|
||||
Stage2::revFlowAlias(node1, pragma[only_bind_into](config))
|
||||
}
|
||||
@@ -2021,8 +2091,8 @@ private module LocalFlowBigStep {
|
||||
exists(NodeEx next | Stage2::revFlow(next, state, config) |
|
||||
jumpStep(node, next, config) or
|
||||
additionalJumpStep(node, next, config) or
|
||||
flowIntoCallNodeCand1(_, node, next, config) or
|
||||
flowOutOfCallNodeCand1(_, node, next, config) or
|
||||
flowIntoCallNodeCand2(_, node, next, _, config) or
|
||||
flowOutOfCallNodeCand2(_, node, _, next, _, config) or
|
||||
Stage2::storeStepCand(node, _, _, next, _, config) or
|
||||
Stage2::readStepCand(node, _, next, config)
|
||||
)
|
||||
@@ -2163,7 +2233,7 @@ private module Stage3Param implements MkStage<Stage2>::StageParam {
|
||||
localFlowBigStep(node1, state1, node2, state2, preservesValue, ap, config, _) and exists(lcc)
|
||||
}
|
||||
|
||||
predicate flowOutOfCall = flowOutOfCallNodeCand2/5;
|
||||
predicate flowOutOfCall = flowOutOfCallNodeCand2/6;
|
||||
|
||||
predicate flowIntoCall = flowIntoCallNodeCand2/5;
|
||||
|
||||
@@ -2233,8 +2303,9 @@ private predicate flowCandSummaryCtx(
|
||||
NodeEx node, FlowState state, AccessPathFront argApf, Configuration config
|
||||
) {
|
||||
exists(AccessPathFront apf |
|
||||
Stage3::revFlow(node, state, true, _, apf, config) and
|
||||
Stage3::fwdFlow(node, state, any(Stage3::CcCall ccc), TAccessPathFrontSome(argApf), apf, config)
|
||||
Stage3::revFlow(node, state, TReturnCtxMaybeFlowThrough(_), _, apf, config) and
|
||||
Stage3::fwdFlow(node, state, any(Stage3::CcCall ccc), _, TAccessPathFrontSome(argApf), apf,
|
||||
config)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -2468,10 +2539,11 @@ private module Stage4Param implements MkStage<Stage3>::StageParam {
|
||||
|
||||
pragma[nomagic]
|
||||
predicate flowOutOfCall(
|
||||
DataFlowCall call, RetNodeEx node1, NodeEx node2, boolean allowsFieldFlow, Configuration config
|
||||
DataFlowCall call, RetNodeEx node1, ReturnKindExt kind, NodeEx node2, boolean allowsFieldFlow,
|
||||
Configuration config
|
||||
) {
|
||||
exists(FlowState state |
|
||||
flowOutOfCallNodeCand2(call, node1, node2, allowsFieldFlow, config) and
|
||||
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))
|
||||
@@ -2508,13 +2580,14 @@ private Configuration unbindConf(Configuration conf) {
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate nodeMayUseSummary0(
|
||||
NodeEx n, DataFlowCallable c, FlowState state, AccessPathApprox apa, Configuration config
|
||||
NodeEx n, DataFlowCallable c, ParameterPosition pos, FlowState state, AccessPathApprox apa,
|
||||
Configuration config
|
||||
) {
|
||||
exists(AccessPathApprox apa0 |
|
||||
Stage4::parameterMayFlowThrough(_, c, _, _) and
|
||||
Stage4::revFlow(n, state, true, _, apa0, config) and
|
||||
Stage4::fwdFlow(n, state, any(CallContextCall ccc), TAccessPathApproxSome(apa), apa0, config) and
|
||||
n.getEnclosingCallable() = c
|
||||
c = n.getEnclosingCallable() and
|
||||
Stage4::revFlow(n, state, TReturnCtxMaybeFlowThrough(_), _, apa0, config) and
|
||||
Stage4::fwdFlow(n, state, any(CallContextCall ccc), TParameterPositionSome(pos),
|
||||
TAccessPathApproxSome(apa), apa0, config)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -2522,9 +2595,10 @@ pragma[nomagic]
|
||||
private predicate nodeMayUseSummary(
|
||||
NodeEx n, FlowState state, AccessPathApprox apa, Configuration config
|
||||
) {
|
||||
exists(DataFlowCallable c |
|
||||
Stage4::parameterMayFlowThrough(_, c, apa, config) and
|
||||
nodeMayUseSummary0(n, c, state, apa, config)
|
||||
exists(DataFlowCallable c, ParameterPosition pos, ParamNodeEx p |
|
||||
Stage4::parameterMayFlowThrough(p, apa, config) and
|
||||
nodeMayUseSummary0(n, c, pos, state, apa, config) and
|
||||
p.isParameterOf(c, pos)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -2532,7 +2606,7 @@ private newtype TSummaryCtx =
|
||||
TSummaryCtxNone() or
|
||||
TSummaryCtxSome(ParamNodeEx p, FlowState state, AccessPath ap) {
|
||||
exists(Configuration config |
|
||||
Stage4::parameterMayFlowThrough(p, _, ap.getApprox(), config) and
|
||||
Stage4::parameterMayFlowThrough(p, ap.getApprox(), config) and
|
||||
Stage4::revFlow(p, state, _, config)
|
||||
)
|
||||
}
|
||||
@@ -3453,17 +3527,11 @@ private predicate paramFlowsThrough(
|
||||
ReturnKindExt kind, FlowState state, CallContextCall cc, SummaryCtxSome sc, AccessPath ap,
|
||||
AccessPathApprox apa, Configuration config
|
||||
) {
|
||||
exists(PathNodeMid mid, RetNodeEx ret, ParameterPosition pos |
|
||||
exists(PathNodeMid mid, RetNodeEx ret |
|
||||
pathNode(mid, ret, state, cc, sc, ap, config, _) and
|
||||
kind = ret.getKind() and
|
||||
apa = ap.getApprox() and
|
||||
pos = sc.getParameterPos() and
|
||||
// we don't expect a parameter to return stored in itself, unless explicitly allowed
|
||||
(
|
||||
not kind.(ParamUpdateReturnKind).getPosition() = pos
|
||||
or
|
||||
sc.getParamNode().allowParameterReturnInSelf()
|
||||
)
|
||||
parameterFlowThroughAllowed(sc.getParamNode(), kind)
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@@ -70,7 +70,7 @@ abstract class Configuration extends string {
|
||||
/**
|
||||
* Holds if `sink` is a relevant data flow sink accepting `state`.
|
||||
*/
|
||||
predicate isSink(Node source, FlowState state) { none() }
|
||||
predicate isSink(Node sink, FlowState state) { none() }
|
||||
|
||||
/**
|
||||
* Holds if data flow through `node` is prohibited. This completely removes
|
||||
@@ -319,8 +319,6 @@ private class ParamNodeEx extends NodeEx {
|
||||
}
|
||||
|
||||
ParameterPosition getPosition() { this.isParameterOf(_, result) }
|
||||
|
||||
predicate allowParameterReturnInSelf() { allowParameterReturnInSelfCached(this.asNode()) }
|
||||
}
|
||||
|
||||
private class RetNodeEx extends NodeEx {
|
||||
@@ -608,6 +606,38 @@ private predicate hasSinkCallCtx(Configuration config) {
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if flow from `p` to a return node of kind `kind` is allowed.
|
||||
*
|
||||
* We don't expect a parameter to return stored in itself, unless
|
||||
* explicitly allowed
|
||||
*/
|
||||
bindingset[p, kind]
|
||||
private predicate parameterFlowThroughAllowed(ParamNodeEx p, ReturnKindExt kind) {
|
||||
exists(ParameterPosition pos | p.isParameterOf(_, pos) |
|
||||
not kind.(ParamUpdateReturnKind).getPosition() = pos
|
||||
or
|
||||
allowParameterReturnInSelfCached(p.asNode())
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* 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;
|
||||
|
||||
@@ -981,21 +1011,22 @@ private module Stage1 implements StageSig {
|
||||
* candidate for the origin of a summary.
|
||||
*/
|
||||
pragma[nomagic]
|
||||
predicate parameterMayFlowThrough(ParamNodeEx p, DataFlowCallable c, Ap ap, Configuration config) {
|
||||
exists(ReturnKindExt kind |
|
||||
predicate parameterMayFlowThrough(ParamNodeEx p, Ap ap, Configuration config) {
|
||||
exists(DataFlowCallable c, ReturnKindExt kind |
|
||||
throughFlowNodeCand(p, config) and
|
||||
returnFlowCallableNodeCand(c, kind, config) and
|
||||
p.getEnclosingCallable() = c and
|
||||
exists(ap) and
|
||||
// we don't expect a parameter to return stored in itself, unless explicitly allowed
|
||||
(
|
||||
not kind.(ParamUpdateReturnKind).getPosition() = p.getPosition()
|
||||
or
|
||||
p.allowParameterReturnInSelf()
|
||||
)
|
||||
parameterFlowThroughAllowed(p, kind)
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
predicate returnMayFlowThrough(RetNodeEx ret, ReturnKindExt kind, Configuration config) {
|
||||
throughFlowNodeCand(ret, config) and
|
||||
kind = ret.getKind()
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
predicate callMayFlowThroughRev(DataFlowCall call, Configuration config) {
|
||||
exists(ArgNodeEx arg, boolean toReturn |
|
||||
@@ -1052,12 +1083,16 @@ private predicate viableReturnPosOutNodeCand1(
|
||||
*/
|
||||
pragma[nomagic]
|
||||
private predicate flowOutOfCallNodeCand1(
|
||||
DataFlowCall call, RetNodeEx ret, NodeEx out, Configuration config
|
||||
DataFlowCall call, RetNodeEx ret, ReturnKindExt kind, NodeEx out, Configuration config
|
||||
) {
|
||||
viableReturnPosOutNodeCand1(call, ret.getReturnPosition(), out, config) and
|
||||
Stage1::revFlow(ret, config) and
|
||||
not outBarrier(ret, config) and
|
||||
not inBarrier(out, config)
|
||||
exists(ReturnPosition pos |
|
||||
viableReturnPosOutNodeCand1(call, pos, out, config) and
|
||||
pos = ret.getReturnPosition() and
|
||||
kind = pos.getKind() and
|
||||
Stage1::revFlow(ret, config) and
|
||||
not outBarrier(ret, config) and
|
||||
not inBarrier(out, config)
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
@@ -1087,10 +1122,11 @@ private predicate flowIntoCallNodeCand1(
|
||||
* edge in the graph of paths between sources and sinks that ignores call
|
||||
* contexts.
|
||||
*/
|
||||
pragma[nomagic]
|
||||
private int branch(NodeEx n1, Configuration conf) {
|
||||
result =
|
||||
strictcount(NodeEx n |
|
||||
flowOutOfCallNodeCand1(_, n1, n, conf) or flowIntoCallNodeCand1(_, n1, n, conf)
|
||||
flowOutOfCallNodeCand1(_, n1, _, n, conf) or flowIntoCallNodeCand1(_, n1, n, conf)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1099,10 +1135,11 @@ private int branch(NodeEx n1, Configuration conf) {
|
||||
* edge in the graph of paths between sources and sinks that ignores call
|
||||
* contexts.
|
||||
*/
|
||||
pragma[nomagic]
|
||||
private int join(NodeEx n2, Configuration conf) {
|
||||
result =
|
||||
strictcount(NodeEx n |
|
||||
flowOutOfCallNodeCand1(_, n, n2, conf) or flowIntoCallNodeCand1(_, n, n2, conf)
|
||||
flowOutOfCallNodeCand1(_, n, _, n2, conf) or flowIntoCallNodeCand1(_, n, n2, conf)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1115,12 +1152,13 @@ private int join(NodeEx n2, Configuration conf) {
|
||||
*/
|
||||
pragma[nomagic]
|
||||
private predicate flowOutOfCallNodeCand1(
|
||||
DataFlowCall call, RetNodeEx ret, NodeEx out, boolean allowsFieldFlow, Configuration config
|
||||
DataFlowCall call, RetNodeEx ret, ReturnKindExt kind, NodeEx out, boolean allowsFieldFlow,
|
||||
Configuration config
|
||||
) {
|
||||
flowOutOfCallNodeCand1(call, ret, out, config) and
|
||||
flowOutOfCallNodeCand1(call, ret, kind, out, pragma[only_bind_into](config)) and
|
||||
exists(int b, int j |
|
||||
b = branch(ret, config) and
|
||||
j = join(out, config) and
|
||||
b = branch(ret, pragma[only_bind_into](config)) and
|
||||
j = join(out, pragma[only_bind_into](config)) and
|
||||
if b.minimum(j) <= config.fieldFlowBranchLimit()
|
||||
then allowsFieldFlow = true
|
||||
else allowsFieldFlow = false
|
||||
@@ -1136,10 +1174,10 @@ pragma[nomagic]
|
||||
private predicate flowIntoCallNodeCand1(
|
||||
DataFlowCall call, ArgNodeEx arg, ParamNodeEx p, boolean allowsFieldFlow, Configuration config
|
||||
) {
|
||||
flowIntoCallNodeCand1(call, arg, p, config) and
|
||||
flowIntoCallNodeCand1(call, arg, p, pragma[only_bind_into](config)) and
|
||||
exists(int b, int j |
|
||||
b = branch(arg, config) and
|
||||
j = join(p, config) and
|
||||
b = branch(arg, pragma[only_bind_into](config)) and
|
||||
j = join(p, pragma[only_bind_into](config)) and
|
||||
if b.minimum(j) <= config.fieldFlowBranchLimit()
|
||||
then allowsFieldFlow = true
|
||||
else allowsFieldFlow = false
|
||||
@@ -1156,7 +1194,9 @@ private signature module StageSig {
|
||||
|
||||
predicate callMayFlowThroughRev(DataFlowCall call, Configuration config);
|
||||
|
||||
predicate parameterMayFlowThrough(ParamNodeEx p, DataFlowCallable c, Ap ap, Configuration config);
|
||||
predicate parameterMayFlowThrough(ParamNodeEx p, Ap ap, Configuration config);
|
||||
|
||||
predicate returnMayFlowThrough(RetNodeEx ret, ReturnKindExt kind, Configuration config);
|
||||
|
||||
predicate storeStepCand(
|
||||
NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, DataFlowType contentType,
|
||||
@@ -1222,7 +1262,8 @@ private module MkStage<StageSig PrevStage> {
|
||||
);
|
||||
|
||||
predicate flowOutOfCall(
|
||||
DataFlowCall call, RetNodeEx ret, NodeEx out, boolean allowsFieldFlow, Configuration config
|
||||
DataFlowCall call, RetNodeEx ret, ReturnKindExt kind, NodeEx out, boolean allowsFieldFlow,
|
||||
Configuration config
|
||||
);
|
||||
|
||||
predicate flowIntoCall(
|
||||
@@ -1247,14 +1288,14 @@ private module MkStage<StageSig PrevStage> {
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate flowThroughOutOfCall(
|
||||
DataFlowCall call, CcCall ccc, RetNodeEx ret, NodeEx out, boolean allowsFieldFlow,
|
||||
Configuration config
|
||||
DataFlowCall call, DataFlowCallable c, CcCall ccc, RetNodeEx ret, ReturnKindExt kind,
|
||||
NodeEx out, boolean allowsFieldFlow, Configuration config
|
||||
) {
|
||||
flowOutOfCall(call, ret, out, allowsFieldFlow, pragma[only_bind_into](config)) and
|
||||
flowOutOfCall(call, ret, kind, out, allowsFieldFlow, pragma[only_bind_into](config)) and
|
||||
PrevStage::callMayFlowThroughRev(call, pragma[only_bind_into](config)) and
|
||||
PrevStage::parameterMayFlowThrough(_, ret.getEnclosingCallable(), _,
|
||||
pragma[only_bind_into](config)) and
|
||||
matchesCall(ccc, call)
|
||||
PrevStage::returnMayFlowThrough(ret, kind, pragma[only_bind_into](config)) and
|
||||
matchesCall(ccc, call) and
|
||||
c = ret.getEnclosingCallable()
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1262,29 +1303,32 @@ private module MkStage<StageSig PrevStage> {
|
||||
* configuration `config`.
|
||||
*
|
||||
* The call context `cc` records whether the node is reached through an
|
||||
* argument in a call, and if so, `argAp` records the access path of that
|
||||
* argument.
|
||||
* argument in a call, and if so, `summaryCtx` and `argAp` record the
|
||||
* corresponding parameter position and access path of that argument, respectively.
|
||||
*/
|
||||
pragma[nomagic]
|
||||
additional predicate fwdFlow(
|
||||
NodeEx node, FlowState state, Cc cc, ApOption argAp, Ap ap, Configuration config
|
||||
NodeEx node, FlowState state, Cc cc, ParameterPositionOption summaryCtx, ApOption argAp,
|
||||
Ap ap, Configuration config
|
||||
) {
|
||||
fwdFlow0(node, state, cc, argAp, ap, config) and
|
||||
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, ApOption argAp, Ap ap, Configuration config
|
||||
NodeEx node, FlowState state, Cc cc, ParameterPositionOption summaryCtx, ApOption argAp,
|
||||
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)
|
||||
or
|
||||
exists(NodeEx mid, FlowState state0, Ap ap0, LocalCc localCc |
|
||||
fwdFlow(mid, state0, cc, argAp, ap0, config) and
|
||||
fwdFlow(mid, state0, cc, summaryCtx, argAp, ap0, config) and
|
||||
localCc = getLocalCc(mid, cc)
|
||||
|
|
||||
localStep(mid, state0, node, state, true, _, config, localCc) and
|
||||
@@ -1295,65 +1339,82 @@ private module MkStage<StageSig PrevStage> {
|
||||
)
|
||||
or
|
||||
exists(NodeEx mid |
|
||||
fwdFlow(mid, pragma[only_bind_into](state), _, _, ap, 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
|
||||
argAp = apNone()
|
||||
)
|
||||
or
|
||||
exists(NodeEx mid, ApNil nil |
|
||||
fwdFlow(mid, state, _, _, nil, pragma[only_bind_into](config)) and
|
||||
fwdFlow(mid, state, _, _, _, nil, pragma[only_bind_into](config)) and
|
||||
additionalJumpStep(mid, node, config) and
|
||||
cc = ccNone() and
|
||||
summaryCtx = TParameterPositionNone() and
|
||||
argAp = apNone() and
|
||||
ap = getApNil(node)
|
||||
)
|
||||
or
|
||||
exists(NodeEx mid, FlowState state0, ApNil nil |
|
||||
fwdFlow(mid, state0, _, _, nil, pragma[only_bind_into](config)) and
|
||||
fwdFlow(mid, state0, _, _, _, nil, pragma[only_bind_into](config)) and
|
||||
additionalJumpStateStep(mid, state0, node, state, config) and
|
||||
cc = ccNone() and
|
||||
summaryCtx = TParameterPositionNone() and
|
||||
argAp = apNone() and
|
||||
ap = getApNil(node)
|
||||
)
|
||||
or
|
||||
// store
|
||||
exists(TypedContent tc, Ap ap0 |
|
||||
fwdFlowStore(_, ap0, tc, node, state, cc, argAp, config) and
|
||||
fwdFlowStore(_, ap0, tc, node, state, cc, summaryCtx, argAp, config) and
|
||||
ap = apCons(tc, ap0)
|
||||
)
|
||||
or
|
||||
// read
|
||||
exists(Ap ap0, Content c |
|
||||
fwdFlowRead(ap0, c, _, node, state, cc, argAp, config) and
|
||||
fwdFlowRead(ap0, c, _, node, state, cc, summaryCtx, argAp, config) and
|
||||
fwdFlowConsCand(ap0, c, ap, config)
|
||||
)
|
||||
or
|
||||
// flow into a callable
|
||||
exists(ApApprox apa |
|
||||
fwdFlowIn(_, node, state, _, cc, _, ap, config) and
|
||||
fwdFlowIn(_, node, state, _, cc, _, _, ap, config) and
|
||||
apa = getApprox(ap) and
|
||||
if PrevStage::parameterMayFlowThrough(node, _, apa, config)
|
||||
then argAp = apSome(ap)
|
||||
else argAp = apNone()
|
||||
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
|
||||
fwdFlowOutNotFromArg(node, state, cc, argAp, ap, config)
|
||||
exists(
|
||||
DataFlowCall call, RetNodeEx ret, boolean allowsFieldFlow, CcNoCall innercc,
|
||||
DataFlowCallable inner
|
||||
|
|
||||
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()
|
||||
)
|
||||
or
|
||||
exists(DataFlowCall call, Ap argAp0 |
|
||||
fwdFlowOutFromArg(call, node, state, argAp0, ap, config) and
|
||||
fwdFlowIsEntered(call, cc, argAp, argAp0, config)
|
||||
// flow through a callable
|
||||
exists(DataFlowCall call, ParameterPosition summaryCtx0, Ap argAp0 |
|
||||
fwdFlowOutFromArg(call, node, state, summaryCtx0, argAp0, ap, config) and
|
||||
fwdFlowIsEntered(call, cc, summaryCtx, argAp, summaryCtx0, argAp0, config)
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate fwdFlowStore(
|
||||
NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, FlowState state, Cc cc, ApOption argAp,
|
||||
Configuration config
|
||||
NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, FlowState state, Cc cc,
|
||||
ParameterPositionOption summaryCtx, ApOption argAp, Configuration config
|
||||
) {
|
||||
exists(DataFlowType contentType |
|
||||
fwdFlow(node1, state, cc, argAp, ap1, config) and
|
||||
fwdFlow(node1, state, cc, summaryCtx, argAp, ap1, config) and
|
||||
PrevStage::storeStepCand(node1, unbindApa(getApprox(ap1)), tc, node2, contentType, config) and
|
||||
typecheckStore(ap1, contentType)
|
||||
)
|
||||
@@ -1366,7 +1427,7 @@ private module MkStage<StageSig PrevStage> {
|
||||
pragma[nomagic]
|
||||
private predicate fwdFlowConsCand(Ap cons, Content c, Ap tail, Configuration config) {
|
||||
exists(TypedContent tc |
|
||||
fwdFlowStore(_, tail, tc, _, _, _, _, config) and
|
||||
fwdFlowStore(_, tail, tc, _, _, _, _, _, config) and
|
||||
tc.getContent() = c and
|
||||
cons = apCons(tc, tail)
|
||||
)
|
||||
@@ -1374,21 +1435,21 @@ private module MkStage<StageSig PrevStage> {
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate fwdFlowRead(
|
||||
Ap ap, Content c, NodeEx node1, NodeEx node2, FlowState state, Cc cc, ApOption argAp,
|
||||
Configuration config
|
||||
Ap ap, Content c, NodeEx node1, NodeEx node2, FlowState state, Cc cc,
|
||||
ParameterPositionOption summaryCtx, ApOption argAp, Configuration config
|
||||
) {
|
||||
fwdFlow(node1, state, cc, 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, Cc innercc, ApOption argAp,
|
||||
Ap ap, 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, argAp, ap, 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()
|
||||
@@ -1396,29 +1457,19 @@ private module MkStage<StageSig PrevStage> {
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate fwdFlowOutNotFromArg(
|
||||
NodeEx out, FlowState state, Cc ccOut, ApOption argAp, Ap ap, Configuration config
|
||||
private predicate fwdFlowOutFromArg(
|
||||
DataFlowCall call, NodeEx out, FlowState state, ParameterPosition summaryCtx, Ap argAp, Ap ap,
|
||||
Configuration config
|
||||
) {
|
||||
exists(
|
||||
DataFlowCall call, RetNodeEx ret, boolean allowsFieldFlow, CcNoCall innercc,
|
||||
DataFlowCallable inner
|
||||
DataFlowCallable c, RetNodeEx ret, ReturnKindExt kind, boolean allowsFieldFlow, CcCall ccc
|
||||
|
|
||||
fwdFlow(ret, state, innercc, argAp, ap, config) and
|
||||
flowOutOfCall(call, ret, out, allowsFieldFlow, config) and
|
||||
inner = ret.getEnclosingCallable() and
|
||||
ccOut = getCallContextReturn(inner, call, innercc) and
|
||||
if allowsFieldFlow = false then ap instanceof ApNil else any()
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate fwdFlowOutFromArg(
|
||||
DataFlowCall call, NodeEx out, FlowState state, Ap argAp, Ap ap, Configuration config
|
||||
) {
|
||||
exists(RetNodeEx ret, boolean allowsFieldFlow, CcCall ccc |
|
||||
fwdFlow(ret, state, ccc, apSome(argAp), ap, config) and
|
||||
flowThroughOutOfCall(call, ccc, ret, out, allowsFieldFlow, config) and
|
||||
if allowsFieldFlow = false then ap instanceof ApNil else any()
|
||||
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
|
||||
(if allowsFieldFlow = false then ap instanceof ApNil else any()) and
|
||||
parameterFlowThroughAllowed(c, pragma[only_bind_into](summaryCtx), kind)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1428,11 +1479,13 @@ private module MkStage<StageSig PrevStage> {
|
||||
*/
|
||||
pragma[nomagic]
|
||||
private predicate fwdFlowIsEntered(
|
||||
DataFlowCall call, Cc cc, ApOption argAp, Ap ap, Configuration config
|
||||
DataFlowCall call, Cc cc, ParameterPositionOption summaryCtx, ApOption argAp,
|
||||
ParameterPosition pos, Ap ap, Configuration config
|
||||
) {
|
||||
exists(ParamNodeEx p |
|
||||
fwdFlowIn(call, p, _, cc, _, argAp, ap, config) and
|
||||
PrevStage::parameterMayFlowThrough(p, _, unbindApa(getApprox(ap)), config)
|
||||
exists(ParamNodeEx param |
|
||||
fwdFlowIn(call, param, _, cc, _, summaryCtx, argAp, ap, config) and
|
||||
PrevStage::parameterMayFlowThrough(param, unbindApa(getApprox(ap)), config) and
|
||||
pos = param.getPosition()
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1440,27 +1493,40 @@ private module MkStage<StageSig PrevStage> {
|
||||
private predicate storeStepFwd(
|
||||
NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, Ap ap2, Configuration config
|
||||
) {
|
||||
fwdFlowStore(node1, ap1, tc, node2, _, _, _, config) and
|
||||
fwdFlowStore(node1, ap1, tc, node2, _, _, _, _, config) and
|
||||
ap2 = apCons(tc, ap1) and
|
||||
fwdFlowRead(ap2, tc.getContent(), _, _, _, _, _, config)
|
||||
fwdFlowRead(ap2, tc.getContent(), _, _, _, _, _, _, config)
|
||||
}
|
||||
|
||||
private predicate readStepFwd(
|
||||
NodeEx n1, Ap ap1, Content c, NodeEx n2, Ap ap2, Configuration config
|
||||
) {
|
||||
fwdFlowRead(ap1, c, n1, n2, _, _, _, config) and
|
||||
fwdFlowRead(ap1, c, n1, n2, _, _, _, _, config) and
|
||||
fwdFlowConsCand(ap1, c, ap2, config)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate callMayFlowThroughFwd(DataFlowCall call, Configuration config) {
|
||||
exists(Ap argAp0, NodeEx out, FlowState state, Cc cc, ApOption argAp, Ap ap |
|
||||
fwdFlow(out, state, pragma[only_bind_into](cc), pragma[only_bind_into](argAp), ap,
|
||||
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
|
||||
fwdFlowOutFromArg(call, out, state, argAp0, ap, config) and
|
||||
fwdFlowIsEntered(pragma[only_bind_into](call), pragma[only_bind_into](cc),
|
||||
pragma[only_bind_into](argAp), pragma[only_bind_into](argAp0),
|
||||
pragma[only_bind_into](config))
|
||||
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(DataFlowCallable c, ParameterPosition ppos |
|
||||
returnFlowsThrough0(ret, kind, state, ccc, c, ppos, argAp, ap, config) and
|
||||
p.isParameterOf(c, ppos) and
|
||||
parameterFlowThroughAllowed(p, kind)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1468,118 +1534,130 @@ private module MkStage<StageSig PrevStage> {
|
||||
private predicate flowThroughIntoCall(
|
||||
DataFlowCall call, ArgNodeEx arg, ParamNodeEx p, boolean allowsFieldFlow, Configuration config
|
||||
) {
|
||||
flowIntoCall(call, arg, p, allowsFieldFlow, config) and
|
||||
fwdFlow(arg, _, _, _, _, pragma[only_bind_into](config)) and
|
||||
PrevStage::parameterMayFlowThrough(p, _, _, pragma[only_bind_into](config)) and
|
||||
callMayFlowThroughFwd(call, pragma[only_bind_into](config))
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate returnNodeMayFlowThrough(
|
||||
RetNodeEx ret, FlowState state, Ap ap, Configuration config
|
||||
) {
|
||||
fwdFlow(ret, state, any(CcCall ccc), apSome(_), ap, config)
|
||||
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))
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `node` with access path `ap` is part of a path from a source to a
|
||||
* sink in the configuration `config`.
|
||||
*
|
||||
* The Boolean `toReturn` records whether the node must be returned from the
|
||||
* enclosing callable in order to reach a sink, and if so, `returnAp` records
|
||||
* the access path of the returned value.
|
||||
* The parameter `returnCtx` records whether (and how) the node must be returned
|
||||
* from the enclosing callable in order to reach a sink, and if so, `returnAp`
|
||||
* records the access path of the returned value.
|
||||
*/
|
||||
pragma[nomagic]
|
||||
additional predicate revFlow(
|
||||
NodeEx node, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, Configuration config
|
||||
NodeEx node, FlowState state, ReturnCtx returnCtx, ApOption returnAp, Ap ap,
|
||||
Configuration config
|
||||
) {
|
||||
revFlow0(node, state, toReturn, returnAp, ap, config) and
|
||||
fwdFlow(node, state, _, _, ap, config)
|
||||
revFlow0(node, state, returnCtx, returnAp, ap, config) and
|
||||
fwdFlow(node, state, _, _, _, ap, config)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate revFlow0(
|
||||
NodeEx node, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, Configuration config
|
||||
NodeEx node, FlowState state, ReturnCtx returnCtx, ApOption returnAp, Ap ap,
|
||||
Configuration config
|
||||
) {
|
||||
fwdFlow(node, state, _, _, ap, config) and
|
||||
fwdFlow(node, state, _, _, _, ap, config) and
|
||||
sinkNode(node, state, config) and
|
||||
(if hasSinkCallCtx(config) then toReturn = true else toReturn = false) and
|
||||
(
|
||||
if hasSinkCallCtx(config)
|
||||
then returnCtx = TReturnCtxNoFlowThrough()
|
||||
else returnCtx = TReturnCtxNone()
|
||||
) and
|
||||
returnAp = apNone() and
|
||||
ap instanceof ApNil
|
||||
or
|
||||
exists(NodeEx mid, FlowState state0 |
|
||||
localStep(node, state, mid, state0, true, _, config, _) and
|
||||
revFlow(mid, state0, toReturn, returnAp, ap, config)
|
||||
revFlow(mid, state0, returnCtx, returnAp, ap, config)
|
||||
)
|
||||
or
|
||||
exists(NodeEx mid, FlowState state0, ApNil nil |
|
||||
fwdFlow(node, pragma[only_bind_into](state), _, _, ap, pragma[only_bind_into](config)) and
|
||||
fwdFlow(node, pragma[only_bind_into](state), _, _, _, ap, pragma[only_bind_into](config)) and
|
||||
localStep(node, pragma[only_bind_into](state), mid, state0, false, _, config, _) and
|
||||
revFlow(mid, state0, toReturn, returnAp, nil, pragma[only_bind_into](config)) and
|
||||
revFlow(mid, state0, returnCtx, returnAp, nil, pragma[only_bind_into](config)) and
|
||||
ap instanceof ApNil
|
||||
)
|
||||
or
|
||||
exists(NodeEx mid |
|
||||
jumpStep(node, mid, config) and
|
||||
revFlow(mid, state, _, _, ap, config) and
|
||||
toReturn = false and
|
||||
returnCtx = TReturnCtxNone() and
|
||||
returnAp = apNone()
|
||||
)
|
||||
or
|
||||
exists(NodeEx mid, ApNil nil |
|
||||
fwdFlow(node, _, _, _, ap, pragma[only_bind_into](config)) and
|
||||
fwdFlow(node, _, _, _, _, ap, pragma[only_bind_into](config)) and
|
||||
additionalJumpStep(node, mid, config) and
|
||||
revFlow(pragma[only_bind_into](mid), state, _, _, nil, pragma[only_bind_into](config)) and
|
||||
toReturn = false and
|
||||
returnCtx = TReturnCtxNone() and
|
||||
returnAp = apNone() and
|
||||
ap instanceof ApNil
|
||||
)
|
||||
or
|
||||
exists(NodeEx mid, FlowState state0, ApNil nil |
|
||||
fwdFlow(node, _, _, _, ap, pragma[only_bind_into](config)) and
|
||||
fwdFlow(node, _, _, _, _, ap, pragma[only_bind_into](config)) and
|
||||
additionalJumpStateStep(node, state, mid, state0, config) and
|
||||
revFlow(pragma[only_bind_into](mid), pragma[only_bind_into](state0), _, _, nil,
|
||||
pragma[only_bind_into](config)) and
|
||||
toReturn = false and
|
||||
returnCtx = TReturnCtxNone() and
|
||||
returnAp = apNone() and
|
||||
ap instanceof ApNil
|
||||
)
|
||||
or
|
||||
// store
|
||||
exists(Ap ap0, Content c |
|
||||
revFlowStore(ap0, c, ap, node, state, _, _, toReturn, returnAp, config) and
|
||||
revFlowStore(ap0, c, ap, node, state, _, _, returnCtx, returnAp, config) and
|
||||
revFlowConsCand(ap0, c, ap, config)
|
||||
)
|
||||
or
|
||||
// read
|
||||
exists(NodeEx mid, Ap ap0 |
|
||||
revFlow(mid, state, toReturn, returnAp, ap0, config) and
|
||||
revFlow(mid, state, returnCtx, returnAp, ap0, config) and
|
||||
readStepFwd(node, ap, _, mid, ap0, config)
|
||||
)
|
||||
or
|
||||
// flow into a callable
|
||||
revFlowInNotToReturn(node, state, returnAp, ap, config) and
|
||||
toReturn = false
|
||||
exists(ParamNodeEx p, boolean allowsFieldFlow |
|
||||
revFlow(p, state, TReturnCtxNone(), returnAp, ap, config) and
|
||||
flowIntoCall(_, node, p, allowsFieldFlow, config) and
|
||||
(if allowsFieldFlow = false then ap instanceof ApNil else any()) and
|
||||
returnCtx = TReturnCtxNone()
|
||||
)
|
||||
or
|
||||
exists(DataFlowCall call, Ap returnAp0 |
|
||||
revFlowInToReturn(call, node, state, returnAp0, ap, config) and
|
||||
revFlowIsReturned(call, toReturn, returnAp, returnAp0, config)
|
||||
// flow through a callable
|
||||
exists(DataFlowCall call, ReturnKindExt returnKind0, Ap returnAp0 |
|
||||
revFlowInToReturn(call, node, state, returnKind0, returnAp0, ap, config) and
|
||||
revFlowIsReturned(call, returnCtx, returnAp, returnKind0, returnAp0, config)
|
||||
)
|
||||
or
|
||||
// flow out of a callable
|
||||
revFlowOut(_, node, state, _, _, ap, config) and
|
||||
toReturn = true and
|
||||
if returnNodeMayFlowThrough(node, state, ap, config)
|
||||
then returnAp = apSome(ap)
|
||||
else returnAp = apNone()
|
||||
exists(ReturnKindExt kind |
|
||||
revFlowOut(_, node, kind, state, _, _, ap, config) and
|
||||
if returnFlowsThrough(node, kind, state, _, _, _, ap, config)
|
||||
then (
|
||||
returnCtx = TReturnCtxMaybeFlowThrough(kind) and
|
||||
returnAp = apSome(ap)
|
||||
) else (
|
||||
returnCtx = TReturnCtxNoFlowThrough() and returnAp = apNone()
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate revFlowStore(
|
||||
Ap ap0, Content c, Ap ap, NodeEx node, FlowState state, TypedContent tc, NodeEx mid,
|
||||
boolean toReturn, ApOption returnAp, Configuration config
|
||||
ReturnCtx returnCtx, ApOption returnAp, Configuration config
|
||||
) {
|
||||
revFlow(mid, state, toReturn, returnAp, ap0, config) and
|
||||
revFlow(mid, state, returnCtx, returnAp, ap0, config) and
|
||||
storeStepFwd(node, ap, tc, mid, ap0, config) and
|
||||
tc.getContent() = c
|
||||
}
|
||||
@@ -1599,35 +1677,27 @@ private module MkStage<StageSig PrevStage> {
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate revFlowOut(
|
||||
DataFlowCall call, RetNodeEx ret, FlowState state, boolean toReturn, ApOption returnAp, Ap ap,
|
||||
Configuration config
|
||||
DataFlowCall call, RetNodeEx ret, ReturnKindExt kind, FlowState state, ReturnCtx returnCtx,
|
||||
ApOption returnAp, Ap ap, Configuration config
|
||||
) {
|
||||
exists(NodeEx out, boolean allowsFieldFlow |
|
||||
revFlow(out, state, toReturn, returnAp, ap, config) and
|
||||
flowOutOfCall(call, ret, out, allowsFieldFlow, config) and
|
||||
if allowsFieldFlow = false then ap instanceof ApNil else any()
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate revFlowInNotToReturn(
|
||||
ArgNodeEx arg, FlowState state, ApOption returnAp, Ap ap, Configuration config
|
||||
) {
|
||||
exists(ParamNodeEx p, boolean allowsFieldFlow |
|
||||
revFlow(p, state, false, returnAp, ap, config) and
|
||||
flowIntoCall(_, arg, p, allowsFieldFlow, config) and
|
||||
revFlow(out, state, returnCtx, returnAp, ap, config) and
|
||||
flowOutOfCall(call, ret, kind, out, allowsFieldFlow, config) and
|
||||
if allowsFieldFlow = false then ap instanceof ApNil else any()
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate revFlowInToReturn(
|
||||
DataFlowCall call, ArgNodeEx arg, FlowState state, Ap returnAp, Ap ap, Configuration config
|
||||
DataFlowCall call, ArgNodeEx arg, FlowState state, ReturnKindExt kind, Ap returnAp, Ap ap,
|
||||
Configuration config
|
||||
) {
|
||||
exists(ParamNodeEx p, boolean allowsFieldFlow |
|
||||
revFlow(p, state, true, apSome(returnAp), ap, config) and
|
||||
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()
|
||||
(if allowsFieldFlow = false then ap instanceof ApNil else any()) and
|
||||
parameterFlowThroughAllowed(p, kind)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1638,11 +1708,12 @@ private module MkStage<StageSig PrevStage> {
|
||||
*/
|
||||
pragma[nomagic]
|
||||
private predicate revFlowIsReturned(
|
||||
DataFlowCall call, boolean toReturn, ApOption returnAp, Ap ap, Configuration config
|
||||
DataFlowCall call, ReturnCtx returnCtx, ApOption returnAp, ReturnKindExt kind, Ap ap,
|
||||
Configuration config
|
||||
) {
|
||||
exists(RetNodeEx ret, FlowState state, CcCall ccc |
|
||||
revFlowOut(call, ret, state, toReturn, returnAp, ap, config) and
|
||||
fwdFlow(ret, state, ccc, apSome(_), ap, config) and
|
||||
revFlowOut(call, ret, kind, state, returnCtx, returnAp, ap, config) and
|
||||
returnFlowsThrough(ret, kind, state, ccc, _, _, ap, config) and
|
||||
matchesCall(ccc, call)
|
||||
)
|
||||
}
|
||||
@@ -1713,40 +1784,39 @@ private module MkStage<StageSig PrevStage> {
|
||||
validAp(ap, config)
|
||||
}
|
||||
|
||||
pragma[noinline]
|
||||
private predicate parameterFlow(
|
||||
ParamNodeEx p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config
|
||||
pragma[nomagic]
|
||||
private predicate parameterFlowsThroughRev(
|
||||
ParamNodeEx p, Ap ap, ReturnKindExt kind, Configuration config
|
||||
) {
|
||||
revFlow(p, _, true, apSome(ap0), ap, config) and
|
||||
c = p.getEnclosingCallable()
|
||||
revFlow(p, _, TReturnCtxMaybeFlowThrough(kind), apSome(_), ap, config) and
|
||||
parameterFlowThroughAllowed(p, kind)
|
||||
}
|
||||
|
||||
predicate parameterMayFlowThrough(ParamNodeEx p, DataFlowCallable c, Ap ap, Configuration config) {
|
||||
exists(RetNodeEx ret, FlowState state, Ap ap0, ReturnKindExt kind, ParameterPosition pos |
|
||||
parameterFlow(p, ap, ap0, c, config) and
|
||||
c = ret.getEnclosingCallable() and
|
||||
revFlow(pragma[only_bind_into](ret), pragma[only_bind_into](state), true, apSome(_),
|
||||
pragma[only_bind_into](ap0), pragma[only_bind_into](config)) and
|
||||
fwdFlow(ret, state, any(CcCall ccc), apSome(ap), ap0, config) and
|
||||
kind = ret.getKind() and
|
||||
p.getPosition() = pos and
|
||||
// we don't expect a parameter to return stored in itself, unless explicitly allowed
|
||||
(
|
||||
not kind.(ParamUpdateReturnKind).getPosition() = pos
|
||||
or
|
||||
p.allowParameterReturnInSelf()
|
||||
)
|
||||
pragma[nomagic]
|
||||
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)
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
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(
|
||||
Ap returnAp0, ArgNodeEx arg, FlowState state, boolean toReturn, ApOption returnAp, Ap ap
|
||||
ReturnKindExt returnKind0, Ap returnAp0, ArgNodeEx arg, FlowState state,
|
||||
ReturnCtx returnCtx, ApOption returnAp, Ap ap
|
||||
|
|
||||
revFlow(arg, state, toReturn, returnAp, ap, config) and
|
||||
revFlowInToReturn(call, arg, state, returnAp0, ap, config) and
|
||||
revFlowIsReturned(call, toReturn, returnAp, returnAp0, config)
|
||||
revFlow(arg, state, returnCtx, returnAp, ap, config) and
|
||||
revFlowInToReturn(call, arg, state, returnKind0, returnAp0, ap, config) and
|
||||
revFlowIsReturned(call, returnCtx, returnAp, returnKind0, returnAp0, config)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1754,14 +1824,13 @@ private module MkStage<StageSig PrevStage> {
|
||||
boolean fwd, int nodes, int fields, int conscand, int states, int tuples, Configuration config
|
||||
) {
|
||||
fwd = true and
|
||||
nodes = count(NodeEx node | fwdFlow(node, _, _, _, _, config)) and
|
||||
nodes = count(NodeEx node | fwdFlow(node, _, _, _, _, _, config)) and
|
||||
fields = count(TypedContent f0 | fwdConsCand(f0, _, config)) and
|
||||
conscand = count(TypedContent f0, Ap ap | fwdConsCand(f0, ap, config)) and
|
||||
states = count(FlowState state | fwdFlow(_, state, _, _, _, config)) and
|
||||
states = count(FlowState state | fwdFlow(_, state, _, _, _, _, config)) and
|
||||
tuples =
|
||||
count(NodeEx n, FlowState state, Cc cc, ApOption argAp, Ap ap |
|
||||
fwdFlow(n, state, cc, argAp, ap, config)
|
||||
)
|
||||
count(NodeEx n, FlowState state, Cc cc, ParameterPositionOption summaryCtx, ApOption argAp,
|
||||
Ap ap | fwdFlow(n, state, cc, summaryCtx, argAp, ap, config))
|
||||
or
|
||||
fwd = false and
|
||||
nodes = count(NodeEx node | revFlow(node, _, _, _, _, config)) and
|
||||
@@ -1769,8 +1838,8 @@ private module MkStage<StageSig PrevStage> {
|
||||
conscand = count(TypedContent f0, Ap ap | consCand(f0, ap, config)) and
|
||||
states = count(FlowState state | revFlow(_, state, _, _, _, config)) and
|
||||
tuples =
|
||||
count(NodeEx n, FlowState state, boolean b, ApOption retAp, Ap ap |
|
||||
revFlow(n, state, b, retAp, ap, config)
|
||||
count(NodeEx n, FlowState state, ReturnCtx returnCtx, ApOption retAp, Ap ap |
|
||||
revFlow(n, state, returnCtx, retAp, ap, config)
|
||||
)
|
||||
}
|
||||
/* End: Stage logic. */
|
||||
@@ -1915,7 +1984,7 @@ private module Stage2Param implements MkStage<Stage1>::StageParam {
|
||||
exists(lcc)
|
||||
}
|
||||
|
||||
predicate flowOutOfCall = flowOutOfCallNodeCand1/5;
|
||||
predicate flowOutOfCall = flowOutOfCallNodeCand1/6;
|
||||
|
||||
predicate flowIntoCall = flowIntoCallNodeCand1/5;
|
||||
|
||||
@@ -1951,9 +2020,10 @@ private module Stage2 implements StageSig {
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate flowOutOfCallNodeCand2(
|
||||
DataFlowCall call, RetNodeEx node1, NodeEx node2, boolean allowsFieldFlow, Configuration config
|
||||
DataFlowCall call, RetNodeEx node1, ReturnKindExt kind, NodeEx node2, boolean allowsFieldFlow,
|
||||
Configuration config
|
||||
) {
|
||||
flowOutOfCallNodeCand1(call, node1, node2, allowsFieldFlow, config) and
|
||||
flowOutOfCallNodeCand1(call, node1, kind, node2, allowsFieldFlow, config) and
|
||||
Stage2::revFlow(node2, pragma[only_bind_into](config)) and
|
||||
Stage2::revFlowAlias(node1, pragma[only_bind_into](config))
|
||||
}
|
||||
@@ -2021,8 +2091,8 @@ private module LocalFlowBigStep {
|
||||
exists(NodeEx next | Stage2::revFlow(next, state, config) |
|
||||
jumpStep(node, next, config) or
|
||||
additionalJumpStep(node, next, config) or
|
||||
flowIntoCallNodeCand1(_, node, next, config) or
|
||||
flowOutOfCallNodeCand1(_, node, next, config) or
|
||||
flowIntoCallNodeCand2(_, node, next, _, config) or
|
||||
flowOutOfCallNodeCand2(_, node, _, next, _, config) or
|
||||
Stage2::storeStepCand(node, _, _, next, _, config) or
|
||||
Stage2::readStepCand(node, _, next, config)
|
||||
)
|
||||
@@ -2163,7 +2233,7 @@ private module Stage3Param implements MkStage<Stage2>::StageParam {
|
||||
localFlowBigStep(node1, state1, node2, state2, preservesValue, ap, config, _) and exists(lcc)
|
||||
}
|
||||
|
||||
predicate flowOutOfCall = flowOutOfCallNodeCand2/5;
|
||||
predicate flowOutOfCall = flowOutOfCallNodeCand2/6;
|
||||
|
||||
predicate flowIntoCall = flowIntoCallNodeCand2/5;
|
||||
|
||||
@@ -2233,8 +2303,9 @@ private predicate flowCandSummaryCtx(
|
||||
NodeEx node, FlowState state, AccessPathFront argApf, Configuration config
|
||||
) {
|
||||
exists(AccessPathFront apf |
|
||||
Stage3::revFlow(node, state, true, _, apf, config) and
|
||||
Stage3::fwdFlow(node, state, any(Stage3::CcCall ccc), TAccessPathFrontSome(argApf), apf, config)
|
||||
Stage3::revFlow(node, state, TReturnCtxMaybeFlowThrough(_), _, apf, config) and
|
||||
Stage3::fwdFlow(node, state, any(Stage3::CcCall ccc), _, TAccessPathFrontSome(argApf), apf,
|
||||
config)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -2468,10 +2539,11 @@ private module Stage4Param implements MkStage<Stage3>::StageParam {
|
||||
|
||||
pragma[nomagic]
|
||||
predicate flowOutOfCall(
|
||||
DataFlowCall call, RetNodeEx node1, NodeEx node2, boolean allowsFieldFlow, Configuration config
|
||||
DataFlowCall call, RetNodeEx node1, ReturnKindExt kind, NodeEx node2, boolean allowsFieldFlow,
|
||||
Configuration config
|
||||
) {
|
||||
exists(FlowState state |
|
||||
flowOutOfCallNodeCand2(call, node1, node2, allowsFieldFlow, config) and
|
||||
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))
|
||||
@@ -2508,13 +2580,14 @@ private Configuration unbindConf(Configuration conf) {
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate nodeMayUseSummary0(
|
||||
NodeEx n, DataFlowCallable c, FlowState state, AccessPathApprox apa, Configuration config
|
||||
NodeEx n, DataFlowCallable c, ParameterPosition pos, FlowState state, AccessPathApprox apa,
|
||||
Configuration config
|
||||
) {
|
||||
exists(AccessPathApprox apa0 |
|
||||
Stage4::parameterMayFlowThrough(_, c, _, _) and
|
||||
Stage4::revFlow(n, state, true, _, apa0, config) and
|
||||
Stage4::fwdFlow(n, state, any(CallContextCall ccc), TAccessPathApproxSome(apa), apa0, config) and
|
||||
n.getEnclosingCallable() = c
|
||||
c = n.getEnclosingCallable() and
|
||||
Stage4::revFlow(n, state, TReturnCtxMaybeFlowThrough(_), _, apa0, config) and
|
||||
Stage4::fwdFlow(n, state, any(CallContextCall ccc), TParameterPositionSome(pos),
|
||||
TAccessPathApproxSome(apa), apa0, config)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -2522,9 +2595,10 @@ pragma[nomagic]
|
||||
private predicate nodeMayUseSummary(
|
||||
NodeEx n, FlowState state, AccessPathApprox apa, Configuration config
|
||||
) {
|
||||
exists(DataFlowCallable c |
|
||||
Stage4::parameterMayFlowThrough(_, c, apa, config) and
|
||||
nodeMayUseSummary0(n, c, state, apa, config)
|
||||
exists(DataFlowCallable c, ParameterPosition pos, ParamNodeEx p |
|
||||
Stage4::parameterMayFlowThrough(p, apa, config) and
|
||||
nodeMayUseSummary0(n, c, pos, state, apa, config) and
|
||||
p.isParameterOf(c, pos)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -2532,7 +2606,7 @@ private newtype TSummaryCtx =
|
||||
TSummaryCtxNone() or
|
||||
TSummaryCtxSome(ParamNodeEx p, FlowState state, AccessPath ap) {
|
||||
exists(Configuration config |
|
||||
Stage4::parameterMayFlowThrough(p, _, ap.getApprox(), config) and
|
||||
Stage4::parameterMayFlowThrough(p, ap.getApprox(), config) and
|
||||
Stage4::revFlow(p, state, _, config)
|
||||
)
|
||||
}
|
||||
@@ -3453,17 +3527,11 @@ private predicate paramFlowsThrough(
|
||||
ReturnKindExt kind, FlowState state, CallContextCall cc, SummaryCtxSome sc, AccessPath ap,
|
||||
AccessPathApprox apa, Configuration config
|
||||
) {
|
||||
exists(PathNodeMid mid, RetNodeEx ret, ParameterPosition pos |
|
||||
exists(PathNodeMid mid, RetNodeEx ret |
|
||||
pathNode(mid, ret, state, cc, sc, ap, config, _) and
|
||||
kind = ret.getKind() and
|
||||
apa = ap.getApprox() and
|
||||
pos = sc.getParameterPos() and
|
||||
// we don't expect a parameter to return stored in itself, unless explicitly allowed
|
||||
(
|
||||
not kind.(ParamUpdateReturnKind).getPosition() = pos
|
||||
or
|
||||
sc.getParamNode().allowParameterReturnInSelf()
|
||||
)
|
||||
parameterFlowThroughAllowed(sc.getParamNode(), kind)
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@@ -70,7 +70,7 @@ abstract class Configuration extends string {
|
||||
/**
|
||||
* Holds if `sink` is a relevant data flow sink accepting `state`.
|
||||
*/
|
||||
predicate isSink(Node source, FlowState state) { none() }
|
||||
predicate isSink(Node sink, FlowState state) { none() }
|
||||
|
||||
/**
|
||||
* Holds if data flow through `node` is prohibited. This completely removes
|
||||
@@ -319,8 +319,6 @@ private class ParamNodeEx extends NodeEx {
|
||||
}
|
||||
|
||||
ParameterPosition getPosition() { this.isParameterOf(_, result) }
|
||||
|
||||
predicate allowParameterReturnInSelf() { allowParameterReturnInSelfCached(this.asNode()) }
|
||||
}
|
||||
|
||||
private class RetNodeEx extends NodeEx {
|
||||
@@ -608,6 +606,38 @@ private predicate hasSinkCallCtx(Configuration config) {
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if flow from `p` to a return node of kind `kind` is allowed.
|
||||
*
|
||||
* We don't expect a parameter to return stored in itself, unless
|
||||
* explicitly allowed
|
||||
*/
|
||||
bindingset[p, kind]
|
||||
private predicate parameterFlowThroughAllowed(ParamNodeEx p, ReturnKindExt kind) {
|
||||
exists(ParameterPosition pos | p.isParameterOf(_, pos) |
|
||||
not kind.(ParamUpdateReturnKind).getPosition() = pos
|
||||
or
|
||||
allowParameterReturnInSelfCached(p.asNode())
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* 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;
|
||||
|
||||
@@ -981,21 +1011,22 @@ private module Stage1 implements StageSig {
|
||||
* candidate for the origin of a summary.
|
||||
*/
|
||||
pragma[nomagic]
|
||||
predicate parameterMayFlowThrough(ParamNodeEx p, DataFlowCallable c, Ap ap, Configuration config) {
|
||||
exists(ReturnKindExt kind |
|
||||
predicate parameterMayFlowThrough(ParamNodeEx p, Ap ap, Configuration config) {
|
||||
exists(DataFlowCallable c, ReturnKindExt kind |
|
||||
throughFlowNodeCand(p, config) and
|
||||
returnFlowCallableNodeCand(c, kind, config) and
|
||||
p.getEnclosingCallable() = c and
|
||||
exists(ap) and
|
||||
// we don't expect a parameter to return stored in itself, unless explicitly allowed
|
||||
(
|
||||
not kind.(ParamUpdateReturnKind).getPosition() = p.getPosition()
|
||||
or
|
||||
p.allowParameterReturnInSelf()
|
||||
)
|
||||
parameterFlowThroughAllowed(p, kind)
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
predicate returnMayFlowThrough(RetNodeEx ret, ReturnKindExt kind, Configuration config) {
|
||||
throughFlowNodeCand(ret, config) and
|
||||
kind = ret.getKind()
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
predicate callMayFlowThroughRev(DataFlowCall call, Configuration config) {
|
||||
exists(ArgNodeEx arg, boolean toReturn |
|
||||
@@ -1052,12 +1083,16 @@ private predicate viableReturnPosOutNodeCand1(
|
||||
*/
|
||||
pragma[nomagic]
|
||||
private predicate flowOutOfCallNodeCand1(
|
||||
DataFlowCall call, RetNodeEx ret, NodeEx out, Configuration config
|
||||
DataFlowCall call, RetNodeEx ret, ReturnKindExt kind, NodeEx out, Configuration config
|
||||
) {
|
||||
viableReturnPosOutNodeCand1(call, ret.getReturnPosition(), out, config) and
|
||||
Stage1::revFlow(ret, config) and
|
||||
not outBarrier(ret, config) and
|
||||
not inBarrier(out, config)
|
||||
exists(ReturnPosition pos |
|
||||
viableReturnPosOutNodeCand1(call, pos, out, config) and
|
||||
pos = ret.getReturnPosition() and
|
||||
kind = pos.getKind() and
|
||||
Stage1::revFlow(ret, config) and
|
||||
not outBarrier(ret, config) and
|
||||
not inBarrier(out, config)
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
@@ -1087,10 +1122,11 @@ private predicate flowIntoCallNodeCand1(
|
||||
* edge in the graph of paths between sources and sinks that ignores call
|
||||
* contexts.
|
||||
*/
|
||||
pragma[nomagic]
|
||||
private int branch(NodeEx n1, Configuration conf) {
|
||||
result =
|
||||
strictcount(NodeEx n |
|
||||
flowOutOfCallNodeCand1(_, n1, n, conf) or flowIntoCallNodeCand1(_, n1, n, conf)
|
||||
flowOutOfCallNodeCand1(_, n1, _, n, conf) or flowIntoCallNodeCand1(_, n1, n, conf)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1099,10 +1135,11 @@ private int branch(NodeEx n1, Configuration conf) {
|
||||
* edge in the graph of paths between sources and sinks that ignores call
|
||||
* contexts.
|
||||
*/
|
||||
pragma[nomagic]
|
||||
private int join(NodeEx n2, Configuration conf) {
|
||||
result =
|
||||
strictcount(NodeEx n |
|
||||
flowOutOfCallNodeCand1(_, n, n2, conf) or flowIntoCallNodeCand1(_, n, n2, conf)
|
||||
flowOutOfCallNodeCand1(_, n, _, n2, conf) or flowIntoCallNodeCand1(_, n, n2, conf)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1115,12 +1152,13 @@ private int join(NodeEx n2, Configuration conf) {
|
||||
*/
|
||||
pragma[nomagic]
|
||||
private predicate flowOutOfCallNodeCand1(
|
||||
DataFlowCall call, RetNodeEx ret, NodeEx out, boolean allowsFieldFlow, Configuration config
|
||||
DataFlowCall call, RetNodeEx ret, ReturnKindExt kind, NodeEx out, boolean allowsFieldFlow,
|
||||
Configuration config
|
||||
) {
|
||||
flowOutOfCallNodeCand1(call, ret, out, config) and
|
||||
flowOutOfCallNodeCand1(call, ret, kind, out, pragma[only_bind_into](config)) and
|
||||
exists(int b, int j |
|
||||
b = branch(ret, config) and
|
||||
j = join(out, config) and
|
||||
b = branch(ret, pragma[only_bind_into](config)) and
|
||||
j = join(out, pragma[only_bind_into](config)) and
|
||||
if b.minimum(j) <= config.fieldFlowBranchLimit()
|
||||
then allowsFieldFlow = true
|
||||
else allowsFieldFlow = false
|
||||
@@ -1136,10 +1174,10 @@ pragma[nomagic]
|
||||
private predicate flowIntoCallNodeCand1(
|
||||
DataFlowCall call, ArgNodeEx arg, ParamNodeEx p, boolean allowsFieldFlow, Configuration config
|
||||
) {
|
||||
flowIntoCallNodeCand1(call, arg, p, config) and
|
||||
flowIntoCallNodeCand1(call, arg, p, pragma[only_bind_into](config)) and
|
||||
exists(int b, int j |
|
||||
b = branch(arg, config) and
|
||||
j = join(p, config) and
|
||||
b = branch(arg, pragma[only_bind_into](config)) and
|
||||
j = join(p, pragma[only_bind_into](config)) and
|
||||
if b.minimum(j) <= config.fieldFlowBranchLimit()
|
||||
then allowsFieldFlow = true
|
||||
else allowsFieldFlow = false
|
||||
@@ -1156,7 +1194,9 @@ private signature module StageSig {
|
||||
|
||||
predicate callMayFlowThroughRev(DataFlowCall call, Configuration config);
|
||||
|
||||
predicate parameterMayFlowThrough(ParamNodeEx p, DataFlowCallable c, Ap ap, Configuration config);
|
||||
predicate parameterMayFlowThrough(ParamNodeEx p, Ap ap, Configuration config);
|
||||
|
||||
predicate returnMayFlowThrough(RetNodeEx ret, ReturnKindExt kind, Configuration config);
|
||||
|
||||
predicate storeStepCand(
|
||||
NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, DataFlowType contentType,
|
||||
@@ -1222,7 +1262,8 @@ private module MkStage<StageSig PrevStage> {
|
||||
);
|
||||
|
||||
predicate flowOutOfCall(
|
||||
DataFlowCall call, RetNodeEx ret, NodeEx out, boolean allowsFieldFlow, Configuration config
|
||||
DataFlowCall call, RetNodeEx ret, ReturnKindExt kind, NodeEx out, boolean allowsFieldFlow,
|
||||
Configuration config
|
||||
);
|
||||
|
||||
predicate flowIntoCall(
|
||||
@@ -1247,14 +1288,14 @@ private module MkStage<StageSig PrevStage> {
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate flowThroughOutOfCall(
|
||||
DataFlowCall call, CcCall ccc, RetNodeEx ret, NodeEx out, boolean allowsFieldFlow,
|
||||
Configuration config
|
||||
DataFlowCall call, DataFlowCallable c, CcCall ccc, RetNodeEx ret, ReturnKindExt kind,
|
||||
NodeEx out, boolean allowsFieldFlow, Configuration config
|
||||
) {
|
||||
flowOutOfCall(call, ret, out, allowsFieldFlow, pragma[only_bind_into](config)) and
|
||||
flowOutOfCall(call, ret, kind, out, allowsFieldFlow, pragma[only_bind_into](config)) and
|
||||
PrevStage::callMayFlowThroughRev(call, pragma[only_bind_into](config)) and
|
||||
PrevStage::parameterMayFlowThrough(_, ret.getEnclosingCallable(), _,
|
||||
pragma[only_bind_into](config)) and
|
||||
matchesCall(ccc, call)
|
||||
PrevStage::returnMayFlowThrough(ret, kind, pragma[only_bind_into](config)) and
|
||||
matchesCall(ccc, call) and
|
||||
c = ret.getEnclosingCallable()
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1262,29 +1303,32 @@ private module MkStage<StageSig PrevStage> {
|
||||
* configuration `config`.
|
||||
*
|
||||
* The call context `cc` records whether the node is reached through an
|
||||
* argument in a call, and if so, `argAp` records the access path of that
|
||||
* argument.
|
||||
* argument in a call, and if so, `summaryCtx` and `argAp` record the
|
||||
* corresponding parameter position and access path of that argument, respectively.
|
||||
*/
|
||||
pragma[nomagic]
|
||||
additional predicate fwdFlow(
|
||||
NodeEx node, FlowState state, Cc cc, ApOption argAp, Ap ap, Configuration config
|
||||
NodeEx node, FlowState state, Cc cc, ParameterPositionOption summaryCtx, ApOption argAp,
|
||||
Ap ap, Configuration config
|
||||
) {
|
||||
fwdFlow0(node, state, cc, argAp, ap, config) and
|
||||
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, ApOption argAp, Ap ap, Configuration config
|
||||
NodeEx node, FlowState state, Cc cc, ParameterPositionOption summaryCtx, ApOption argAp,
|
||||
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)
|
||||
or
|
||||
exists(NodeEx mid, FlowState state0, Ap ap0, LocalCc localCc |
|
||||
fwdFlow(mid, state0, cc, argAp, ap0, config) and
|
||||
fwdFlow(mid, state0, cc, summaryCtx, argAp, ap0, config) and
|
||||
localCc = getLocalCc(mid, cc)
|
||||
|
|
||||
localStep(mid, state0, node, state, true, _, config, localCc) and
|
||||
@@ -1295,65 +1339,82 @@ private module MkStage<StageSig PrevStage> {
|
||||
)
|
||||
or
|
||||
exists(NodeEx mid |
|
||||
fwdFlow(mid, pragma[only_bind_into](state), _, _, ap, 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
|
||||
argAp = apNone()
|
||||
)
|
||||
or
|
||||
exists(NodeEx mid, ApNil nil |
|
||||
fwdFlow(mid, state, _, _, nil, pragma[only_bind_into](config)) and
|
||||
fwdFlow(mid, state, _, _, _, nil, pragma[only_bind_into](config)) and
|
||||
additionalJumpStep(mid, node, config) and
|
||||
cc = ccNone() and
|
||||
summaryCtx = TParameterPositionNone() and
|
||||
argAp = apNone() and
|
||||
ap = getApNil(node)
|
||||
)
|
||||
or
|
||||
exists(NodeEx mid, FlowState state0, ApNil nil |
|
||||
fwdFlow(mid, state0, _, _, nil, pragma[only_bind_into](config)) and
|
||||
fwdFlow(mid, state0, _, _, _, nil, pragma[only_bind_into](config)) and
|
||||
additionalJumpStateStep(mid, state0, node, state, config) and
|
||||
cc = ccNone() and
|
||||
summaryCtx = TParameterPositionNone() and
|
||||
argAp = apNone() and
|
||||
ap = getApNil(node)
|
||||
)
|
||||
or
|
||||
// store
|
||||
exists(TypedContent tc, Ap ap0 |
|
||||
fwdFlowStore(_, ap0, tc, node, state, cc, argAp, config) and
|
||||
fwdFlowStore(_, ap0, tc, node, state, cc, summaryCtx, argAp, config) and
|
||||
ap = apCons(tc, ap0)
|
||||
)
|
||||
or
|
||||
// read
|
||||
exists(Ap ap0, Content c |
|
||||
fwdFlowRead(ap0, c, _, node, state, cc, argAp, config) and
|
||||
fwdFlowRead(ap0, c, _, node, state, cc, summaryCtx, argAp, config) and
|
||||
fwdFlowConsCand(ap0, c, ap, config)
|
||||
)
|
||||
or
|
||||
// flow into a callable
|
||||
exists(ApApprox apa |
|
||||
fwdFlowIn(_, node, state, _, cc, _, ap, config) and
|
||||
fwdFlowIn(_, node, state, _, cc, _, _, ap, config) and
|
||||
apa = getApprox(ap) and
|
||||
if PrevStage::parameterMayFlowThrough(node, _, apa, config)
|
||||
then argAp = apSome(ap)
|
||||
else argAp = apNone()
|
||||
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
|
||||
fwdFlowOutNotFromArg(node, state, cc, argAp, ap, config)
|
||||
exists(
|
||||
DataFlowCall call, RetNodeEx ret, boolean allowsFieldFlow, CcNoCall innercc,
|
||||
DataFlowCallable inner
|
||||
|
|
||||
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()
|
||||
)
|
||||
or
|
||||
exists(DataFlowCall call, Ap argAp0 |
|
||||
fwdFlowOutFromArg(call, node, state, argAp0, ap, config) and
|
||||
fwdFlowIsEntered(call, cc, argAp, argAp0, config)
|
||||
// flow through a callable
|
||||
exists(DataFlowCall call, ParameterPosition summaryCtx0, Ap argAp0 |
|
||||
fwdFlowOutFromArg(call, node, state, summaryCtx0, argAp0, ap, config) and
|
||||
fwdFlowIsEntered(call, cc, summaryCtx, argAp, summaryCtx0, argAp0, config)
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate fwdFlowStore(
|
||||
NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, FlowState state, Cc cc, ApOption argAp,
|
||||
Configuration config
|
||||
NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, FlowState state, Cc cc,
|
||||
ParameterPositionOption summaryCtx, ApOption argAp, Configuration config
|
||||
) {
|
||||
exists(DataFlowType contentType |
|
||||
fwdFlow(node1, state, cc, argAp, ap1, config) and
|
||||
fwdFlow(node1, state, cc, summaryCtx, argAp, ap1, config) and
|
||||
PrevStage::storeStepCand(node1, unbindApa(getApprox(ap1)), tc, node2, contentType, config) and
|
||||
typecheckStore(ap1, contentType)
|
||||
)
|
||||
@@ -1366,7 +1427,7 @@ private module MkStage<StageSig PrevStage> {
|
||||
pragma[nomagic]
|
||||
private predicate fwdFlowConsCand(Ap cons, Content c, Ap tail, Configuration config) {
|
||||
exists(TypedContent tc |
|
||||
fwdFlowStore(_, tail, tc, _, _, _, _, config) and
|
||||
fwdFlowStore(_, tail, tc, _, _, _, _, _, config) and
|
||||
tc.getContent() = c and
|
||||
cons = apCons(tc, tail)
|
||||
)
|
||||
@@ -1374,21 +1435,21 @@ private module MkStage<StageSig PrevStage> {
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate fwdFlowRead(
|
||||
Ap ap, Content c, NodeEx node1, NodeEx node2, FlowState state, Cc cc, ApOption argAp,
|
||||
Configuration config
|
||||
Ap ap, Content c, NodeEx node1, NodeEx node2, FlowState state, Cc cc,
|
||||
ParameterPositionOption summaryCtx, ApOption argAp, Configuration config
|
||||
) {
|
||||
fwdFlow(node1, state, cc, 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, Cc innercc, ApOption argAp,
|
||||
Ap ap, 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, argAp, ap, 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()
|
||||
@@ -1396,29 +1457,19 @@ private module MkStage<StageSig PrevStage> {
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate fwdFlowOutNotFromArg(
|
||||
NodeEx out, FlowState state, Cc ccOut, ApOption argAp, Ap ap, Configuration config
|
||||
private predicate fwdFlowOutFromArg(
|
||||
DataFlowCall call, NodeEx out, FlowState state, ParameterPosition summaryCtx, Ap argAp, Ap ap,
|
||||
Configuration config
|
||||
) {
|
||||
exists(
|
||||
DataFlowCall call, RetNodeEx ret, boolean allowsFieldFlow, CcNoCall innercc,
|
||||
DataFlowCallable inner
|
||||
DataFlowCallable c, RetNodeEx ret, ReturnKindExt kind, boolean allowsFieldFlow, CcCall ccc
|
||||
|
|
||||
fwdFlow(ret, state, innercc, argAp, ap, config) and
|
||||
flowOutOfCall(call, ret, out, allowsFieldFlow, config) and
|
||||
inner = ret.getEnclosingCallable() and
|
||||
ccOut = getCallContextReturn(inner, call, innercc) and
|
||||
if allowsFieldFlow = false then ap instanceof ApNil else any()
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate fwdFlowOutFromArg(
|
||||
DataFlowCall call, NodeEx out, FlowState state, Ap argAp, Ap ap, Configuration config
|
||||
) {
|
||||
exists(RetNodeEx ret, boolean allowsFieldFlow, CcCall ccc |
|
||||
fwdFlow(ret, state, ccc, apSome(argAp), ap, config) and
|
||||
flowThroughOutOfCall(call, ccc, ret, out, allowsFieldFlow, config) and
|
||||
if allowsFieldFlow = false then ap instanceof ApNil else any()
|
||||
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
|
||||
(if allowsFieldFlow = false then ap instanceof ApNil else any()) and
|
||||
parameterFlowThroughAllowed(c, pragma[only_bind_into](summaryCtx), kind)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1428,11 +1479,13 @@ private module MkStage<StageSig PrevStage> {
|
||||
*/
|
||||
pragma[nomagic]
|
||||
private predicate fwdFlowIsEntered(
|
||||
DataFlowCall call, Cc cc, ApOption argAp, Ap ap, Configuration config
|
||||
DataFlowCall call, Cc cc, ParameterPositionOption summaryCtx, ApOption argAp,
|
||||
ParameterPosition pos, Ap ap, Configuration config
|
||||
) {
|
||||
exists(ParamNodeEx p |
|
||||
fwdFlowIn(call, p, _, cc, _, argAp, ap, config) and
|
||||
PrevStage::parameterMayFlowThrough(p, _, unbindApa(getApprox(ap)), config)
|
||||
exists(ParamNodeEx param |
|
||||
fwdFlowIn(call, param, _, cc, _, summaryCtx, argAp, ap, config) and
|
||||
PrevStage::parameterMayFlowThrough(param, unbindApa(getApprox(ap)), config) and
|
||||
pos = param.getPosition()
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1440,27 +1493,40 @@ private module MkStage<StageSig PrevStage> {
|
||||
private predicate storeStepFwd(
|
||||
NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, Ap ap2, Configuration config
|
||||
) {
|
||||
fwdFlowStore(node1, ap1, tc, node2, _, _, _, config) and
|
||||
fwdFlowStore(node1, ap1, tc, node2, _, _, _, _, config) and
|
||||
ap2 = apCons(tc, ap1) and
|
||||
fwdFlowRead(ap2, tc.getContent(), _, _, _, _, _, config)
|
||||
fwdFlowRead(ap2, tc.getContent(), _, _, _, _, _, _, config)
|
||||
}
|
||||
|
||||
private predicate readStepFwd(
|
||||
NodeEx n1, Ap ap1, Content c, NodeEx n2, Ap ap2, Configuration config
|
||||
) {
|
||||
fwdFlowRead(ap1, c, n1, n2, _, _, _, config) and
|
||||
fwdFlowRead(ap1, c, n1, n2, _, _, _, _, config) and
|
||||
fwdFlowConsCand(ap1, c, ap2, config)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate callMayFlowThroughFwd(DataFlowCall call, Configuration config) {
|
||||
exists(Ap argAp0, NodeEx out, FlowState state, Cc cc, ApOption argAp, Ap ap |
|
||||
fwdFlow(out, state, pragma[only_bind_into](cc), pragma[only_bind_into](argAp), ap,
|
||||
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
|
||||
fwdFlowOutFromArg(call, out, state, argAp0, ap, config) and
|
||||
fwdFlowIsEntered(pragma[only_bind_into](call), pragma[only_bind_into](cc),
|
||||
pragma[only_bind_into](argAp), pragma[only_bind_into](argAp0),
|
||||
pragma[only_bind_into](config))
|
||||
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(DataFlowCallable c, ParameterPosition ppos |
|
||||
returnFlowsThrough0(ret, kind, state, ccc, c, ppos, argAp, ap, config) and
|
||||
p.isParameterOf(c, ppos) and
|
||||
parameterFlowThroughAllowed(p, kind)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1468,118 +1534,130 @@ private module MkStage<StageSig PrevStage> {
|
||||
private predicate flowThroughIntoCall(
|
||||
DataFlowCall call, ArgNodeEx arg, ParamNodeEx p, boolean allowsFieldFlow, Configuration config
|
||||
) {
|
||||
flowIntoCall(call, arg, p, allowsFieldFlow, config) and
|
||||
fwdFlow(arg, _, _, _, _, pragma[only_bind_into](config)) and
|
||||
PrevStage::parameterMayFlowThrough(p, _, _, pragma[only_bind_into](config)) and
|
||||
callMayFlowThroughFwd(call, pragma[only_bind_into](config))
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate returnNodeMayFlowThrough(
|
||||
RetNodeEx ret, FlowState state, Ap ap, Configuration config
|
||||
) {
|
||||
fwdFlow(ret, state, any(CcCall ccc), apSome(_), ap, config)
|
||||
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))
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `node` with access path `ap` is part of a path from a source to a
|
||||
* sink in the configuration `config`.
|
||||
*
|
||||
* The Boolean `toReturn` records whether the node must be returned from the
|
||||
* enclosing callable in order to reach a sink, and if so, `returnAp` records
|
||||
* the access path of the returned value.
|
||||
* The parameter `returnCtx` records whether (and how) the node must be returned
|
||||
* from the enclosing callable in order to reach a sink, and if so, `returnAp`
|
||||
* records the access path of the returned value.
|
||||
*/
|
||||
pragma[nomagic]
|
||||
additional predicate revFlow(
|
||||
NodeEx node, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, Configuration config
|
||||
NodeEx node, FlowState state, ReturnCtx returnCtx, ApOption returnAp, Ap ap,
|
||||
Configuration config
|
||||
) {
|
||||
revFlow0(node, state, toReturn, returnAp, ap, config) and
|
||||
fwdFlow(node, state, _, _, ap, config)
|
||||
revFlow0(node, state, returnCtx, returnAp, ap, config) and
|
||||
fwdFlow(node, state, _, _, _, ap, config)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate revFlow0(
|
||||
NodeEx node, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, Configuration config
|
||||
NodeEx node, FlowState state, ReturnCtx returnCtx, ApOption returnAp, Ap ap,
|
||||
Configuration config
|
||||
) {
|
||||
fwdFlow(node, state, _, _, ap, config) and
|
||||
fwdFlow(node, state, _, _, _, ap, config) and
|
||||
sinkNode(node, state, config) and
|
||||
(if hasSinkCallCtx(config) then toReturn = true else toReturn = false) and
|
||||
(
|
||||
if hasSinkCallCtx(config)
|
||||
then returnCtx = TReturnCtxNoFlowThrough()
|
||||
else returnCtx = TReturnCtxNone()
|
||||
) and
|
||||
returnAp = apNone() and
|
||||
ap instanceof ApNil
|
||||
or
|
||||
exists(NodeEx mid, FlowState state0 |
|
||||
localStep(node, state, mid, state0, true, _, config, _) and
|
||||
revFlow(mid, state0, toReturn, returnAp, ap, config)
|
||||
revFlow(mid, state0, returnCtx, returnAp, ap, config)
|
||||
)
|
||||
or
|
||||
exists(NodeEx mid, FlowState state0, ApNil nil |
|
||||
fwdFlow(node, pragma[only_bind_into](state), _, _, ap, pragma[only_bind_into](config)) and
|
||||
fwdFlow(node, pragma[only_bind_into](state), _, _, _, ap, pragma[only_bind_into](config)) and
|
||||
localStep(node, pragma[only_bind_into](state), mid, state0, false, _, config, _) and
|
||||
revFlow(mid, state0, toReturn, returnAp, nil, pragma[only_bind_into](config)) and
|
||||
revFlow(mid, state0, returnCtx, returnAp, nil, pragma[only_bind_into](config)) and
|
||||
ap instanceof ApNil
|
||||
)
|
||||
or
|
||||
exists(NodeEx mid |
|
||||
jumpStep(node, mid, config) and
|
||||
revFlow(mid, state, _, _, ap, config) and
|
||||
toReturn = false and
|
||||
returnCtx = TReturnCtxNone() and
|
||||
returnAp = apNone()
|
||||
)
|
||||
or
|
||||
exists(NodeEx mid, ApNil nil |
|
||||
fwdFlow(node, _, _, _, ap, pragma[only_bind_into](config)) and
|
||||
fwdFlow(node, _, _, _, _, ap, pragma[only_bind_into](config)) and
|
||||
additionalJumpStep(node, mid, config) and
|
||||
revFlow(pragma[only_bind_into](mid), state, _, _, nil, pragma[only_bind_into](config)) and
|
||||
toReturn = false and
|
||||
returnCtx = TReturnCtxNone() and
|
||||
returnAp = apNone() and
|
||||
ap instanceof ApNil
|
||||
)
|
||||
or
|
||||
exists(NodeEx mid, FlowState state0, ApNil nil |
|
||||
fwdFlow(node, _, _, _, ap, pragma[only_bind_into](config)) and
|
||||
fwdFlow(node, _, _, _, _, ap, pragma[only_bind_into](config)) and
|
||||
additionalJumpStateStep(node, state, mid, state0, config) and
|
||||
revFlow(pragma[only_bind_into](mid), pragma[only_bind_into](state0), _, _, nil,
|
||||
pragma[only_bind_into](config)) and
|
||||
toReturn = false and
|
||||
returnCtx = TReturnCtxNone() and
|
||||
returnAp = apNone() and
|
||||
ap instanceof ApNil
|
||||
)
|
||||
or
|
||||
// store
|
||||
exists(Ap ap0, Content c |
|
||||
revFlowStore(ap0, c, ap, node, state, _, _, toReturn, returnAp, config) and
|
||||
revFlowStore(ap0, c, ap, node, state, _, _, returnCtx, returnAp, config) and
|
||||
revFlowConsCand(ap0, c, ap, config)
|
||||
)
|
||||
or
|
||||
// read
|
||||
exists(NodeEx mid, Ap ap0 |
|
||||
revFlow(mid, state, toReturn, returnAp, ap0, config) and
|
||||
revFlow(mid, state, returnCtx, returnAp, ap0, config) and
|
||||
readStepFwd(node, ap, _, mid, ap0, config)
|
||||
)
|
||||
or
|
||||
// flow into a callable
|
||||
revFlowInNotToReturn(node, state, returnAp, ap, config) and
|
||||
toReturn = false
|
||||
exists(ParamNodeEx p, boolean allowsFieldFlow |
|
||||
revFlow(p, state, TReturnCtxNone(), returnAp, ap, config) and
|
||||
flowIntoCall(_, node, p, allowsFieldFlow, config) and
|
||||
(if allowsFieldFlow = false then ap instanceof ApNil else any()) and
|
||||
returnCtx = TReturnCtxNone()
|
||||
)
|
||||
or
|
||||
exists(DataFlowCall call, Ap returnAp0 |
|
||||
revFlowInToReturn(call, node, state, returnAp0, ap, config) and
|
||||
revFlowIsReturned(call, toReturn, returnAp, returnAp0, config)
|
||||
// flow through a callable
|
||||
exists(DataFlowCall call, ReturnKindExt returnKind0, Ap returnAp0 |
|
||||
revFlowInToReturn(call, node, state, returnKind0, returnAp0, ap, config) and
|
||||
revFlowIsReturned(call, returnCtx, returnAp, returnKind0, returnAp0, config)
|
||||
)
|
||||
or
|
||||
// flow out of a callable
|
||||
revFlowOut(_, node, state, _, _, ap, config) and
|
||||
toReturn = true and
|
||||
if returnNodeMayFlowThrough(node, state, ap, config)
|
||||
then returnAp = apSome(ap)
|
||||
else returnAp = apNone()
|
||||
exists(ReturnKindExt kind |
|
||||
revFlowOut(_, node, kind, state, _, _, ap, config) and
|
||||
if returnFlowsThrough(node, kind, state, _, _, _, ap, config)
|
||||
then (
|
||||
returnCtx = TReturnCtxMaybeFlowThrough(kind) and
|
||||
returnAp = apSome(ap)
|
||||
) else (
|
||||
returnCtx = TReturnCtxNoFlowThrough() and returnAp = apNone()
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate revFlowStore(
|
||||
Ap ap0, Content c, Ap ap, NodeEx node, FlowState state, TypedContent tc, NodeEx mid,
|
||||
boolean toReturn, ApOption returnAp, Configuration config
|
||||
ReturnCtx returnCtx, ApOption returnAp, Configuration config
|
||||
) {
|
||||
revFlow(mid, state, toReturn, returnAp, ap0, config) and
|
||||
revFlow(mid, state, returnCtx, returnAp, ap0, config) and
|
||||
storeStepFwd(node, ap, tc, mid, ap0, config) and
|
||||
tc.getContent() = c
|
||||
}
|
||||
@@ -1599,35 +1677,27 @@ private module MkStage<StageSig PrevStage> {
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate revFlowOut(
|
||||
DataFlowCall call, RetNodeEx ret, FlowState state, boolean toReturn, ApOption returnAp, Ap ap,
|
||||
Configuration config
|
||||
DataFlowCall call, RetNodeEx ret, ReturnKindExt kind, FlowState state, ReturnCtx returnCtx,
|
||||
ApOption returnAp, Ap ap, Configuration config
|
||||
) {
|
||||
exists(NodeEx out, boolean allowsFieldFlow |
|
||||
revFlow(out, state, toReturn, returnAp, ap, config) and
|
||||
flowOutOfCall(call, ret, out, allowsFieldFlow, config) and
|
||||
if allowsFieldFlow = false then ap instanceof ApNil else any()
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate revFlowInNotToReturn(
|
||||
ArgNodeEx arg, FlowState state, ApOption returnAp, Ap ap, Configuration config
|
||||
) {
|
||||
exists(ParamNodeEx p, boolean allowsFieldFlow |
|
||||
revFlow(p, state, false, returnAp, ap, config) and
|
||||
flowIntoCall(_, arg, p, allowsFieldFlow, config) and
|
||||
revFlow(out, state, returnCtx, returnAp, ap, config) and
|
||||
flowOutOfCall(call, ret, kind, out, allowsFieldFlow, config) and
|
||||
if allowsFieldFlow = false then ap instanceof ApNil else any()
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate revFlowInToReturn(
|
||||
DataFlowCall call, ArgNodeEx arg, FlowState state, Ap returnAp, Ap ap, Configuration config
|
||||
DataFlowCall call, ArgNodeEx arg, FlowState state, ReturnKindExt kind, Ap returnAp, Ap ap,
|
||||
Configuration config
|
||||
) {
|
||||
exists(ParamNodeEx p, boolean allowsFieldFlow |
|
||||
revFlow(p, state, true, apSome(returnAp), ap, config) and
|
||||
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()
|
||||
(if allowsFieldFlow = false then ap instanceof ApNil else any()) and
|
||||
parameterFlowThroughAllowed(p, kind)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1638,11 +1708,12 @@ private module MkStage<StageSig PrevStage> {
|
||||
*/
|
||||
pragma[nomagic]
|
||||
private predicate revFlowIsReturned(
|
||||
DataFlowCall call, boolean toReturn, ApOption returnAp, Ap ap, Configuration config
|
||||
DataFlowCall call, ReturnCtx returnCtx, ApOption returnAp, ReturnKindExt kind, Ap ap,
|
||||
Configuration config
|
||||
) {
|
||||
exists(RetNodeEx ret, FlowState state, CcCall ccc |
|
||||
revFlowOut(call, ret, state, toReturn, returnAp, ap, config) and
|
||||
fwdFlow(ret, state, ccc, apSome(_), ap, config) and
|
||||
revFlowOut(call, ret, kind, state, returnCtx, returnAp, ap, config) and
|
||||
returnFlowsThrough(ret, kind, state, ccc, _, _, ap, config) and
|
||||
matchesCall(ccc, call)
|
||||
)
|
||||
}
|
||||
@@ -1713,40 +1784,39 @@ private module MkStage<StageSig PrevStage> {
|
||||
validAp(ap, config)
|
||||
}
|
||||
|
||||
pragma[noinline]
|
||||
private predicate parameterFlow(
|
||||
ParamNodeEx p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config
|
||||
pragma[nomagic]
|
||||
private predicate parameterFlowsThroughRev(
|
||||
ParamNodeEx p, Ap ap, ReturnKindExt kind, Configuration config
|
||||
) {
|
||||
revFlow(p, _, true, apSome(ap0), ap, config) and
|
||||
c = p.getEnclosingCallable()
|
||||
revFlow(p, _, TReturnCtxMaybeFlowThrough(kind), apSome(_), ap, config) and
|
||||
parameterFlowThroughAllowed(p, kind)
|
||||
}
|
||||
|
||||
predicate parameterMayFlowThrough(ParamNodeEx p, DataFlowCallable c, Ap ap, Configuration config) {
|
||||
exists(RetNodeEx ret, FlowState state, Ap ap0, ReturnKindExt kind, ParameterPosition pos |
|
||||
parameterFlow(p, ap, ap0, c, config) and
|
||||
c = ret.getEnclosingCallable() and
|
||||
revFlow(pragma[only_bind_into](ret), pragma[only_bind_into](state), true, apSome(_),
|
||||
pragma[only_bind_into](ap0), pragma[only_bind_into](config)) and
|
||||
fwdFlow(ret, state, any(CcCall ccc), apSome(ap), ap0, config) and
|
||||
kind = ret.getKind() and
|
||||
p.getPosition() = pos and
|
||||
// we don't expect a parameter to return stored in itself, unless explicitly allowed
|
||||
(
|
||||
not kind.(ParamUpdateReturnKind).getPosition() = pos
|
||||
or
|
||||
p.allowParameterReturnInSelf()
|
||||
)
|
||||
pragma[nomagic]
|
||||
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)
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
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(
|
||||
Ap returnAp0, ArgNodeEx arg, FlowState state, boolean toReturn, ApOption returnAp, Ap ap
|
||||
ReturnKindExt returnKind0, Ap returnAp0, ArgNodeEx arg, FlowState state,
|
||||
ReturnCtx returnCtx, ApOption returnAp, Ap ap
|
||||
|
|
||||
revFlow(arg, state, toReturn, returnAp, ap, config) and
|
||||
revFlowInToReturn(call, arg, state, returnAp0, ap, config) and
|
||||
revFlowIsReturned(call, toReturn, returnAp, returnAp0, config)
|
||||
revFlow(arg, state, returnCtx, returnAp, ap, config) and
|
||||
revFlowInToReturn(call, arg, state, returnKind0, returnAp0, ap, config) and
|
||||
revFlowIsReturned(call, returnCtx, returnAp, returnKind0, returnAp0, config)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1754,14 +1824,13 @@ private module MkStage<StageSig PrevStage> {
|
||||
boolean fwd, int nodes, int fields, int conscand, int states, int tuples, Configuration config
|
||||
) {
|
||||
fwd = true and
|
||||
nodes = count(NodeEx node | fwdFlow(node, _, _, _, _, config)) and
|
||||
nodes = count(NodeEx node | fwdFlow(node, _, _, _, _, _, config)) and
|
||||
fields = count(TypedContent f0 | fwdConsCand(f0, _, config)) and
|
||||
conscand = count(TypedContent f0, Ap ap | fwdConsCand(f0, ap, config)) and
|
||||
states = count(FlowState state | fwdFlow(_, state, _, _, _, config)) and
|
||||
states = count(FlowState state | fwdFlow(_, state, _, _, _, _, config)) and
|
||||
tuples =
|
||||
count(NodeEx n, FlowState state, Cc cc, ApOption argAp, Ap ap |
|
||||
fwdFlow(n, state, cc, argAp, ap, config)
|
||||
)
|
||||
count(NodeEx n, FlowState state, Cc cc, ParameterPositionOption summaryCtx, ApOption argAp,
|
||||
Ap ap | fwdFlow(n, state, cc, summaryCtx, argAp, ap, config))
|
||||
or
|
||||
fwd = false and
|
||||
nodes = count(NodeEx node | revFlow(node, _, _, _, _, config)) and
|
||||
@@ -1769,8 +1838,8 @@ private module MkStage<StageSig PrevStage> {
|
||||
conscand = count(TypedContent f0, Ap ap | consCand(f0, ap, config)) and
|
||||
states = count(FlowState state | revFlow(_, state, _, _, _, config)) and
|
||||
tuples =
|
||||
count(NodeEx n, FlowState state, boolean b, ApOption retAp, Ap ap |
|
||||
revFlow(n, state, b, retAp, ap, config)
|
||||
count(NodeEx n, FlowState state, ReturnCtx returnCtx, ApOption retAp, Ap ap |
|
||||
revFlow(n, state, returnCtx, retAp, ap, config)
|
||||
)
|
||||
}
|
||||
/* End: Stage logic. */
|
||||
@@ -1915,7 +1984,7 @@ private module Stage2Param implements MkStage<Stage1>::StageParam {
|
||||
exists(lcc)
|
||||
}
|
||||
|
||||
predicate flowOutOfCall = flowOutOfCallNodeCand1/5;
|
||||
predicate flowOutOfCall = flowOutOfCallNodeCand1/6;
|
||||
|
||||
predicate flowIntoCall = flowIntoCallNodeCand1/5;
|
||||
|
||||
@@ -1951,9 +2020,10 @@ private module Stage2 implements StageSig {
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate flowOutOfCallNodeCand2(
|
||||
DataFlowCall call, RetNodeEx node1, NodeEx node2, boolean allowsFieldFlow, Configuration config
|
||||
DataFlowCall call, RetNodeEx node1, ReturnKindExt kind, NodeEx node2, boolean allowsFieldFlow,
|
||||
Configuration config
|
||||
) {
|
||||
flowOutOfCallNodeCand1(call, node1, node2, allowsFieldFlow, config) and
|
||||
flowOutOfCallNodeCand1(call, node1, kind, node2, allowsFieldFlow, config) and
|
||||
Stage2::revFlow(node2, pragma[only_bind_into](config)) and
|
||||
Stage2::revFlowAlias(node1, pragma[only_bind_into](config))
|
||||
}
|
||||
@@ -2021,8 +2091,8 @@ private module LocalFlowBigStep {
|
||||
exists(NodeEx next | Stage2::revFlow(next, state, config) |
|
||||
jumpStep(node, next, config) or
|
||||
additionalJumpStep(node, next, config) or
|
||||
flowIntoCallNodeCand1(_, node, next, config) or
|
||||
flowOutOfCallNodeCand1(_, node, next, config) or
|
||||
flowIntoCallNodeCand2(_, node, next, _, config) or
|
||||
flowOutOfCallNodeCand2(_, node, _, next, _, config) or
|
||||
Stage2::storeStepCand(node, _, _, next, _, config) or
|
||||
Stage2::readStepCand(node, _, next, config)
|
||||
)
|
||||
@@ -2163,7 +2233,7 @@ private module Stage3Param implements MkStage<Stage2>::StageParam {
|
||||
localFlowBigStep(node1, state1, node2, state2, preservesValue, ap, config, _) and exists(lcc)
|
||||
}
|
||||
|
||||
predicate flowOutOfCall = flowOutOfCallNodeCand2/5;
|
||||
predicate flowOutOfCall = flowOutOfCallNodeCand2/6;
|
||||
|
||||
predicate flowIntoCall = flowIntoCallNodeCand2/5;
|
||||
|
||||
@@ -2233,8 +2303,9 @@ private predicate flowCandSummaryCtx(
|
||||
NodeEx node, FlowState state, AccessPathFront argApf, Configuration config
|
||||
) {
|
||||
exists(AccessPathFront apf |
|
||||
Stage3::revFlow(node, state, true, _, apf, config) and
|
||||
Stage3::fwdFlow(node, state, any(Stage3::CcCall ccc), TAccessPathFrontSome(argApf), apf, config)
|
||||
Stage3::revFlow(node, state, TReturnCtxMaybeFlowThrough(_), _, apf, config) and
|
||||
Stage3::fwdFlow(node, state, any(Stage3::CcCall ccc), _, TAccessPathFrontSome(argApf), apf,
|
||||
config)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -2468,10 +2539,11 @@ private module Stage4Param implements MkStage<Stage3>::StageParam {
|
||||
|
||||
pragma[nomagic]
|
||||
predicate flowOutOfCall(
|
||||
DataFlowCall call, RetNodeEx node1, NodeEx node2, boolean allowsFieldFlow, Configuration config
|
||||
DataFlowCall call, RetNodeEx node1, ReturnKindExt kind, NodeEx node2, boolean allowsFieldFlow,
|
||||
Configuration config
|
||||
) {
|
||||
exists(FlowState state |
|
||||
flowOutOfCallNodeCand2(call, node1, node2, allowsFieldFlow, config) and
|
||||
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))
|
||||
@@ -2508,13 +2580,14 @@ private Configuration unbindConf(Configuration conf) {
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate nodeMayUseSummary0(
|
||||
NodeEx n, DataFlowCallable c, FlowState state, AccessPathApprox apa, Configuration config
|
||||
NodeEx n, DataFlowCallable c, ParameterPosition pos, FlowState state, AccessPathApprox apa,
|
||||
Configuration config
|
||||
) {
|
||||
exists(AccessPathApprox apa0 |
|
||||
Stage4::parameterMayFlowThrough(_, c, _, _) and
|
||||
Stage4::revFlow(n, state, true, _, apa0, config) and
|
||||
Stage4::fwdFlow(n, state, any(CallContextCall ccc), TAccessPathApproxSome(apa), apa0, config) and
|
||||
n.getEnclosingCallable() = c
|
||||
c = n.getEnclosingCallable() and
|
||||
Stage4::revFlow(n, state, TReturnCtxMaybeFlowThrough(_), _, apa0, config) and
|
||||
Stage4::fwdFlow(n, state, any(CallContextCall ccc), TParameterPositionSome(pos),
|
||||
TAccessPathApproxSome(apa), apa0, config)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -2522,9 +2595,10 @@ pragma[nomagic]
|
||||
private predicate nodeMayUseSummary(
|
||||
NodeEx n, FlowState state, AccessPathApprox apa, Configuration config
|
||||
) {
|
||||
exists(DataFlowCallable c |
|
||||
Stage4::parameterMayFlowThrough(_, c, apa, config) and
|
||||
nodeMayUseSummary0(n, c, state, apa, config)
|
||||
exists(DataFlowCallable c, ParameterPosition pos, ParamNodeEx p |
|
||||
Stage4::parameterMayFlowThrough(p, apa, config) and
|
||||
nodeMayUseSummary0(n, c, pos, state, apa, config) and
|
||||
p.isParameterOf(c, pos)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -2532,7 +2606,7 @@ private newtype TSummaryCtx =
|
||||
TSummaryCtxNone() or
|
||||
TSummaryCtxSome(ParamNodeEx p, FlowState state, AccessPath ap) {
|
||||
exists(Configuration config |
|
||||
Stage4::parameterMayFlowThrough(p, _, ap.getApprox(), config) and
|
||||
Stage4::parameterMayFlowThrough(p, ap.getApprox(), config) and
|
||||
Stage4::revFlow(p, state, _, config)
|
||||
)
|
||||
}
|
||||
@@ -3453,17 +3527,11 @@ private predicate paramFlowsThrough(
|
||||
ReturnKindExt kind, FlowState state, CallContextCall cc, SummaryCtxSome sc, AccessPath ap,
|
||||
AccessPathApprox apa, Configuration config
|
||||
) {
|
||||
exists(PathNodeMid mid, RetNodeEx ret, ParameterPosition pos |
|
||||
exists(PathNodeMid mid, RetNodeEx ret |
|
||||
pathNode(mid, ret, state, cc, sc, ap, config, _) and
|
||||
kind = ret.getKind() and
|
||||
apa = ap.getApprox() and
|
||||
pos = sc.getParameterPos() and
|
||||
// we don't expect a parameter to return stored in itself, unless explicitly allowed
|
||||
(
|
||||
not kind.(ParamUpdateReturnKind).getPosition() = pos
|
||||
or
|
||||
sc.getParamNode().allowParameterReturnInSelf()
|
||||
)
|
||||
parameterFlowThroughAllowed(sc.getParamNode(), kind)
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@@ -915,6 +915,17 @@ private module Cached {
|
||||
TDataFlowCallNone() or
|
||||
TDataFlowCallSome(DataFlowCall call)
|
||||
|
||||
cached
|
||||
newtype TParameterPositionOption =
|
||||
TParameterPositionNone() or
|
||||
TParameterPositionSome(ParameterPosition pos)
|
||||
|
||||
cached
|
||||
newtype TReturnCtx =
|
||||
TReturnCtxNone() or
|
||||
TReturnCtxNoFlowThrough() or
|
||||
TReturnCtxMaybeFlowThrough(ReturnKindExt kind)
|
||||
|
||||
cached
|
||||
newtype TTypedContent = MkTypedContent(Content c, DataFlowType t) { store(_, c, _, _, t) }
|
||||
|
||||
@@ -1304,6 +1315,44 @@ class DataFlowCallOption extends TDataFlowCallOption {
|
||||
}
|
||||
}
|
||||
|
||||
/** An optional `ParameterPosition`. */
|
||||
class ParameterPositionOption extends TParameterPositionOption {
|
||||
string toString() {
|
||||
this = TParameterPositionNone() and
|
||||
result = "(none)"
|
||||
or
|
||||
exists(ParameterPosition pos |
|
||||
this = TParameterPositionSome(pos) and
|
||||
result = pos.toString()
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A return context used to calculate flow summaries in reverse flow.
|
||||
*
|
||||
* The possible values are:
|
||||
*
|
||||
* - `TReturnCtxNone()`: no return flow.
|
||||
* - `TReturnCtxNoFlowThrough()`: return flow, but flow through is not possible.
|
||||
* - `TReturnCtxMaybeFlowThrough(ReturnKindExt kind)`: return flow, of kind `kind`, and
|
||||
* flow through may be possible.
|
||||
*/
|
||||
class ReturnCtx extends TReturnCtx {
|
||||
string toString() {
|
||||
this = TReturnCtxNone() and
|
||||
result = "(none)"
|
||||
or
|
||||
this = TReturnCtxNoFlowThrough() and
|
||||
result = "(no flow through)"
|
||||
or
|
||||
exists(ReturnKindExt kind |
|
||||
this = TReturnCtxMaybeFlowThrough(kind) and
|
||||
result = kind.toString()
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/** A `Content` tagged with the type of a containing object. */
|
||||
class TypedContent extends MkTypedContent {
|
||||
private Content c;
|
||||
|
||||
@@ -244,4 +244,20 @@ module Consistency {
|
||||
not callable = viableCallable(call) and
|
||||
not any(ConsistencyConfiguration c).viableImplInCallContextTooLargeExclude(call, ctx, callable)
|
||||
}
|
||||
|
||||
query predicate uniqueParameterNodeAtPosition(
|
||||
DataFlowCallable c, ParameterPosition pos, Node p, string msg
|
||||
) {
|
||||
isParameterNode(p, c, pos) and
|
||||
not exists(unique(Node p0 | isParameterNode(p0, c, pos))) and
|
||||
msg = "Parameters with overlapping positions."
|
||||
}
|
||||
|
||||
query predicate uniqueParameterNodePosition(
|
||||
DataFlowCallable c, ParameterPosition pos, Node p, string msg
|
||||
) {
|
||||
isParameterNode(p, c, pos) and
|
||||
not exists(unique(ParameterPosition pos0 | isParameterNode(p, c, pos0))) and
|
||||
msg = "Parameter node with multiple positions."
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,644 @@
|
||||
/**
|
||||
* 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.Sscanf
|
||||
private import implementations.Scanf
|
||||
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,15 +1,19 @@
|
||||
/**
|
||||
* Provides an implementation class modeling the POSIX function `getenv`.
|
||||
* Provides an implementation class modeling the POSIX function `getenv` and
|
||||
* various similar functions.
|
||||
*/
|
||||
|
||||
import cpp
|
||||
import semmle.code.cpp.models.interfaces.FlowSource
|
||||
|
||||
/**
|
||||
* The POSIX function `getenv`.
|
||||
* The POSIX function `getenv`, the GNU function `secure_getenv`, and the
|
||||
* Windows function `_wgetenv`.
|
||||
*/
|
||||
class Getenv extends LocalFlowSourceFunction {
|
||||
Getenv() { this.hasGlobalOrStdOrBslName("getenv") }
|
||||
Getenv() {
|
||||
this.hasGlobalOrStdOrBslName("getenv") or this.hasGlobalName(["secure_getenv", "_wgetenv"])
|
||||
}
|
||||
|
||||
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,9 +1,10 @@
|
||||
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() { hasGlobalName("inet_ntoa") }
|
||||
InetNtoa() { this.hasGlobalName("inet_ntoa") }
|
||||
|
||||
override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) {
|
||||
input.isParameter(0) and
|
||||
@@ -12,7 +13,7 @@ private class InetNtoa extends TaintFunction {
|
||||
}
|
||||
|
||||
private class InetAton extends TaintFunction, ArrayFunction {
|
||||
InetAton() { hasGlobalName("inet_aton") }
|
||||
InetAton() { this.hasGlobalName("inet_aton") }
|
||||
|
||||
override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) {
|
||||
input.isParameterDeref(0) and
|
||||
@@ -32,7 +33,7 @@ private class InetAton extends TaintFunction, ArrayFunction {
|
||||
}
|
||||
|
||||
private class InetAddr extends TaintFunction, ArrayFunction, AliasFunction {
|
||||
InetAddr() { hasGlobalName("inet_addr") }
|
||||
InetAddr() { this.hasGlobalName("inet_addr") }
|
||||
|
||||
override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) {
|
||||
input.isParameterDeref(0) and
|
||||
@@ -51,7 +52,7 @@ private class InetAddr extends TaintFunction, ArrayFunction, AliasFunction {
|
||||
}
|
||||
|
||||
private class InetNetwork extends TaintFunction, ArrayFunction {
|
||||
InetNetwork() { hasGlobalName("inet_network") }
|
||||
InetNetwork() { this.hasGlobalName("inet_network") }
|
||||
|
||||
override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) {
|
||||
input.isParameterDeref(0) and
|
||||
@@ -64,7 +65,7 @@ private class InetNetwork extends TaintFunction, ArrayFunction {
|
||||
}
|
||||
|
||||
private class InetMakeaddr extends TaintFunction {
|
||||
InetMakeaddr() { hasGlobalName("inet_makeaddr") }
|
||||
InetMakeaddr() { this.hasGlobalName("inet_makeaddr") }
|
||||
|
||||
override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) {
|
||||
(
|
||||
@@ -76,7 +77,7 @@ private class InetMakeaddr extends TaintFunction {
|
||||
}
|
||||
|
||||
private class InetLnaof extends TaintFunction {
|
||||
InetLnaof() { hasGlobalName("inet_lnaof") }
|
||||
InetLnaof() { this.hasGlobalName("inet_lnaof") }
|
||||
|
||||
override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) {
|
||||
input.isParameter(0) and
|
||||
@@ -85,7 +86,7 @@ private class InetLnaof extends TaintFunction {
|
||||
}
|
||||
|
||||
private class InetNetof extends TaintFunction {
|
||||
InetNetof() { hasGlobalName("inet_netof") }
|
||||
InetNetof() { this.hasGlobalName("inet_netof") }
|
||||
|
||||
override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) {
|
||||
input.isParameter(0) and
|
||||
@@ -94,7 +95,7 @@ private class InetNetof extends TaintFunction {
|
||||
}
|
||||
|
||||
private class InetPton extends TaintFunction, ArrayFunction {
|
||||
InetPton() { hasGlobalName("inet_pton") }
|
||||
InetPton() { this.hasGlobalName("inet_pton") }
|
||||
|
||||
override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) {
|
||||
(
|
||||
@@ -114,7 +115,7 @@ private class InetPton extends TaintFunction, ArrayFunction {
|
||||
}
|
||||
|
||||
private class Gethostbyname extends TaintFunction, ArrayFunction {
|
||||
Gethostbyname() { hasGlobalName("gethostbyname") }
|
||||
Gethostbyname() { this.hasGlobalName("gethostbyname") }
|
||||
|
||||
override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) {
|
||||
input.isParameterDeref(0) and
|
||||
@@ -127,7 +128,7 @@ private class Gethostbyname extends TaintFunction, ArrayFunction {
|
||||
}
|
||||
|
||||
private class Gethostbyaddr extends TaintFunction, ArrayFunction {
|
||||
Gethostbyaddr() { hasGlobalName("gethostbyaddr") }
|
||||
Gethostbyaddr() { this.hasGlobalName("gethostbyaddr") }
|
||||
|
||||
override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) {
|
||||
(
|
||||
@@ -142,3 +143,21 @@ 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()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -31,7 +31,17 @@ private class IteratorTraits extends Class {
|
||||
* `std::iterator_traits` instantiation for it.
|
||||
*/
|
||||
private class IteratorByTraits extends Iterator {
|
||||
IteratorByTraits() { exists(IteratorTraits it | it.getIteratorType() = this) }
|
||||
IteratorTraits trait;
|
||||
|
||||
IteratorByTraits() { trait.getIteratorType() = this }
|
||||
|
||||
override Type getValueType() {
|
||||
exists(TypedefType t |
|
||||
trait.getAMember() = t and
|
||||
t.getName() = "value_type" and
|
||||
result = t.getUnderlyingType()
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -42,20 +52,27 @@ private class IteratorByTraits extends Iterator {
|
||||
*/
|
||||
private class IteratorByPointer extends Iterator instanceof PointerType {
|
||||
IteratorByPointer() { not this instanceof IteratorByTraits }
|
||||
|
||||
override Type getValueType() { result = super.getBaseType() }
|
||||
}
|
||||
|
||||
/**
|
||||
* A type which has the typedefs expected for an iterator.
|
||||
*/
|
||||
private class IteratorByTypedefs extends Iterator, Class {
|
||||
TypedefType valueType;
|
||||
|
||||
IteratorByTypedefs() {
|
||||
this.getAMember().(TypedefType).hasName("difference_type") and
|
||||
this.getAMember().(TypedefType).hasName("value_type") and
|
||||
valueType = this.getAMember() and
|
||||
valueType.hasName("value_type") and
|
||||
this.getAMember().(TypedefType).hasName("pointer") and
|
||||
this.getAMember().(TypedefType).hasName("reference") and
|
||||
this.getAMember().(TypedefType).hasName("iterator_category") and
|
||||
not this.hasQualifiedName(["std", "bsl"], "iterator_traits")
|
||||
}
|
||||
|
||||
override Type getValueType() { result = valueType.getUnderlyingType() }
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -63,6 +80,8 @@ private class IteratorByTypedefs extends Iterator, Class {
|
||||
*/
|
||||
private class StdIterator extends Iterator, Class {
|
||||
StdIterator() { this.hasQualifiedName(["std", "bsl"], "iterator") }
|
||||
|
||||
override Type getValueType() { result = this.getTemplateArgument(1).(Type).getUnderlyingType() }
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -166,12 +185,15 @@ private class IteratorSubOperator extends Operator, TaintFunction {
|
||||
/**
|
||||
* A non-member `operator+=` or `operator-=` function for an iterator type.
|
||||
*/
|
||||
private class IteratorAssignArithmeticOperator extends Operator, DataFlowFunction, TaintFunction {
|
||||
class IteratorAssignArithmeticOperator extends Operator {
|
||||
IteratorAssignArithmeticOperator() {
|
||||
this.hasName(["operator+=", "operator-="]) and
|
||||
exists(getIteratorArgumentInput(this, 0))
|
||||
}
|
||||
}
|
||||
|
||||
private class IteratorAssignArithmeticOperatorModel extends IteratorAssignArithmeticOperator,
|
||||
DataFlowFunction, TaintFunction {
|
||||
override predicate hasDataFlow(FunctionInput input, FunctionOutput output) {
|
||||
input.isParameter(0) and
|
||||
output.isReturnValue()
|
||||
@@ -210,11 +232,14 @@ class IteratorPointerDereferenceMemberOperator extends MemberFunction, TaintFunc
|
||||
/**
|
||||
* An `operator++` or `operator--` member function for an iterator type.
|
||||
*/
|
||||
private class IteratorCrementMemberOperator extends MemberFunction, DataFlowFunction, TaintFunction {
|
||||
class IteratorCrementMemberOperator extends MemberFunction {
|
||||
IteratorCrementMemberOperator() {
|
||||
this.getClassAndName(["operator++", "operator--"]) instanceof Iterator
|
||||
}
|
||||
}
|
||||
|
||||
private class IteratorCrementMemberOperatorModel extends IteratorCrementMemberOperator,
|
||||
DataFlowFunction, TaintFunction {
|
||||
override predicate hasDataFlow(FunctionInput input, FunctionOutput output) {
|
||||
input.isQualifierAddress() and
|
||||
output.isReturnValue()
|
||||
|
||||
@@ -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) }
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/**
|
||||
* Provides implementation classes modeling `sscanf`, `fscanf` and various similar
|
||||
* functions. See `semmle.code.cpp.models.Models` for usage information.
|
||||
* Provides implementation classes modeling the `scanf` family of functions.
|
||||
* See `semmle.code.cpp.models.Models` for usage information.
|
||||
*/
|
||||
|
||||
import semmle.code.cpp.Function
|
||||
@@ -9,18 +9,15 @@ 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 standard function `sscanf`, `fscanf` and its assorted variants
|
||||
* The `scanf` family of functions.
|
||||
*/
|
||||
private class SscanfModel extends ArrayFunction, TaintFunction, AliasFunction, SideEffectFunction {
|
||||
SscanfModel() { this instanceof Sscanf or this instanceof Fscanf or this instanceof Snscanf }
|
||||
|
||||
abstract private class ScanfFunctionModel extends ArrayFunction, TaintFunction, AliasFunction,
|
||||
SideEffectFunction {
|
||||
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) }
|
||||
@@ -36,7 +33,7 @@ private class SscanfModel extends ArrayFunction, TaintFunction, AliasFunction, S
|
||||
)
|
||||
}
|
||||
|
||||
private int getArgsStartPosition() { result = this.getNumberOfParameters() }
|
||||
int getArgsStartPosition() { result = this.getNumberOfParameters() }
|
||||
|
||||
override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) {
|
||||
input.isParameterDeref(this.(ScanfFunction).getInputParameterIndex()) and
|
||||
@@ -70,3 +67,36 @@ private class SscanfModel extends ArrayFunction, TaintFunction, AliasFunction, S
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 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()
|
||||
}
|
||||
}
|
||||
@@ -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) }
|
||||
|
||||
@@ -5,38 +5,53 @@
|
||||
import semmle.code.cpp.models.interfaces.Taint
|
||||
import semmle.code.cpp.models.interfaces.Iterator
|
||||
|
||||
/**
|
||||
* A sequence container template class (for example, `std::vector`) from the
|
||||
* standard library.
|
||||
*/
|
||||
abstract class StdSequenceContainer extends Class {
|
||||
Type getElementType() { result = this.getTemplateArgument(0) }
|
||||
}
|
||||
|
||||
/**
|
||||
* The `std::array` template class.
|
||||
*/
|
||||
private class Array extends Class {
|
||||
private class Array extends StdSequenceContainer {
|
||||
Array() { this.hasQualifiedName(["std", "bsl"], "array") }
|
||||
}
|
||||
|
||||
/**
|
||||
* The `std::string` template class.
|
||||
*/
|
||||
private class String extends StdSequenceContainer {
|
||||
String() { this.hasQualifiedName(["std", "bsl"], "basic_string") }
|
||||
}
|
||||
|
||||
/**
|
||||
* The `std::deque` template class.
|
||||
*/
|
||||
private class Deque extends Class {
|
||||
private class Deque extends StdSequenceContainer {
|
||||
Deque() { this.hasQualifiedName(["std", "bsl"], "deque") }
|
||||
}
|
||||
|
||||
/**
|
||||
* The `std::forward_list` template class.
|
||||
*/
|
||||
private class ForwardList extends Class {
|
||||
private class ForwardList extends StdSequenceContainer {
|
||||
ForwardList() { this.hasQualifiedName(["std", "bsl"], "forward_list") }
|
||||
}
|
||||
|
||||
/**
|
||||
* The `std::list` template class.
|
||||
*/
|
||||
private class List extends Class {
|
||||
private class List extends StdSequenceContainer {
|
||||
List() { this.hasQualifiedName(["std", "bsl"], "list") }
|
||||
}
|
||||
|
||||
/**
|
||||
* The `std::vector` template class.
|
||||
*/
|
||||
private class Vector extends Class {
|
||||
private class Vector extends StdSequenceContainer {
|
||||
Vector() { this.hasQualifiedName(["std", "bsl"], "vector") }
|
||||
}
|
||||
|
||||
|
||||
@@ -17,6 +17,15 @@ private class StdBasicString extends ClassTemplateInstantiation {
|
||||
|
||||
/**
|
||||
* 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() {
|
||||
|
||||
@@ -29,5 +29,17 @@ abstract class GetIteratorFunction extends Function {
|
||||
|
||||
/**
|
||||
* A type which can be used as an iterator.
|
||||
*
|
||||
* Note: Do _not_ `extend` when inheriting from this class in queries. Always use `instanceof`:
|
||||
* ```
|
||||
* class MyIterator instanceof Iterator { ... }
|
||||
* ```
|
||||
*/
|
||||
abstract class Iterator extends Type { }
|
||||
abstract class Iterator extends Type {
|
||||
/**
|
||||
* Gets the value type of this iterator, if any.
|
||||
*
|
||||
* For example, the value type of a `std::vector<int>::iterator` is `int`.
|
||||
*/
|
||||
Type getValueType() { none() }
|
||||
}
|
||||
|
||||
@@ -89,9 +89,9 @@ private class LocalParameterSource extends LocalFlowSource {
|
||||
|
||||
private class ArgvSource extends LocalFlowSource {
|
||||
ArgvSource() {
|
||||
exists(Parameter argv |
|
||||
argv.hasName("argv") and
|
||||
argv.getFunction().hasGlobalName("main") and
|
||||
exists(Function main, Parameter argv |
|
||||
main.hasGlobalName("main") and
|
||||
main.getParameter(1) = argv and
|
||||
this.asExpr() = argv.getAnAccess()
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
/*
|
||||
/**
|
||||
* Support for tracking tainted data through the program. This is an alias for
|
||||
* `semmle.code.cpp.ir.dataflow.DefaultTaintTracking` provided for backwards
|
||||
* compatibility.
|
||||
|
||||
@@ -121,7 +121,9 @@ 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
|
||||
*/
|
||||
private predicate betweenFunctionsValueMoveTo(Element src, Element dest, boolean destFromArg) {
|
||||
deprecated private predicate betweenFunctionsValueMoveTo(
|
||||
Element src, Element dest, boolean destFromArg
|
||||
) {
|
||||
not unreachable(src) and
|
||||
not unreachable(dest) and
|
||||
(
|
||||
@@ -162,13 +164,13 @@ private predicate betweenFunctionsValueMoveTo(Element src, Element dest, boolean
|
||||
// predicate folding for proper join-order
|
||||
// bad magic: pushes down predicate that ruins join-order
|
||||
pragma[nomagic]
|
||||
private predicate resolveCallWithParam(Call call, Function called, int i, Parameter p) {
|
||||
deprecated 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. */
|
||||
library class FlowVariable extends Variable {
|
||||
deprecated library class FlowVariable extends Variable {
|
||||
FlowVariable() {
|
||||
(
|
||||
this instanceof LocalScopeVariable or
|
||||
@@ -179,11 +181,11 @@ library class FlowVariable extends Variable {
|
||||
}
|
||||
|
||||
/** A local scope variable for which flow through is allowed. */
|
||||
library class FlowLocalScopeVariable extends Variable {
|
||||
deprecated library class FlowLocalScopeVariable extends Variable {
|
||||
FlowLocalScopeVariable() { this instanceof LocalScopeVariable }
|
||||
}
|
||||
|
||||
private predicate insideFunctionValueMoveTo(Element src, Element dest) {
|
||||
deprecated private predicate insideFunctionValueMoveTo(Element src, Element dest) {
|
||||
not unreachable(src) and
|
||||
not unreachable(dest) and
|
||||
(
|
||||
@@ -324,7 +326,7 @@ private predicate unionAccess(Variable v, Field f, FieldAccess a) {
|
||||
a.getQualifier() = v.getAnAccess()
|
||||
}
|
||||
|
||||
GlobalOrNamespaceVariable globalVarFromId(string id) {
|
||||
deprecated GlobalOrNamespaceVariable globalVarFromId(string id) {
|
||||
if result instanceof NamespaceVariable
|
||||
then id = result.getNamespace() + "::" + result.getName()
|
||||
else id = result.getName()
|
||||
@@ -353,7 +355,7 @@ private predicate hasUpperBoundsCheck(Variable var) {
|
||||
}
|
||||
|
||||
cached
|
||||
private predicate taintedWithArgsAndGlobalVars(
|
||||
deprecated private predicate taintedWithArgsAndGlobalVars(
|
||||
Element src, Element dest, boolean destFromArg, string globalVar
|
||||
) {
|
||||
isUserInput(src, _) and
|
||||
@@ -395,7 +397,7 @@ private predicate taintedWithArgsAndGlobalVars(
|
||||
* This doesn't include data flow through global variables.
|
||||
* If you need that you must call taintedIncludingGlobalVars.
|
||||
*/
|
||||
predicate tainted(Expr source, Element tainted) {
|
||||
deprecated predicate tainted(Expr source, Element tainted) {
|
||||
taintedWithArgsAndGlobalVars(source, tainted, _, "")
|
||||
}
|
||||
|
||||
@@ -410,7 +412,7 @@ 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.
|
||||
*/
|
||||
predicate taintedIncludingGlobalVars(Expr source, Element tainted, string globalVar) {
|
||||
deprecated predicate taintedIncludingGlobalVars(Expr source, Element tainted, string globalVar) {
|
||||
taintedWithArgsAndGlobalVars(source, tainted, _, globalVar)
|
||||
}
|
||||
|
||||
@@ -541,14 +543,14 @@ private predicate returnArgument(Function f, int sourceArg) {
|
||||
* targets a virtual method, simple data flow analysis is performed
|
||||
* in order to identify target(s).
|
||||
*/
|
||||
Function resolveCall(Call call) {
|
||||
deprecated Function resolveCall(Call call) {
|
||||
result = call.getTarget()
|
||||
or
|
||||
result = call.(DataSensitiveCallExpr).resolve()
|
||||
}
|
||||
|
||||
/** A data sensitive call expression. */
|
||||
abstract library class DataSensitiveCallExpr extends Expr {
|
||||
abstract deprecated library class DataSensitiveCallExpr extends Expr {
|
||||
DataSensitiveCallExpr() { not unreachable(this) }
|
||||
|
||||
abstract Expr getSrc();
|
||||
@@ -579,7 +581,7 @@ abstract library class DataSensitiveCallExpr extends Expr {
|
||||
}
|
||||
|
||||
/** Call through a function pointer. */
|
||||
library class DataSensitiveExprCall extends DataSensitiveCallExpr, ExprCall {
|
||||
deprecated library class DataSensitiveExprCall extends DataSensitiveCallExpr, ExprCall {
|
||||
override Expr getSrc() { result = getExpr() }
|
||||
|
||||
override Function resolve() {
|
||||
@@ -588,7 +590,8 @@ library class DataSensitiveExprCall extends DataSensitiveCallExpr, ExprCall {
|
||||
}
|
||||
|
||||
/** Call to a virtual function. */
|
||||
library class DataSensitiveOverriddenFunctionCall extends DataSensitiveCallExpr, FunctionCall {
|
||||
deprecated library class DataSensitiveOverriddenFunctionCall extends DataSensitiveCallExpr,
|
||||
FunctionCall {
|
||||
DataSensitiveOverriddenFunctionCall() {
|
||||
exists(getTarget().(VirtualFunction).getAnOverridingFunction())
|
||||
}
|
||||
|
||||
@@ -1,4 +1,8 @@
|
||||
/**
|
||||
* DEPRECATED: This library has been replaced with a newer version which
|
||||
* provides better performance and precision. Use
|
||||
* `semmle.code.cpp.valuenumbering.GlobalValueNumbering` instead.
|
||||
*
|
||||
* Provides an implementation of Global Value Numbering.
|
||||
* See https://en.wikipedia.org/wiki/Global_value_numbering
|
||||
*
|
||||
@@ -221,7 +225,7 @@ private newtype GvnBase =
|
||||
* expression with this `GVN` and using its `toString` and `getLocation`
|
||||
* methods.
|
||||
*/
|
||||
class GVN extends GvnBase {
|
||||
deprecated class GVN extends GvnBase {
|
||||
GVN() { this instanceof GvnBase }
|
||||
|
||||
/** Gets an expression that has this GVN. */
|
||||
@@ -503,7 +507,7 @@ private predicate mk_Deref(GVN p, ControlFlowNode dominator, PointerDereferenceE
|
||||
|
||||
/** Gets the global value number of expression `e`. */
|
||||
cached
|
||||
GVN globalValueNumber(Expr e) {
|
||||
deprecated GVN globalValueNumber(Expr e) {
|
||||
exists(int val, Type t |
|
||||
mk_IntConst(val, t, e) and
|
||||
result = GVN_IntConst(val, t)
|
||||
|
||||
@@ -26,11 +26,11 @@ predicate intentionallyReturnsStackPointer(Function f) {
|
||||
class ReturnStackAllocatedMemoryConfig extends MustFlowConfiguration {
|
||||
ReturnStackAllocatedMemoryConfig() { this = "ReturnStackAllocatedMemoryConfig" }
|
||||
|
||||
override predicate isSource(DataFlow::Node source) {
|
||||
override predicate isSource(Instruction source) {
|
||||
// Holds if `source` is a node that represents the use of a stack variable
|
||||
exists(VariableAddressInstruction var, Function func |
|
||||
var = source.asInstruction() and
|
||||
func = var.getEnclosingFunction() and
|
||||
var = source and
|
||||
func = source.getEnclosingFunction() and
|
||||
var.getAstVariable() instanceof StackVariable and
|
||||
// Pointer-to-member types aren't properly handled in the dbscheme.
|
||||
not var.getResultType() instanceof PointerToMemberType and
|
||||
@@ -40,7 +40,7 @@ class ReturnStackAllocatedMemoryConfig extends MustFlowConfiguration {
|
||||
)
|
||||
}
|
||||
|
||||
override predicate isSink(DataFlow::Node sink) {
|
||||
override predicate isSink(Operand sink) {
|
||||
// Holds if `sink` is a node that represents the `StoreInstruction` that is subsequently used in
|
||||
// a `ReturnValueInstruction`.
|
||||
// We use the `StoreInstruction` instead of the instruction that defines the
|
||||
@@ -48,7 +48,7 @@ class ReturnStackAllocatedMemoryConfig extends MustFlowConfiguration {
|
||||
exists(StoreInstruction store |
|
||||
store.getDestinationAddress().(VariableAddressInstruction).getIRVariable() instanceof
|
||||
IRReturnVariable and
|
||||
sink.asOperand() = store.getSourceValueOperand()
|
||||
sink = store.getSourceValueOperand()
|
||||
)
|
||||
}
|
||||
|
||||
@@ -77,10 +77,10 @@ class ReturnStackAllocatedMemoryConfig extends MustFlowConfiguration {
|
||||
* }
|
||||
* ```
|
||||
*/
|
||||
override predicate isAdditionalFlowStep(DataFlow::Node node1, DataFlow::Node node2) {
|
||||
node2.asInstruction().(FieldAddressInstruction).getObjectAddressOperand() = node1.asOperand()
|
||||
override predicate isAdditionalFlowStep(Operand node1, Instruction node2) {
|
||||
node2.(FieldAddressInstruction).getObjectAddressOperand() = node1
|
||||
or
|
||||
node2.asInstruction().(PointerOffsetInstruction).getLeftOperand() = node1.asOperand()
|
||||
node2.(PointerOffsetInstruction).getLeftOperand() = node1
|
||||
}
|
||||
}
|
||||
|
||||
@@ -89,6 +89,6 @@ from
|
||||
ReturnStackAllocatedMemoryConfig conf
|
||||
where
|
||||
conf.hasFlowPath(pragma[only_bind_into](source), pragma[only_bind_into](sink)) and
|
||||
source.getNode().asInstruction() = var
|
||||
select sink.getNode(), source, sink, "May return stack-allocated memory from $@.", var.getAst(),
|
||||
var.getAst().toString()
|
||||
source.getInstruction() = var
|
||||
select sink.getInstruction(), source, sink, "May return stack-allocated memory from $@.",
|
||||
var.getAst(), var.getAst().toString()
|
||||
|
||||
@@ -22,37 +22,40 @@ import PathGraph
|
||||
class UnsafeUseOfThisConfig extends MustFlowConfiguration {
|
||||
UnsafeUseOfThisConfig() { this = "UnsafeUseOfThisConfig" }
|
||||
|
||||
override predicate isSource(DataFlow::Node source) { isSource(source, _, _) }
|
||||
override predicate isSource(Instruction source) { isSource(source, _, _) }
|
||||
|
||||
override predicate isSink(DataFlow::Node sink) { isSink(sink, _) }
|
||||
override predicate isSink(Operand sink) { isSink(sink, _) }
|
||||
}
|
||||
|
||||
/** Holds if `instr` is a `this` pointer used by the call instruction `call`. */
|
||||
predicate isSink(DataFlow::Node sink, CallInstruction call) {
|
||||
/** Holds if `sink` is a `this` pointer used by the call instruction `call`. */
|
||||
predicate isSink(Operand sink, CallInstruction call) {
|
||||
exists(PureVirtualFunction func |
|
||||
call.getStaticCallTarget() = func and
|
||||
call.getThisArgument() = sink.asInstruction() and
|
||||
call.getThisArgumentOperand() = sink and
|
||||
// Weed out implicit calls to destructors of a base class
|
||||
not func instanceof Destructor
|
||||
)
|
||||
}
|
||||
|
||||
/** Holds if `init` initializes the `this` pointer in class `c`. */
|
||||
predicate isSource(DataFlow::Node source, string msg, Class c) {
|
||||
exists(InitializeParameterInstruction init | init = source.asInstruction() |
|
||||
(
|
||||
exists(Constructor func |
|
||||
not func instanceof CopyConstructor and
|
||||
not func instanceof MoveConstructor and
|
||||
func = init.getEnclosingFunction() and
|
||||
msg = "construction"
|
||||
)
|
||||
or
|
||||
init.getEnclosingFunction() instanceof Destructor and msg = "destruction"
|
||||
) and
|
||||
init.getIRVariable() instanceof IRThisVariable and
|
||||
init.getEnclosingFunction().getDeclaringType() = c
|
||||
)
|
||||
/**
|
||||
* Holds if `source` initializes the `this` pointer in class `c`.
|
||||
*
|
||||
* The string `msg` describes whether the enclosing function is a
|
||||
* constructor or destructor.
|
||||
*/
|
||||
predicate isSource(InitializeParameterInstruction source, string msg, Class c) {
|
||||
(
|
||||
exists(Constructor func |
|
||||
not func instanceof CopyConstructor and
|
||||
not func instanceof MoveConstructor and
|
||||
func = source.getEnclosingFunction() and
|
||||
msg = "construction"
|
||||
)
|
||||
or
|
||||
source.getEnclosingFunction() instanceof Destructor and msg = "destruction"
|
||||
) and
|
||||
source.getIRVariable() instanceof IRThisVariable and
|
||||
source.getEnclosingFunction().getDeclaringType() = c
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -68,8 +71,8 @@ predicate flows(
|
||||
) {
|
||||
exists(UnsafeUseOfThisConfig conf |
|
||||
conf.hasFlowPath(source, sink) and
|
||||
isSource(source.getNode(), msg, sourceClass) and
|
||||
isSink(sink.getNode(), call)
|
||||
isSource(source.getInstruction(), msg, sourceClass) and
|
||||
isSink(sink.getInstruction().getAUse(), call)
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@@ -16,9 +16,10 @@
|
||||
|
||||
import cpp
|
||||
import semmle.code.cpp.security.FunctionWithWrappers
|
||||
import semmle.code.cpp.security.Security
|
||||
import semmle.code.cpp.security.TaintTracking
|
||||
import TaintedWithPath
|
||||
import semmle.code.cpp.security.FlowSources
|
||||
import semmle.code.cpp.ir.IR
|
||||
import semmle.code.cpp.ir.dataflow.TaintTracking
|
||||
import DataFlow::PathGraph
|
||||
|
||||
/**
|
||||
* A function for opening a file.
|
||||
@@ -46,19 +47,71 @@ class FileFunction extends FunctionWithWrappers {
|
||||
override predicate interestingArg(int arg) { arg = 0 }
|
||||
}
|
||||
|
||||
class TaintedPathConfiguration extends TaintTrackingConfiguration {
|
||||
override predicate isSink(Element tainted) {
|
||||
exists(FileFunction fileFunction | fileFunction.outermostWrapperFunctionCall(tainted, _))
|
||||
Expr asSinkExpr(DataFlow::Node node) {
|
||||
result =
|
||||
node.asOperand()
|
||||
.(SideEffectOperand)
|
||||
.getUse()
|
||||
.(ReadSideEffectInstruction)
|
||||
.getArgumentDef()
|
||||
.getUnconvertedResultExpression()
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds for a variable that has any kind of upper-bound check anywhere in the program.
|
||||
* This is biased towards being inclusive and being a coarse overapproximation because
|
||||
* there are a lot of valid ways of doing an upper bounds checks if we don't consider
|
||||
* where it occurs, for example:
|
||||
* ```cpp
|
||||
* if (x < 10) { sink(x); }
|
||||
*
|
||||
* if (10 > y) { sink(y); }
|
||||
*
|
||||
* if (z > 10) { z = 10; }
|
||||
* sink(z);
|
||||
* ```
|
||||
*/
|
||||
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"
|
||||
)
|
||||
}
|
||||
|
||||
class TaintedPathConfiguration extends TaintTracking::Configuration {
|
||||
TaintedPathConfiguration() { this = "TaintedPathConfiguration" }
|
||||
|
||||
override predicate isSource(DataFlow::Node node) { node instanceof FlowSource }
|
||||
|
||||
override predicate isSink(DataFlow::Node node) {
|
||||
exists(FileFunction fileFunction |
|
||||
fileFunction.outermostWrapperFunctionCall(asSinkExpr(node), _)
|
||||
)
|
||||
}
|
||||
|
||||
override predicate isSanitizerIn(DataFlow::Node node) { this.isSource(node) }
|
||||
|
||||
override predicate isSanitizer(DataFlow::Node node) {
|
||||
node.asExpr().(Call).getTarget().getUnspecifiedType() instanceof ArithmeticType
|
||||
or
|
||||
exists(LoadInstruction load, Variable checkedVar |
|
||||
load = node.asInstruction() and
|
||||
checkedVar = load.getSourceAddress().(VariableAddressInstruction).getAstVariable() and
|
||||
hasUpperBoundsCheck(checkedVar)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
from
|
||||
FileFunction fileFunction, Expr taintedArg, Expr taintSource, PathNode sourceNode,
|
||||
PathNode sinkNode, string taintCause, string callChain
|
||||
FileFunction fileFunction, Expr taintedArg, FlowSource taintSource, TaintedPathConfiguration cfg,
|
||||
DataFlow::PathNode sourceNode, DataFlow::PathNode sinkNode, string callChain
|
||||
where
|
||||
taintedArg = asSinkExpr(sinkNode.getNode()) and
|
||||
fileFunction.outermostWrapperFunctionCall(taintedArg, callChain) and
|
||||
taintedWithPath(taintSource, taintedArg, sourceNode, sinkNode) and
|
||||
isUserInput(taintSource, taintCause)
|
||||
cfg.hasFlowPath(sourceNode, sinkNode) and
|
||||
taintSource = sourceNode.getNode()
|
||||
select taintedArg, sourceNode, sinkNode,
|
||||
"This argument to a file access function is derived from $@ and then passed to " + callChain + ".",
|
||||
taintSource, "user input (" + taintCause + ")"
|
||||
taintSource, "user input (" + taintSource.getSourceType() + ")"
|
||||
|
||||
@@ -13,7 +13,7 @@
|
||||
|
||||
import cpp
|
||||
import semmle.code.cpp.commons.Environment
|
||||
import semmle.code.cpp.security.TaintTracking
|
||||
import semmle.code.cpp.ir.dataflow.internal.DefaultTaintTrackingImpl
|
||||
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.security.TaintTracking
|
||||
import semmle.code.cpp.ir.dataflow.internal.DefaultTaintTrackingImpl
|
||||
import TaintedWithPath
|
||||
|
||||
class SqlLikeFunction extends FunctionWithWrappers {
|
||||
|
||||
@@ -14,7 +14,7 @@
|
||||
|
||||
import cpp
|
||||
import semmle.code.cpp.security.Security
|
||||
import semmle.code.cpp.security.TaintTracking
|
||||
import semmle.code.cpp.ir.dataflow.internal.DefaultTaintTrackingImpl
|
||||
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.security.TaintTracking
|
||||
import semmle.code.cpp.ir.dataflow.internal.DefaultTaintTrackingImpl
|
||||
import TaintedWithPath
|
||||
|
||||
/*
|
||||
|
||||
@@ -116,10 +116,6 @@ 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
|
||||
@@ -128,4 +124,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(), lowerFirst(sourceType)
|
||||
source.getNode(), sourceType
|
||||
|
||||
@@ -16,7 +16,7 @@
|
||||
import cpp
|
||||
import semmle.code.cpp.security.Security
|
||||
import semmle.code.cpp.security.FunctionWithWrappers
|
||||
import semmle.code.cpp.security.TaintTracking
|
||||
import semmle.code.cpp.ir.dataflow.internal.DefaultTaintTrackingImpl
|
||||
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.security.TaintTracking
|
||||
import semmle.code.cpp.ir.dataflow.internal.DefaultTaintTrackingImpl
|
||||
import TaintedWithPath
|
||||
|
||||
class Configuration extends TaintTrackingConfiguration {
|
||||
|
||||
@@ -12,7 +12,7 @@
|
||||
|
||||
import cpp
|
||||
import semmle.code.cpp.commons.NullTermination
|
||||
import semmle.code.cpp.security.TaintTracking
|
||||
import semmle.code.cpp.ir.dataflow.internal.DefaultTaintTrackingImpl
|
||||
|
||||
/** 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.security.TaintTracking
|
||||
import semmle.code.cpp.ir.dataflow.internal.DefaultTaintTrackingImpl
|
||||
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.security.TaintTracking
|
||||
import semmle.code.cpp.ir.dataflow.internal.DefaultTaintTrackingImpl
|
||||
|
||||
predicate isMaxValue(Expr mie) {
|
||||
exists(MacroInvocation mi |
|
||||
|
||||
@@ -15,7 +15,7 @@
|
||||
|
||||
import cpp
|
||||
import semmle.code.cpp.rangeanalysis.SimpleRangeAnalysis
|
||||
import semmle.code.cpp.security.TaintTracking
|
||||
import semmle.code.cpp.ir.dataflow.internal.DefaultTaintTrackingImpl
|
||||
|
||||
/** Holds if `expr` might overflow. */
|
||||
predicate outOfBoundsExpr(Expr expr, string kind) {
|
||||
|
||||
@@ -12,7 +12,7 @@
|
||||
* external/cwe/cwe-290
|
||||
*/
|
||||
|
||||
import semmle.code.cpp.security.TaintTracking
|
||||
import semmle.code.cpp.ir.dataflow.internal.DefaultTaintTrackingImpl
|
||||
import TaintedWithPath
|
||||
|
||||
predicate hardCodedAddressOrIP(StringLiteral txt) {
|
||||
|
||||
@@ -19,7 +19,25 @@ import semmle.code.cpp.ir.dataflow.TaintTracking
|
||||
import DataFlow::PathGraph
|
||||
|
||||
/**
|
||||
* A taint flow configuration for flow from user input to a buffer write.
|
||||
* 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.
|
||||
*/
|
||||
class ToBufferConfiguration extends TaintTracking::Configuration {
|
||||
ToBufferConfiguration() { this = "ToBufferConfiguration" }
|
||||
@@ -31,18 +49,17 @@ class ToBufferConfiguration extends TaintTracking::Configuration {
|
||||
}
|
||||
|
||||
override predicate isSink(DataFlow::Node sink) {
|
||||
exists(BufferWrite::BufferWrite w | w.getASource() = sink.asExpr())
|
||||
exists(SensitiveBufferWrite w | w.getASource() = sink.asExpr())
|
||||
}
|
||||
}
|
||||
|
||||
from
|
||||
ToBufferConfiguration config, BufferWrite::BufferWrite w, DataFlow::PathNode sourceNode,
|
||||
DataFlow::PathNode sinkNode, FlowSource source, SensitiveExpr dest
|
||||
ToBufferConfiguration config, SensitiveBufferWrite w, DataFlow::PathNode sourceNode,
|
||||
DataFlow::PathNode sinkNode, FlowSource source
|
||||
where
|
||||
config.hasFlowPath(sourceNode, sinkNode) and
|
||||
sourceNode.getNode() = source and
|
||||
w.getASource() = sinkNode.getNode().asExpr() and
|
||||
dest = w.getDest()
|
||||
w.getASource() = sinkNode.getNode().asExpr()
|
||||
select w, sourceNode, sinkNode,
|
||||
"This write into buffer '" + dest.toString() + "' may contain unencrypted data from $@.", source,
|
||||
"user input (" + source.getSourceType() + ")"
|
||||
"This write into buffer '" + w.getDest().toString() + "' may contain unencrypted data from $@.",
|
||||
source, "user input (" + source.getSourceType() + ")"
|
||||
|
||||
@@ -12,7 +12,7 @@
|
||||
* external/cwe/cwe-807
|
||||
*/
|
||||
|
||||
import semmle.code.cpp.security.TaintTracking
|
||||
import semmle.code.cpp.ir.dataflow.internal.DefaultTaintTrackingImpl
|
||||
import TaintedWithPath
|
||||
|
||||
predicate sensitiveCondition(Expr condition, Expr raise) {
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
/*
|
||||
/**
|
||||
* Common functions for implementing naming conventions
|
||||
*
|
||||
* Naming rules are the following:
|
||||
|
||||
@@ -0,0 +1,2 @@
|
||||
WARNING: Module TaintedWithPath has been deprecated and may be removed in future (tainted.ql:9,8-47)
|
||||
WARNING: Predicate tainted has been deprecated and may be removed in future (tainted.ql:20,49-74)
|
||||
|
||||
@@ -1,18 +1,6 @@
|
||||
#include "../shared.h"
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
int main() {
|
||||
|
||||
|
||||
|
||||
sink(_strdup(getenv("VAR"))); // $ ir MISSING: ast
|
||||
sink(strdup(getenv("VAR"))); // $ ast,ir
|
||||
sink(unmodeled_function(getenv("VAR"))); // clean by assumption
|
||||
@@ -59,9 +47,6 @@ void test_outparams() {
|
||||
sink(p2); // $ ir MISSING: ast
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
struct XY {
|
||||
int x;
|
||||
int y;
|
||||
@@ -230,24 +215,17 @@ void test_recv() {
|
||||
|
||||
// --- send and related functions ---
|
||||
|
||||
int send(int, const void*, int, int);
|
||||
|
||||
void test_send(char* buffer, int length) {
|
||||
send(0, buffer, length, 0); // $ remote
|
||||
}
|
||||
|
||||
struct iovec {
|
||||
void *iov_base;
|
||||
unsigned iov_len;
|
||||
};
|
||||
|
||||
int readv(int, const struct iovec*, int);
|
||||
int writev(int, const struct iovec*, int);
|
||||
|
||||
void sink(const iovec* iovs);
|
||||
void sink(iovec);
|
||||
|
||||
int test_readv_and_writev(iovec* iovs) {
|
||||
void test_readv_and_writev(iovec* iovs) {
|
||||
readv(0, iovs, 16);
|
||||
sink(iovs); // $ast,ir
|
||||
sink(iovs[0]); // $ast,ir
|
||||
@@ -256,6 +234,4 @@ int test_readv_and_writev(iovec* iovs) {
|
||||
char* p = (char*)iovs[1].iov_base;
|
||||
sink(p); // $ MISSING: ast,ir
|
||||
sink(*p); // $ MISSING: ast,ir
|
||||
|
||||
writev(0, iovs, 16); // $ remote
|
||||
}
|
||||
|
||||
@@ -1,20 +0,0 @@
|
||||
/** This tests that we are able to detect remote flow sinks. */
|
||||
|
||||
import cpp
|
||||
import TestUtilities.InlineExpectationsTest
|
||||
import semmle.code.cpp.security.FlowSources
|
||||
|
||||
class RemoteFlowSinkTest extends InlineExpectationsTest {
|
||||
RemoteFlowSinkTest() { this = "RemoteFlowSinkTest" }
|
||||
|
||||
override string getARelevantTag() { result = "remote" }
|
||||
|
||||
override predicate hasActualResult(Location location, string element, string tag, string value) {
|
||||
tag = "remote" and
|
||||
value = "" and
|
||||
exists(RemoteFlowSink node |
|
||||
location = node.getLocation() and
|
||||
element = node.toString()
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
WARNING: Module TaintedWithPath has been deprecated and may be removed in future (tainted.ql:10,8-47)
|
||||
WARNING: Predicate tainted has been deprecated and may be removed in future (tainted.ql:21,3-28)
|
||||
|
||||
@@ -0,0 +1,2 @@
|
||||
WARNING: Predicate taintedIncludingGlobalVars has been deprecated and may be removed in future (global.ql:8,3-47)
|
||||
WARNING: Predicate taintedIncludingGlobalVars has been deprecated and may be removed in future (global.ql:12,3-53)
|
||||
|
||||
@@ -93,3 +93,5 @@ postWithInFlow
|
||||
| test.cpp:499:4:499:4 | p [inner post update] | PostUpdateNode should not be the target of local flow. |
|
||||
| test.cpp:505:35:505:35 | x [inner post update] | PostUpdateNode should not be the target of local flow. |
|
||||
viableImplInCallContextTooLarge
|
||||
uniqueParameterNodeAtPosition
|
||||
uniqueParameterNodePosition
|
||||
|
||||
@@ -637,3 +637,5 @@ postWithInFlow
|
||||
| true_upon_entry.cpp:101:18:101:18 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
|
||||
| true_upon_entry.cpp:102:5:102:5 | x [post update] | PostUpdateNode should not be the target of local flow. |
|
||||
viableImplInCallContextTooLarge
|
||||
uniqueParameterNodeAtPosition
|
||||
uniqueParameterNodePosition
|
||||
|
||||
@@ -158,3 +158,5 @@ postWithInFlow
|
||||
| struct_init.c:24:11:24:12 | ab [inner post update] | PostUpdateNode should not be the target of local flow. |
|
||||
| struct_init.c:36:17:36:24 | nestedAB [inner post update] | PostUpdateNode should not be the target of local flow. |
|
||||
viableImplInCallContextTooLarge
|
||||
uniqueParameterNodeAtPosition
|
||||
uniqueParameterNodePosition
|
||||
|
||||
@@ -1326,3 +1326,5 @@ postWithInFlow
|
||||
| struct_init.c:46:16:46:24 | pointerAB [post update] | PostUpdateNode should not be the target of local flow. |
|
||||
| struct_init.c:46:16:46:24 | pointerAB [post update] | PostUpdateNode should not be the target of local flow. |
|
||||
viableImplInCallContextTooLarge
|
||||
uniqueParameterNodeAtPosition
|
||||
uniqueParameterNodePosition
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
WARNING: Predicate taintedIncludingGlobalVars has been deprecated and may be removed in future (tainted.ql:5,3-29)
|
||||
| test.cpp:23:23:23:28 | call to getenv | test.cpp:8:24:8:25 | s1 | |
|
||||
| test.cpp:23:23:23:28 | call to getenv | test.cpp:23:14:23:19 | envStr | |
|
||||
| test.cpp:23:23:23:28 | call to getenv | test.cpp:23:23:23:28 | call to getenv | |
|
||||
|
||||
@@ -1,3 +1,8 @@
|
||||
WARNING: Module TaintedWithPath has been deprecated and may be removed in future (tainted_diff.ql:5,35-54)
|
||||
WARNING: Module TaintedWithPath has been deprecated and may be removed in future (tainted_diff.ql:12,7-26)
|
||||
WARNING: Module TaintedWithPath has been deprecated and may be removed in future (tainted_diff.ql:16,3-22)
|
||||
WARNING: Predicate taintedIncludingGlobalVars has been deprecated and may be removed in future (tainted_diff.ql:11,3-34)
|
||||
WARNING: Predicate taintedIncludingGlobalVars has been deprecated and may be removed in future (tainted_diff.ql:17,7-38)
|
||||
| test.cpp:23:23:23:28 | call to getenv | test.cpp:8:24:8:25 | s1 | AST only |
|
||||
| test.cpp:23:23:23:28 | call to getenv | test.cpp:23:14:23:19 | envStr | AST only |
|
||||
| test.cpp:38:23:38:28 | call to getenv | test.cpp:8:24:8:25 | s1 | AST only |
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
WARNING: Module TaintedWithPath has been deprecated and may be removed in future (tainted_ir.ql:3,35-50)
|
||||
WARNING: Module TaintedWithPath has been deprecated and may be removed in future (tainted_ir.ql:9,3-18)
|
||||
| test.cpp:23:23:23:28 | call to getenv | test.cpp:23:23:23:28 | call to getenv |
|
||||
| test.cpp:23:23:23:28 | call to getenv | test.cpp:23:23:23:40 | (const char *)... |
|
||||
| test.cpp:23:23:23:28 | call to getenv | test.cpp:25:6:25:29 | ! ... |
|
||||
|
||||
@@ -0,0 +1,32 @@
|
||||
/** This tests that we are able to detect local flow sources. */
|
||||
|
||||
import cpp
|
||||
import TestUtilities.InlineExpectationsTest
|
||||
import semmle.code.cpp.security.FlowSources
|
||||
|
||||
class LocalFlowSourceTest extends InlineExpectationsTest {
|
||||
LocalFlowSourceTest() { this = "LocalFlowSourceTest" }
|
||||
|
||||
override string getARelevantTag() { result = "local_source" }
|
||||
|
||||
override predicate hasActualResult(Location location, string element, string tag, string value) {
|
||||
tag = "local_source" and
|
||||
exists(LocalFlowSource node, int n |
|
||||
n =
|
||||
strictcount(LocalFlowSource otherNode |
|
||||
node.getLocation().getStartLine() = otherNode.getLocation().getStartLine()
|
||||
) and
|
||||
(
|
||||
n = 1 and value = ""
|
||||
or
|
||||
// If there is more than one node on this line
|
||||
// we specify the location explicitly.
|
||||
n > 1 and
|
||||
value =
|
||||
node.getLocation().getStartLine().toString() + ":" + node.getLocation().getStartColumn()
|
||||
) and
|
||||
location = node.getLocation() and
|
||||
element = node.toString()
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,59 @@
|
||||
/** This tests that we are able to detect remote flow sources and sinks. */
|
||||
|
||||
import cpp
|
||||
import TestUtilities.InlineExpectationsTest
|
||||
import semmle.code.cpp.security.FlowSources
|
||||
|
||||
class RemoteFlowSourceTest extends InlineExpectationsTest {
|
||||
RemoteFlowSourceTest() { this = "RemoteFlowSourceTest" }
|
||||
|
||||
override string getARelevantTag() { result = "remote_source" }
|
||||
|
||||
override predicate hasActualResult(Location location, string element, string tag, string value) {
|
||||
tag = "remote_source" and
|
||||
exists(RemoteFlowSource node, int n |
|
||||
n =
|
||||
strictcount(RemoteFlowSource otherNode |
|
||||
node.getLocation().getStartLine() = otherNode.getLocation().getStartLine()
|
||||
) and
|
||||
(
|
||||
n = 1 and value = ""
|
||||
or
|
||||
// If there is more than one node on this line
|
||||
// we specify the location explicitly.
|
||||
n > 1 and
|
||||
value =
|
||||
node.getLocation().getStartLine().toString() + ":" + node.getLocation().getStartColumn()
|
||||
) and
|
||||
location = node.getLocation() and
|
||||
element = node.toString()
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
class RemoteFlowSinkTest extends InlineExpectationsTest {
|
||||
RemoteFlowSinkTest() { this = "RemoteFlowSinkTest" }
|
||||
|
||||
override string getARelevantTag() { result = "remote_sink" }
|
||||
|
||||
override predicate hasActualResult(Location location, string element, string tag, string value) {
|
||||
tag = "remote_sink" and
|
||||
exists(RemoteFlowSink node, int n |
|
||||
n =
|
||||
strictcount(RemoteFlowSink otherNode |
|
||||
node.getLocation().getStartLine() = otherNode.getLocation().getStartLine()
|
||||
) and
|
||||
(
|
||||
n = 1 and value = ""
|
||||
or
|
||||
// If there is more than one node on this line
|
||||
// we specify the location explicitly.
|
||||
n > 1 and
|
||||
value =
|
||||
node.getLocation().getStartLine().toString() + ":" + node.getLocation().getStartColumn()
|
||||
) and
|
||||
location = node.getLocation() and
|
||||
element = node.toString()
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,52 @@
|
||||
char *getenv(const char *name);
|
||||
char *secure_getenv(const char *name);
|
||||
wchar_t *_wgetenv(const wchar_t *name);
|
||||
|
||||
void test_getenv() {
|
||||
void *var1 = getenv("VAR"); // $ local_source
|
||||
void *var2 = secure_getenv("VAR"); // $ local_source
|
||||
void *var3 = _wgetenv(L"VAR"); // $ local_source
|
||||
}
|
||||
|
||||
int send(int, const void*, int, int);
|
||||
|
||||
void test_send(char* buffer, int length) {
|
||||
send(0, buffer, length, 0); // $ remote_sink
|
||||
}
|
||||
|
||||
struct iovec {
|
||||
void *iov_base;
|
||||
unsigned iov_len;
|
||||
};
|
||||
|
||||
int readv(int, const struct iovec*, int);
|
||||
int writev(int, const struct iovec*, int);
|
||||
|
||||
void test_readv_and_writev(iovec* iovs) {
|
||||
readv(0, iovs, 16); // $ remote_source
|
||||
writev(0, iovs, 16); // $ remote_sink
|
||||
}
|
||||
|
||||
struct FILE;
|
||||
|
||||
int fscanf(FILE *stream, const char *format, ...);
|
||||
int scanf(const char *format, ...);
|
||||
|
||||
void test_scanf(FILE *stream, int *d, char *buf) {
|
||||
scanf(""); // Not a local source, as there are no output arguments
|
||||
fscanf(stream, ""); // Not a remote source, as there are no output arguments
|
||||
scanf("%d", d); // $ local_source
|
||||
fscanf(stream, "%d", d); // $ remote_source
|
||||
scanf("%d %s", d, buf); // $ local_source=40:18 local_source=40:21
|
||||
fscanf(stream, "%d %s", d, buf); // $ remote_source=41:27 remote_source=41:30
|
||||
}
|
||||
|
||||
struct addrinfo;
|
||||
|
||||
int getaddrinfo(const char *hostname, const char *servname,
|
||||
const struct addrinfo *hints, struct addrinfo **res);
|
||||
|
||||
void test_inet(char *hostname, char *servname, struct addrinfo *hints) {
|
||||
addrinfo *res;
|
||||
int ret = getaddrinfo(hostname, servname, hints, &res); // $ remote_source
|
||||
}
|
||||
@@ -127,3 +127,5 @@ postWithInFlow
|
||||
| static_init_templates.cpp:21:2:21:4 | val [post update] | PostUpdateNode should not be the target of local flow. |
|
||||
| try_catch.cpp:7:8:7:8 | call to exception | PostUpdateNode should not be the target of local flow. |
|
||||
viableImplInCallContextTooLarge
|
||||
uniqueParameterNodeAtPosition
|
||||
uniqueParameterNodePosition
|
||||
|
||||
@@ -2713,3 +2713,7 @@ postWithInFlow
|
||||
| whilestmt.c:40:7:40:7 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
|
||||
| whilestmt.c:42:7:42:7 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
|
||||
viableImplInCallContextTooLarge
|
||||
uniqueParameterNodeAtPosition
|
||||
| ir.cpp:724:6:724:13 | TryCatch | 0 | ir.cpp:735:22:735:22 | *s | Parameters with overlapping positions. |
|
||||
| ir.cpp:724:6:724:13 | TryCatch | 0 | ir.cpp:738:24:738:24 | *e | Parameters with overlapping positions. |
|
||||
uniqueParameterNodePosition
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
WARNING: Type GVN has been deprecated and may be removed in future (ast_gvn.ql:4,6-9)
|
||||
| test.cpp:5:3:5:3 | x | 5:c3-c3 6:c3-c3 |
|
||||
| test.cpp:5:7:5:8 | p0 | 5:c7-c8 6:c7-c8 |
|
||||
| test.cpp:5:7:5:13 | ... + ... | 5:c7-c13 6:c7-c13 7:c7-c7 |
|
||||
|
||||
@@ -0,0 +1,3 @@
|
||||
WARNING: Predicate globalValueNumber has been deprecated and may be removed in future (ast_uniqueness.ql:7,13-30)
|
||||
WARNING: Predicate globalValueNumber has been deprecated and may be removed in future (ast_uniqueness.ql:8,30-47)
|
||||
WARNING: Type GVN has been deprecated and may be removed in future (ast_uniqueness.ql:8,18-21)
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
WARNING: Predicate globalValueNumber has been deprecated and may be removed in future (diff_ir_expr.ql:8,29-51)
|
||||
| test.cpp:5:3:5:13 | ... = ... | test.cpp:5:3:5:13 | ... = ... | AST only |
|
||||
| test.cpp:6:3:6:13 | ... = ... | test.cpp:6:3:6:13 | ... = ... | AST only |
|
||||
| test.cpp:7:3:7:7 | ... = ... | test.cpp:7:3:7:7 | ... = ... | AST only |
|
||||
|
||||
@@ -1,103 +1,61 @@
|
||||
edges
|
||||
| test.cpp:7:3:7:3 | this | test.cpp:8:12:8:15 | Load |
|
||||
| test.cpp:8:12:8:15 | Load | test.cpp:8:12:8:15 | this |
|
||||
| test.cpp:7:3:7:3 | B | test.cpp:8:12:8:15 | this |
|
||||
| test.cpp:8:12:8:15 | this | test.cpp:34:16:34:16 | x |
|
||||
| test.cpp:11:8:11:8 | b | test.cpp:12:5:12:5 | Load |
|
||||
| test.cpp:12:5:12:5 | (reference dereference) | test.cpp:12:5:12:5 | Unary |
|
||||
| test.cpp:12:5:12:5 | Load | test.cpp:12:5:12:5 | b |
|
||||
| test.cpp:12:5:12:5 | Unary | test.cpp:12:5:12:5 | (A)... |
|
||||
| test.cpp:12:5:12:5 | Unary | test.cpp:12:5:12:5 | (reference dereference) |
|
||||
| test.cpp:12:5:12:5 | b | test.cpp:12:5:12:5 | Unary |
|
||||
| test.cpp:15:3:15:4 | this | test.cpp:16:5:16:5 | Load |
|
||||
| test.cpp:16:5:16:5 | Load | test.cpp:16:5:16:5 | this |
|
||||
| test.cpp:16:5:16:5 | Unary | file://:0:0:0:0 | (A *)... |
|
||||
| test.cpp:16:5:16:5 | this | test.cpp:16:5:16:5 | Unary |
|
||||
| test.cpp:21:3:21:3 | Unary | test.cpp:21:13:21:13 | ConvertToNonVirtualBase |
|
||||
| test.cpp:21:3:21:3 | this | test.cpp:21:3:21:3 | Unary |
|
||||
| test.cpp:21:3:21:3 | this | test.cpp:22:12:22:15 | Load |
|
||||
| test.cpp:21:3:21:3 | this | test.cpp:25:7:25:10 | Load |
|
||||
| test.cpp:21:13:21:13 | ConvertToNonVirtualBase | test.cpp:7:3:7:3 | this |
|
||||
| test.cpp:11:8:11:8 | b | test.cpp:12:5:12:5 | b |
|
||||
| test.cpp:12:5:12:5 | (reference dereference) | test.cpp:12:5:12:5 | (A)... |
|
||||
| test.cpp:12:5:12:5 | b | test.cpp:12:5:12:5 | (reference dereference) |
|
||||
| test.cpp:15:3:15:4 | ~B | test.cpp:16:5:16:5 | this |
|
||||
| test.cpp:16:5:16:5 | this | file://:0:0:0:0 | (A *)... |
|
||||
| test.cpp:21:3:21:3 | C | test.cpp:21:13:21:13 | call to B |
|
||||
| test.cpp:21:3:21:3 | C | test.cpp:22:12:22:15 | this |
|
||||
| test.cpp:21:3:21:3 | C | test.cpp:25:7:25:10 | this |
|
||||
| test.cpp:21:13:21:13 | call to B | test.cpp:7:3:7:3 | B |
|
||||
| test.cpp:22:12:22:15 | (B *)... | test.cpp:34:16:34:16 | x |
|
||||
| test.cpp:22:12:22:15 | Load | test.cpp:22:12:22:15 | this |
|
||||
| test.cpp:22:12:22:15 | Unary | test.cpp:22:12:22:15 | (B *)... |
|
||||
| test.cpp:22:12:22:15 | this | test.cpp:22:12:22:15 | Unary |
|
||||
| test.cpp:25:7:25:10 | (B *)... | test.cpp:25:7:25:10 | Unary |
|
||||
| test.cpp:25:7:25:10 | Load | test.cpp:25:7:25:10 | this |
|
||||
| test.cpp:25:7:25:10 | Unary | test.cpp:25:7:25:10 | (A *)... |
|
||||
| test.cpp:25:7:25:10 | Unary | test.cpp:25:7:25:10 | (B *)... |
|
||||
| test.cpp:25:7:25:10 | this | test.cpp:25:7:25:10 | Unary |
|
||||
| test.cpp:31:3:31:3 | this | test.cpp:31:12:31:15 | Load |
|
||||
| test.cpp:31:11:31:15 | (B)... | test.cpp:31:11:31:15 | Unary |
|
||||
| test.cpp:22:12:22:15 | this | test.cpp:22:12:22:15 | (B *)... |
|
||||
| test.cpp:25:7:25:10 | (B *)... | test.cpp:25:7:25:10 | (A *)... |
|
||||
| test.cpp:25:7:25:10 | this | test.cpp:25:7:25:10 | (B *)... |
|
||||
| test.cpp:31:3:31:3 | D | test.cpp:31:12:31:15 | this |
|
||||
| test.cpp:31:11:31:15 | (B)... | test.cpp:31:11:31:15 | (reference to) |
|
||||
| test.cpp:31:11:31:15 | (reference to) | test.cpp:11:8:11:8 | b |
|
||||
| test.cpp:31:11:31:15 | * ... | test.cpp:31:11:31:15 | Unary |
|
||||
| test.cpp:31:11:31:15 | Unary | test.cpp:31:11:31:15 | (B)... |
|
||||
| test.cpp:31:11:31:15 | Unary | test.cpp:31:11:31:15 | (reference to) |
|
||||
| test.cpp:31:12:31:15 | Load | test.cpp:31:12:31:15 | this |
|
||||
| test.cpp:31:12:31:15 | Unary | test.cpp:31:11:31:15 | * ... |
|
||||
| test.cpp:31:12:31:15 | this | test.cpp:31:12:31:15 | Unary |
|
||||
| test.cpp:34:16:34:16 | x | test.cpp:35:3:35:3 | Load |
|
||||
| test.cpp:35:3:35:3 | Load | test.cpp:35:3:35:3 | x |
|
||||
| test.cpp:35:3:35:3 | Unary | test.cpp:35:3:35:3 | (A *)... |
|
||||
| test.cpp:35:3:35:3 | x | test.cpp:35:3:35:3 | Unary |
|
||||
| test.cpp:47:3:47:3 | this | test.cpp:48:10:48:13 | Load |
|
||||
| test.cpp:48:10:48:13 | (E *)... | test.cpp:48:10:48:13 | Unary |
|
||||
| test.cpp:48:10:48:13 | Load | test.cpp:48:10:48:13 | this |
|
||||
| test.cpp:48:10:48:13 | Unary | test.cpp:48:6:48:13 | (A *)... |
|
||||
| test.cpp:48:10:48:13 | Unary | test.cpp:48:10:48:13 | (E *)... |
|
||||
| test.cpp:48:10:48:13 | this | test.cpp:48:10:48:13 | Unary |
|
||||
| test.cpp:31:11:31:15 | * ... | test.cpp:31:11:31:15 | (B)... |
|
||||
| test.cpp:31:12:31:15 | this | test.cpp:31:11:31:15 | * ... |
|
||||
| test.cpp:34:16:34:16 | x | test.cpp:35:3:35:3 | x |
|
||||
| test.cpp:35:3:35:3 | x | test.cpp:35:3:35:3 | (A *)... |
|
||||
| test.cpp:47:3:47:3 | F | test.cpp:48:10:48:13 | this |
|
||||
| test.cpp:48:10:48:13 | (E *)... | test.cpp:48:6:48:13 | (A *)... |
|
||||
| test.cpp:48:10:48:13 | this | test.cpp:48:10:48:13 | (E *)... |
|
||||
nodes
|
||||
| file://:0:0:0:0 | (A *)... | semmle.label | (A *)... |
|
||||
| test.cpp:7:3:7:3 | this | semmle.label | this |
|
||||
| test.cpp:8:12:8:15 | Load | semmle.label | Load |
|
||||
| test.cpp:7:3:7:3 | B | semmle.label | B |
|
||||
| test.cpp:8:12:8:15 | this | semmle.label | this |
|
||||
| test.cpp:11:8:11:8 | b | semmle.label | b |
|
||||
| test.cpp:12:5:12:5 | (A)... | semmle.label | (A)... |
|
||||
| test.cpp:12:5:12:5 | (reference dereference) | semmle.label | (reference dereference) |
|
||||
| test.cpp:12:5:12:5 | Load | semmle.label | Load |
|
||||
| test.cpp:12:5:12:5 | Unary | semmle.label | Unary |
|
||||
| test.cpp:12:5:12:5 | Unary | semmle.label | Unary |
|
||||
| test.cpp:12:5:12:5 | b | semmle.label | b |
|
||||
| test.cpp:15:3:15:4 | this | semmle.label | this |
|
||||
| test.cpp:16:5:16:5 | Load | semmle.label | Load |
|
||||
| test.cpp:16:5:16:5 | Unary | semmle.label | Unary |
|
||||
| test.cpp:15:3:15:4 | ~B | semmle.label | ~B |
|
||||
| test.cpp:16:5:16:5 | this | semmle.label | this |
|
||||
| test.cpp:21:3:21:3 | Unary | semmle.label | Unary |
|
||||
| test.cpp:21:3:21:3 | this | semmle.label | this |
|
||||
| test.cpp:21:13:21:13 | ConvertToNonVirtualBase | semmle.label | ConvertToNonVirtualBase |
|
||||
| test.cpp:21:3:21:3 | C | semmle.label | C |
|
||||
| test.cpp:21:13:21:13 | call to B | semmle.label | call to B |
|
||||
| test.cpp:22:12:22:15 | (B *)... | semmle.label | (B *)... |
|
||||
| test.cpp:22:12:22:15 | Load | semmle.label | Load |
|
||||
| test.cpp:22:12:22:15 | Unary | semmle.label | Unary |
|
||||
| test.cpp:22:12:22:15 | this | semmle.label | this |
|
||||
| test.cpp:25:7:25:10 | (A *)... | semmle.label | (A *)... |
|
||||
| test.cpp:25:7:25:10 | (B *)... | semmle.label | (B *)... |
|
||||
| test.cpp:25:7:25:10 | Load | semmle.label | Load |
|
||||
| test.cpp:25:7:25:10 | Unary | semmle.label | Unary |
|
||||
| test.cpp:25:7:25:10 | Unary | semmle.label | Unary |
|
||||
| test.cpp:25:7:25:10 | this | semmle.label | this |
|
||||
| test.cpp:31:3:31:3 | this | semmle.label | this |
|
||||
| test.cpp:31:3:31:3 | D | semmle.label | D |
|
||||
| test.cpp:31:11:31:15 | (B)... | semmle.label | (B)... |
|
||||
| test.cpp:31:11:31:15 | (reference to) | semmle.label | (reference to) |
|
||||
| test.cpp:31:11:31:15 | * ... | semmle.label | * ... |
|
||||
| test.cpp:31:11:31:15 | Unary | semmle.label | Unary |
|
||||
| test.cpp:31:11:31:15 | Unary | semmle.label | Unary |
|
||||
| test.cpp:31:12:31:15 | Load | semmle.label | Load |
|
||||
| test.cpp:31:12:31:15 | Unary | semmle.label | Unary |
|
||||
| test.cpp:31:12:31:15 | this | semmle.label | this |
|
||||
| test.cpp:34:16:34:16 | x | semmle.label | x |
|
||||
| test.cpp:35:3:35:3 | (A *)... | semmle.label | (A *)... |
|
||||
| test.cpp:35:3:35:3 | Load | semmle.label | Load |
|
||||
| test.cpp:35:3:35:3 | Unary | semmle.label | Unary |
|
||||
| test.cpp:35:3:35:3 | x | semmle.label | x |
|
||||
| test.cpp:47:3:47:3 | this | semmle.label | this |
|
||||
| test.cpp:47:3:47:3 | F | semmle.label | F |
|
||||
| test.cpp:48:6:48:13 | (A *)... | semmle.label | (A *)... |
|
||||
| test.cpp:48:10:48:13 | (E *)... | semmle.label | (E *)... |
|
||||
| test.cpp:48:10:48:13 | Load | semmle.label | Load |
|
||||
| test.cpp:48:10:48:13 | Unary | semmle.label | Unary |
|
||||
| test.cpp:48:10:48:13 | Unary | semmle.label | Unary |
|
||||
| test.cpp:48:10:48:13 | this | semmle.label | this |
|
||||
#select
|
||||
| test.cpp:12:7:12:7 | call to f | test.cpp:31:3:31:3 | this | test.cpp:12:5:12:5 | (A)... | Call to pure virtual function during construction. |
|
||||
| test.cpp:16:5:16:5 | call to f | test.cpp:15:3:15:4 | this | file://:0:0:0:0 | (A *)... | Call to pure virtual function during destruction. |
|
||||
| test.cpp:25:13:25:13 | call to f | test.cpp:21:3:21:3 | this | test.cpp:25:7:25:10 | (A *)... | Call to pure virtual function during construction. |
|
||||
| test.cpp:35:6:35:6 | call to f | test.cpp:7:3:7:3 | this | test.cpp:35:3:35:3 | (A *)... | Call to pure virtual function during construction. |
|
||||
| test.cpp:35:6:35:6 | call to f | test.cpp:21:3:21:3 | this | test.cpp:35:3:35:3 | (A *)... | Call to pure virtual function during construction. |
|
||||
| test.cpp:12:7:12:7 | call to f | test.cpp:31:3:31:3 | D | test.cpp:12:5:12:5 | (A)... | Call to pure virtual function during construction. |
|
||||
| test.cpp:16:5:16:5 | call to f | test.cpp:15:3:15:4 | ~B | file://:0:0:0:0 | (A *)... | Call to pure virtual function during destruction. |
|
||||
| test.cpp:25:13:25:13 | call to f | test.cpp:21:3:21:3 | C | test.cpp:25:7:25:10 | (A *)... | Call to pure virtual function during construction. |
|
||||
| test.cpp:35:6:35:6 | call to f | test.cpp:7:3:7:3 | B | test.cpp:35:3:35:3 | (A *)... | Call to pure virtual function during construction. |
|
||||
| test.cpp:35:6:35:6 | call to f | test.cpp:21:3:21:3 | C | test.cpp:35:3:35:3 | (A *)... | Call to pure virtual function during construction. |
|
||||
|
||||
@@ -1,231 +1,117 @@
|
||||
edges
|
||||
| test.cpp:17:9:17:11 | & ... | test.cpp:17:9:17:11 | StoreValue |
|
||||
| test.cpp:17:10:17:11 | Unary | test.cpp:17:9:17:11 | & ... |
|
||||
| test.cpp:17:10:17:11 | mc | test.cpp:17:10:17:11 | Unary |
|
||||
| test.cpp:23:17:23:19 | & ... | test.cpp:23:17:23:19 | StoreValue |
|
||||
| test.cpp:23:17:23:19 | Store | test.cpp:25:9:25:11 | Load |
|
||||
| test.cpp:23:17:23:19 | StoreValue | test.cpp:23:17:23:19 | Store |
|
||||
| test.cpp:23:18:23:19 | Unary | test.cpp:23:17:23:19 | & ... |
|
||||
| test.cpp:23:18:23:19 | mc | test.cpp:23:18:23:19 | Unary |
|
||||
| test.cpp:25:9:25:11 | Load | test.cpp:25:9:25:11 | ptr |
|
||||
| test.cpp:25:9:25:11 | ptr | test.cpp:25:9:25:11 | StoreValue |
|
||||
| test.cpp:39:17:39:18 | (reference to) | test.cpp:39:17:39:18 | StoreValue |
|
||||
| test.cpp:39:17:39:18 | Store | test.cpp:41:10:41:12 | Load |
|
||||
| test.cpp:39:17:39:18 | StoreValue | test.cpp:39:17:39:18 | Store |
|
||||
| test.cpp:39:17:39:18 | Unary | test.cpp:39:17:39:18 | (reference to) |
|
||||
| test.cpp:39:17:39:18 | mc | test.cpp:39:17:39:18 | Unary |
|
||||
| test.cpp:41:9:41:12 | & ... | test.cpp:41:9:41:12 | StoreValue |
|
||||
| test.cpp:41:10:41:12 | (reference dereference) | test.cpp:41:10:41:12 | Unary |
|
||||
| test.cpp:41:10:41:12 | Load | test.cpp:41:10:41:12 | ref |
|
||||
| test.cpp:41:10:41:12 | Unary | test.cpp:41:9:41:12 | & ... |
|
||||
| test.cpp:41:10:41:12 | Unary | test.cpp:41:10:41:12 | (reference dereference) |
|
||||
| test.cpp:41:10:41:12 | ref | test.cpp:41:10:41:12 | Unary |
|
||||
| test.cpp:47:9:47:10 | (reference to) | test.cpp:47:9:47:10 | StoreValue |
|
||||
| test.cpp:47:9:47:10 | Unary | test.cpp:47:9:47:10 | (reference to) |
|
||||
| test.cpp:47:9:47:10 | mc | test.cpp:47:9:47:10 | Unary |
|
||||
| test.cpp:54:9:54:15 | & ... | test.cpp:54:9:54:15 | StoreValue |
|
||||
| test.cpp:54:11:54:12 | Unary | test.cpp:54:14:54:14 | a |
|
||||
| test.cpp:54:11:54:12 | mc | test.cpp:54:11:54:12 | Unary |
|
||||
| test.cpp:54:14:54:14 | Unary | test.cpp:54:9:54:15 | & ... |
|
||||
| test.cpp:54:14:54:14 | a | test.cpp:54:14:54:14 | Unary |
|
||||
| test.cpp:89:3:89:11 | Store | test.cpp:92:9:92:11 | Load |
|
||||
| test.cpp:89:9:89:11 | & ... | test.cpp:89:9:89:11 | StoreValue |
|
||||
| test.cpp:89:9:89:11 | StoreValue | test.cpp:89:3:89:11 | Store |
|
||||
| test.cpp:89:10:89:11 | Unary | test.cpp:89:9:89:11 | & ... |
|
||||
| test.cpp:89:10:89:11 | mc | test.cpp:89:10:89:11 | Unary |
|
||||
| test.cpp:92:9:92:11 | Load | test.cpp:92:9:92:11 | ptr |
|
||||
| test.cpp:92:9:92:11 | ptr | test.cpp:92:9:92:11 | StoreValue |
|
||||
| test.cpp:112:9:112:11 | Unary | test.cpp:112:9:112:11 | array to pointer conversion |
|
||||
| test.cpp:112:9:112:11 | arr | test.cpp:112:9:112:11 | Unary |
|
||||
| test.cpp:112:9:112:11 | array to pointer conversion | test.cpp:112:9:112:11 | StoreValue |
|
||||
| test.cpp:119:9:119:18 | & ... | test.cpp:119:9:119:18 | StoreValue |
|
||||
| test.cpp:119:11:119:13 | Left | test.cpp:119:11:119:17 | access to array |
|
||||
| test.cpp:119:11:119:13 | Unary | test.cpp:119:11:119:13 | array to pointer conversion |
|
||||
| test.cpp:119:11:119:13 | arr | test.cpp:119:11:119:13 | Unary |
|
||||
| test.cpp:119:11:119:13 | array to pointer conversion | test.cpp:119:11:119:13 | Left |
|
||||
| test.cpp:119:11:119:17 | Unary | test.cpp:119:9:119:18 | & ... |
|
||||
| test.cpp:119:11:119:17 | access to array | test.cpp:119:11:119:17 | Unary |
|
||||
| test.cpp:134:2:134:14 | Store | test.cpp:135:2:135:4 | Load |
|
||||
| test.cpp:134:8:134:10 | Left | test.cpp:134:8:134:14 | ... + ... |
|
||||
| test.cpp:134:8:134:10 | Unary | test.cpp:134:8:134:10 | array to pointer conversion |
|
||||
| test.cpp:134:8:134:10 | arr | test.cpp:134:8:134:10 | Unary |
|
||||
| test.cpp:134:8:134:10 | array to pointer conversion | test.cpp:134:8:134:10 | Left |
|
||||
| test.cpp:134:8:134:14 | ... + ... | test.cpp:134:8:134:14 | StoreValue |
|
||||
| test.cpp:134:8:134:14 | StoreValue | test.cpp:134:2:134:14 | Store |
|
||||
| test.cpp:135:2:135:4 | Left | test.cpp:135:2:135:6 | PointerAdd |
|
||||
| test.cpp:135:2:135:4 | Load | test.cpp:135:2:135:4 | ptr |
|
||||
| test.cpp:135:2:135:4 | ptr | test.cpp:135:2:135:4 | Left |
|
||||
| test.cpp:135:2:135:6 | PointerAdd | test.cpp:135:2:135:6 | StoreValue |
|
||||
| test.cpp:135:2:135:6 | Store | test.cpp:137:9:137:11 | Load |
|
||||
| test.cpp:135:2:135:6 | StoreValue | test.cpp:135:2:135:6 | Store |
|
||||
| test.cpp:137:9:137:11 | Load | test.cpp:137:9:137:11 | ptr |
|
||||
| test.cpp:137:9:137:11 | ptr | test.cpp:137:9:137:11 | StoreValue |
|
||||
| test.cpp:170:26:170:41 | (void *)... | test.cpp:170:26:170:41 | StoreValue |
|
||||
| test.cpp:170:26:170:41 | Store | test.cpp:171:10:171:23 | Load |
|
||||
| test.cpp:170:26:170:41 | StoreValue | test.cpp:170:26:170:41 | Store |
|
||||
| test.cpp:170:34:170:41 | & ... | test.cpp:170:34:170:41 | Unary |
|
||||
| test.cpp:170:34:170:41 | Unary | test.cpp:170:26:170:41 | (void *)... |
|
||||
| test.cpp:170:35:170:41 | Unary | test.cpp:170:34:170:41 | & ... |
|
||||
| test.cpp:170:35:170:41 | myLocal | test.cpp:170:35:170:41 | Unary |
|
||||
| test.cpp:171:10:171:23 | Load | test.cpp:171:10:171:23 | pointerToLocal |
|
||||
| test.cpp:171:10:171:23 | pointerToLocal | test.cpp:171:10:171:23 | StoreValue |
|
||||
| test.cpp:176:25:176:34 | Store | test.cpp:177:10:177:23 | Load |
|
||||
| test.cpp:176:25:176:34 | StoreValue | test.cpp:176:25:176:34 | Store |
|
||||
| test.cpp:176:25:176:34 | Unary | test.cpp:176:25:176:34 | array to pointer conversion |
|
||||
| test.cpp:176:25:176:34 | array to pointer conversion | test.cpp:176:25:176:34 | StoreValue |
|
||||
| test.cpp:176:25:176:34 | localArray | test.cpp:176:25:176:34 | Unary |
|
||||
| test.cpp:177:10:177:23 | (void *)... | test.cpp:177:10:177:23 | StoreValue |
|
||||
| test.cpp:177:10:177:23 | Load | test.cpp:177:10:177:23 | pointerToLocal |
|
||||
| test.cpp:177:10:177:23 | Unary | test.cpp:177:10:177:23 | (void *)... |
|
||||
| test.cpp:177:10:177:23 | pointerToLocal | test.cpp:177:10:177:23 | Unary |
|
||||
| test.cpp:182:21:182:27 | (reference to) | test.cpp:182:21:182:27 | StoreValue |
|
||||
| test.cpp:182:21:182:27 | Store | test.cpp:183:10:183:19 | Load |
|
||||
| test.cpp:182:21:182:27 | StoreValue | test.cpp:182:21:182:27 | Store |
|
||||
| test.cpp:182:21:182:27 | Unary | test.cpp:182:21:182:27 | (reference to) |
|
||||
| test.cpp:182:21:182:27 | myLocal | test.cpp:182:21:182:27 | Unary |
|
||||
| test.cpp:183:10:183:19 | (reference dereference) | test.cpp:183:10:183:19 | Unary |
|
||||
| test.cpp:183:10:183:19 | (reference to) | test.cpp:183:10:183:19 | StoreValue |
|
||||
| test.cpp:183:10:183:19 | Load | test.cpp:183:10:183:19 | refToLocal |
|
||||
| test.cpp:183:10:183:19 | Unary | test.cpp:183:10:183:19 | (reference dereference) |
|
||||
| test.cpp:183:10:183:19 | Unary | test.cpp:183:10:183:19 | (reference to) |
|
||||
| test.cpp:183:10:183:19 | refToLocal | test.cpp:183:10:183:19 | Unary |
|
||||
| test.cpp:189:16:189:16 | (reference to) | test.cpp:189:16:189:16 | StoreValue |
|
||||
| test.cpp:189:16:189:16 | Store | test.cpp:190:10:190:13 | Load |
|
||||
| test.cpp:189:16:189:16 | StoreValue | test.cpp:189:16:189:16 | Store |
|
||||
| test.cpp:189:16:189:16 | Unary | test.cpp:189:16:189:16 | (reference to) |
|
||||
| test.cpp:189:16:189:16 | p | test.cpp:189:16:189:16 | Unary |
|
||||
| test.cpp:190:10:190:13 | (reference dereference) | test.cpp:190:10:190:13 | Unary |
|
||||
| test.cpp:190:10:190:13 | (reference to) | test.cpp:190:10:190:13 | StoreValue |
|
||||
| test.cpp:190:10:190:13 | Load | test.cpp:190:10:190:13 | pRef |
|
||||
| test.cpp:190:10:190:13 | Unary | test.cpp:190:10:190:13 | (reference dereference) |
|
||||
| test.cpp:190:10:190:13 | Unary | test.cpp:190:10:190:13 | (reference to) |
|
||||
| test.cpp:190:10:190:13 | pRef | test.cpp:190:10:190:13 | Unary |
|
||||
| test.cpp:17:10:17:11 | mc | test.cpp:17:9:17:11 | & ... |
|
||||
| test.cpp:23:17:23:19 | & ... | test.cpp:23:17:23:19 | & ... |
|
||||
| test.cpp:23:17:23:19 | & ... | test.cpp:25:9:25:11 | ptr |
|
||||
| test.cpp:23:18:23:19 | mc | test.cpp:23:17:23:19 | & ... |
|
||||
| test.cpp:39:17:39:18 | (reference to) | test.cpp:39:17:39:18 | (reference to) |
|
||||
| test.cpp:39:17:39:18 | (reference to) | test.cpp:41:10:41:12 | ref |
|
||||
| test.cpp:39:17:39:18 | mc | test.cpp:39:17:39:18 | (reference to) |
|
||||
| test.cpp:41:10:41:12 | (reference dereference) | test.cpp:41:9:41:12 | & ... |
|
||||
| test.cpp:41:10:41:12 | ref | test.cpp:41:10:41:12 | (reference dereference) |
|
||||
| test.cpp:47:9:47:10 | mc | test.cpp:47:9:47:10 | (reference to) |
|
||||
| test.cpp:54:11:54:12 | mc | test.cpp:54:14:54:14 | a |
|
||||
| test.cpp:54:14:54:14 | a | test.cpp:54:9:54:15 | & ... |
|
||||
| test.cpp:89:3:89:11 | ... = ... | test.cpp:92:9:92:11 | ptr |
|
||||
| test.cpp:89:9:89:11 | & ... | test.cpp:89:3:89:11 | ... = ... |
|
||||
| test.cpp:89:10:89:11 | mc | test.cpp:89:9:89:11 | & ... |
|
||||
| test.cpp:112:9:112:11 | arr | test.cpp:112:9:112:11 | array to pointer conversion |
|
||||
| test.cpp:119:11:119:13 | arr | test.cpp:119:11:119:13 | array to pointer conversion |
|
||||
| test.cpp:119:11:119:13 | array to pointer conversion | test.cpp:119:11:119:17 | access to array |
|
||||
| test.cpp:119:11:119:17 | access to array | test.cpp:119:9:119:18 | & ... |
|
||||
| test.cpp:134:2:134:14 | ... = ... | test.cpp:135:2:135:4 | ptr |
|
||||
| test.cpp:134:8:134:10 | arr | test.cpp:134:8:134:10 | array to pointer conversion |
|
||||
| test.cpp:134:8:134:10 | array to pointer conversion | test.cpp:134:8:134:14 | ... + ... |
|
||||
| test.cpp:134:8:134:14 | ... + ... | test.cpp:134:2:134:14 | ... = ... |
|
||||
| test.cpp:135:2:135:4 | ptr | test.cpp:135:2:135:6 | ... ++ |
|
||||
| test.cpp:135:2:135:6 | ... ++ | test.cpp:135:2:135:6 | ... ++ |
|
||||
| test.cpp:135:2:135:6 | ... ++ | test.cpp:137:9:137:11 | ptr |
|
||||
| test.cpp:170:26:170:41 | (void *)... | test.cpp:170:26:170:41 | (void *)... |
|
||||
| test.cpp:170:26:170:41 | (void *)... | test.cpp:171:10:171:23 | pointerToLocal |
|
||||
| test.cpp:170:34:170:41 | & ... | test.cpp:170:26:170:41 | (void *)... |
|
||||
| test.cpp:170:35:170:41 | myLocal | test.cpp:170:34:170:41 | & ... |
|
||||
| test.cpp:176:25:176:34 | array to pointer conversion | test.cpp:176:25:176:34 | array to pointer conversion |
|
||||
| test.cpp:176:25:176:34 | array to pointer conversion | test.cpp:177:10:177:23 | pointerToLocal |
|
||||
| test.cpp:176:25:176:34 | localArray | test.cpp:176:25:176:34 | array to pointer conversion |
|
||||
| test.cpp:177:10:177:23 | pointerToLocal | test.cpp:177:10:177:23 | (void *)... |
|
||||
| test.cpp:182:21:182:27 | (reference to) | test.cpp:182:21:182:27 | (reference to) |
|
||||
| test.cpp:182:21:182:27 | (reference to) | test.cpp:183:10:183:19 | refToLocal |
|
||||
| test.cpp:182:21:182:27 | myLocal | test.cpp:182:21:182:27 | (reference to) |
|
||||
| test.cpp:183:10:183:19 | (reference dereference) | test.cpp:183:10:183:19 | (reference to) |
|
||||
| test.cpp:183:10:183:19 | refToLocal | test.cpp:183:10:183:19 | (reference dereference) |
|
||||
| test.cpp:189:16:189:16 | (reference to) | test.cpp:189:16:189:16 | (reference to) |
|
||||
| test.cpp:189:16:189:16 | (reference to) | test.cpp:190:10:190:13 | pRef |
|
||||
| test.cpp:189:16:189:16 | p | test.cpp:189:16:189:16 | (reference to) |
|
||||
| test.cpp:190:10:190:13 | (reference dereference) | test.cpp:190:10:190:13 | (reference to) |
|
||||
| test.cpp:190:10:190:13 | pRef | test.cpp:190:10:190:13 | (reference dereference) |
|
||||
nodes
|
||||
| test.cpp:17:9:17:11 | & ... | semmle.label | & ... |
|
||||
| test.cpp:17:9:17:11 | StoreValue | semmle.label | StoreValue |
|
||||
| test.cpp:17:10:17:11 | Unary | semmle.label | Unary |
|
||||
| test.cpp:17:10:17:11 | mc | semmle.label | mc |
|
||||
| test.cpp:23:17:23:19 | & ... | semmle.label | & ... |
|
||||
| test.cpp:23:17:23:19 | Store | semmle.label | Store |
|
||||
| test.cpp:23:17:23:19 | StoreValue | semmle.label | StoreValue |
|
||||
| test.cpp:23:18:23:19 | Unary | semmle.label | Unary |
|
||||
| test.cpp:23:17:23:19 | & ... | semmle.label | & ... |
|
||||
| test.cpp:23:18:23:19 | mc | semmle.label | mc |
|
||||
| test.cpp:25:9:25:11 | Load | semmle.label | Load |
|
||||
| test.cpp:25:9:25:11 | StoreValue | semmle.label | StoreValue |
|
||||
| test.cpp:25:9:25:11 | ptr | semmle.label | ptr |
|
||||
| test.cpp:39:17:39:18 | (reference to) | semmle.label | (reference to) |
|
||||
| test.cpp:39:17:39:18 | Store | semmle.label | Store |
|
||||
| test.cpp:39:17:39:18 | StoreValue | semmle.label | StoreValue |
|
||||
| test.cpp:39:17:39:18 | Unary | semmle.label | Unary |
|
||||
| test.cpp:39:17:39:18 | (reference to) | semmle.label | (reference to) |
|
||||
| test.cpp:39:17:39:18 | mc | semmle.label | mc |
|
||||
| test.cpp:41:9:41:12 | & ... | semmle.label | & ... |
|
||||
| test.cpp:41:9:41:12 | StoreValue | semmle.label | StoreValue |
|
||||
| test.cpp:41:10:41:12 | (reference dereference) | semmle.label | (reference dereference) |
|
||||
| test.cpp:41:10:41:12 | Load | semmle.label | Load |
|
||||
| test.cpp:41:10:41:12 | Unary | semmle.label | Unary |
|
||||
| test.cpp:41:10:41:12 | Unary | semmle.label | Unary |
|
||||
| test.cpp:41:10:41:12 | ref | semmle.label | ref |
|
||||
| test.cpp:47:9:47:10 | (reference to) | semmle.label | (reference to) |
|
||||
| test.cpp:47:9:47:10 | StoreValue | semmle.label | StoreValue |
|
||||
| test.cpp:47:9:47:10 | Unary | semmle.label | Unary |
|
||||
| test.cpp:47:9:47:10 | mc | semmle.label | mc |
|
||||
| test.cpp:54:9:54:15 | & ... | semmle.label | & ... |
|
||||
| test.cpp:54:9:54:15 | StoreValue | semmle.label | StoreValue |
|
||||
| test.cpp:54:11:54:12 | Unary | semmle.label | Unary |
|
||||
| test.cpp:54:11:54:12 | mc | semmle.label | mc |
|
||||
| test.cpp:54:14:54:14 | Unary | semmle.label | Unary |
|
||||
| test.cpp:54:14:54:14 | a | semmle.label | a |
|
||||
| test.cpp:89:3:89:11 | Store | semmle.label | Store |
|
||||
| test.cpp:89:3:89:11 | ... = ... | semmle.label | ... = ... |
|
||||
| test.cpp:89:9:89:11 | & ... | semmle.label | & ... |
|
||||
| test.cpp:89:9:89:11 | StoreValue | semmle.label | StoreValue |
|
||||
| test.cpp:89:10:89:11 | Unary | semmle.label | Unary |
|
||||
| test.cpp:89:10:89:11 | mc | semmle.label | mc |
|
||||
| test.cpp:92:9:92:11 | Load | semmle.label | Load |
|
||||
| test.cpp:92:9:92:11 | StoreValue | semmle.label | StoreValue |
|
||||
| test.cpp:92:9:92:11 | ptr | semmle.label | ptr |
|
||||
| test.cpp:112:9:112:11 | StoreValue | semmle.label | StoreValue |
|
||||
| test.cpp:112:9:112:11 | Unary | semmle.label | Unary |
|
||||
| test.cpp:112:9:112:11 | arr | semmle.label | arr |
|
||||
| test.cpp:112:9:112:11 | array to pointer conversion | semmle.label | array to pointer conversion |
|
||||
| test.cpp:119:9:119:18 | & ... | semmle.label | & ... |
|
||||
| test.cpp:119:9:119:18 | StoreValue | semmle.label | StoreValue |
|
||||
| test.cpp:119:11:119:13 | Left | semmle.label | Left |
|
||||
| test.cpp:119:11:119:13 | Unary | semmle.label | Unary |
|
||||
| test.cpp:119:11:119:13 | arr | semmle.label | arr |
|
||||
| test.cpp:119:11:119:13 | array to pointer conversion | semmle.label | array to pointer conversion |
|
||||
| test.cpp:119:11:119:17 | Unary | semmle.label | Unary |
|
||||
| test.cpp:119:11:119:17 | access to array | semmle.label | access to array |
|
||||
| test.cpp:134:2:134:14 | Store | semmle.label | Store |
|
||||
| test.cpp:134:8:134:10 | Left | semmle.label | Left |
|
||||
| test.cpp:134:8:134:10 | Unary | semmle.label | Unary |
|
||||
| test.cpp:134:2:134:14 | ... = ... | semmle.label | ... = ... |
|
||||
| test.cpp:134:8:134:10 | arr | semmle.label | arr |
|
||||
| test.cpp:134:8:134:10 | array to pointer conversion | semmle.label | array to pointer conversion |
|
||||
| test.cpp:134:8:134:14 | ... + ... | semmle.label | ... + ... |
|
||||
| test.cpp:134:8:134:14 | StoreValue | semmle.label | StoreValue |
|
||||
| test.cpp:135:2:135:4 | Left | semmle.label | Left |
|
||||
| test.cpp:135:2:135:4 | Load | semmle.label | Load |
|
||||
| test.cpp:135:2:135:4 | ptr | semmle.label | ptr |
|
||||
| test.cpp:135:2:135:6 | PointerAdd | semmle.label | PointerAdd |
|
||||
| test.cpp:135:2:135:6 | Store | semmle.label | Store |
|
||||
| test.cpp:135:2:135:6 | StoreValue | semmle.label | StoreValue |
|
||||
| test.cpp:137:9:137:11 | Load | semmle.label | Load |
|
||||
| test.cpp:137:9:137:11 | StoreValue | semmle.label | StoreValue |
|
||||
| test.cpp:135:2:135:6 | ... ++ | semmle.label | ... ++ |
|
||||
| test.cpp:135:2:135:6 | ... ++ | semmle.label | ... ++ |
|
||||
| test.cpp:137:9:137:11 | ptr | semmle.label | ptr |
|
||||
| test.cpp:170:26:170:41 | (void *)... | semmle.label | (void *)... |
|
||||
| test.cpp:170:26:170:41 | Store | semmle.label | Store |
|
||||
| test.cpp:170:26:170:41 | StoreValue | semmle.label | StoreValue |
|
||||
| test.cpp:170:26:170:41 | (void *)... | semmle.label | (void *)... |
|
||||
| test.cpp:170:34:170:41 | & ... | semmle.label | & ... |
|
||||
| test.cpp:170:34:170:41 | Unary | semmle.label | Unary |
|
||||
| test.cpp:170:35:170:41 | Unary | semmle.label | Unary |
|
||||
| test.cpp:170:35:170:41 | myLocal | semmle.label | myLocal |
|
||||
| test.cpp:171:10:171:23 | Load | semmle.label | Load |
|
||||
| test.cpp:171:10:171:23 | StoreValue | semmle.label | StoreValue |
|
||||
| test.cpp:171:10:171:23 | pointerToLocal | semmle.label | pointerToLocal |
|
||||
| test.cpp:176:25:176:34 | Store | semmle.label | Store |
|
||||
| test.cpp:176:25:176:34 | StoreValue | semmle.label | StoreValue |
|
||||
| test.cpp:176:25:176:34 | Unary | semmle.label | Unary |
|
||||
| test.cpp:176:25:176:34 | array to pointer conversion | semmle.label | array to pointer conversion |
|
||||
| test.cpp:176:25:176:34 | array to pointer conversion | semmle.label | array to pointer conversion |
|
||||
| test.cpp:176:25:176:34 | localArray | semmle.label | localArray |
|
||||
| test.cpp:177:10:177:23 | (void *)... | semmle.label | (void *)... |
|
||||
| test.cpp:177:10:177:23 | Load | semmle.label | Load |
|
||||
| test.cpp:177:10:177:23 | StoreValue | semmle.label | StoreValue |
|
||||
| test.cpp:177:10:177:23 | Unary | semmle.label | Unary |
|
||||
| test.cpp:177:10:177:23 | pointerToLocal | semmle.label | pointerToLocal |
|
||||
| test.cpp:182:21:182:27 | (reference to) | semmle.label | (reference to) |
|
||||
| test.cpp:182:21:182:27 | Store | semmle.label | Store |
|
||||
| test.cpp:182:21:182:27 | StoreValue | semmle.label | StoreValue |
|
||||
| test.cpp:182:21:182:27 | Unary | semmle.label | Unary |
|
||||
| test.cpp:182:21:182:27 | (reference to) | semmle.label | (reference to) |
|
||||
| test.cpp:182:21:182:27 | myLocal | semmle.label | myLocal |
|
||||
| test.cpp:183:10:183:19 | (reference dereference) | semmle.label | (reference dereference) |
|
||||
| test.cpp:183:10:183:19 | (reference to) | semmle.label | (reference to) |
|
||||
| test.cpp:183:10:183:19 | Load | semmle.label | Load |
|
||||
| test.cpp:183:10:183:19 | StoreValue | semmle.label | StoreValue |
|
||||
| test.cpp:183:10:183:19 | Unary | semmle.label | Unary |
|
||||
| test.cpp:183:10:183:19 | Unary | semmle.label | Unary |
|
||||
| test.cpp:183:10:183:19 | refToLocal | semmle.label | refToLocal |
|
||||
| test.cpp:189:16:189:16 | (reference to) | semmle.label | (reference to) |
|
||||
| test.cpp:189:16:189:16 | Store | semmle.label | Store |
|
||||
| test.cpp:189:16:189:16 | StoreValue | semmle.label | StoreValue |
|
||||
| test.cpp:189:16:189:16 | Unary | semmle.label | Unary |
|
||||
| test.cpp:189:16:189:16 | (reference to) | semmle.label | (reference to) |
|
||||
| test.cpp:189:16:189:16 | p | semmle.label | p |
|
||||
| test.cpp:190:10:190:13 | (reference dereference) | semmle.label | (reference dereference) |
|
||||
| test.cpp:190:10:190:13 | (reference to) | semmle.label | (reference to) |
|
||||
| test.cpp:190:10:190:13 | Load | semmle.label | Load |
|
||||
| test.cpp:190:10:190:13 | StoreValue | semmle.label | StoreValue |
|
||||
| test.cpp:190:10:190:13 | Unary | semmle.label | Unary |
|
||||
| test.cpp:190:10:190:13 | Unary | semmle.label | Unary |
|
||||
| test.cpp:190:10:190:13 | pRef | semmle.label | pRef |
|
||||
#select
|
||||
| test.cpp:17:9:17:11 | StoreValue | test.cpp:17:10:17:11 | mc | test.cpp:17:9:17:11 | StoreValue | May return stack-allocated memory from $@. | test.cpp:17:10:17:11 | mc | mc |
|
||||
| test.cpp:25:9:25:11 | StoreValue | test.cpp:23:18:23:19 | mc | test.cpp:25:9:25:11 | StoreValue | May return stack-allocated memory from $@. | test.cpp:23:18:23:19 | mc | mc |
|
||||
| test.cpp:41:9:41:12 | StoreValue | test.cpp:39:17:39:18 | mc | test.cpp:41:9:41:12 | StoreValue | May return stack-allocated memory from $@. | test.cpp:39:17:39:18 | mc | mc |
|
||||
| test.cpp:47:9:47:10 | StoreValue | test.cpp:47:9:47:10 | mc | test.cpp:47:9:47:10 | StoreValue | May return stack-allocated memory from $@. | test.cpp:47:9:47:10 | mc | mc |
|
||||
| test.cpp:54:9:54:15 | StoreValue | test.cpp:54:11:54:12 | mc | test.cpp:54:9:54:15 | StoreValue | May return stack-allocated memory from $@. | test.cpp:54:11:54:12 | mc | mc |
|
||||
| test.cpp:92:9:92:11 | StoreValue | test.cpp:89:10:89:11 | mc | test.cpp:92:9:92:11 | StoreValue | May return stack-allocated memory from $@. | test.cpp:89:10:89:11 | mc | mc |
|
||||
| test.cpp:112:9:112:11 | StoreValue | test.cpp:112:9:112:11 | arr | test.cpp:112:9:112:11 | StoreValue | May return stack-allocated memory from $@. | test.cpp:112:9:112:11 | arr | arr |
|
||||
| test.cpp:119:9:119:18 | StoreValue | test.cpp:119:11:119:13 | arr | test.cpp:119:9:119:18 | StoreValue | May return stack-allocated memory from $@. | test.cpp:119:11:119:13 | arr | arr |
|
||||
| test.cpp:137:9:137:11 | StoreValue | test.cpp:134:8:134:10 | arr | test.cpp:137:9:137:11 | StoreValue | May return stack-allocated memory from $@. | test.cpp:134:8:134:10 | arr | arr |
|
||||
| test.cpp:171:10:171:23 | StoreValue | test.cpp:170:35:170:41 | myLocal | test.cpp:171:10:171:23 | StoreValue | May return stack-allocated memory from $@. | test.cpp:170:35:170:41 | myLocal | myLocal |
|
||||
| test.cpp:177:10:177:23 | StoreValue | test.cpp:176:25:176:34 | localArray | test.cpp:177:10:177:23 | StoreValue | May return stack-allocated memory from $@. | test.cpp:176:25:176:34 | localArray | localArray |
|
||||
| test.cpp:183:10:183:19 | StoreValue | test.cpp:182:21:182:27 | myLocal | test.cpp:183:10:183:19 | StoreValue | May return stack-allocated memory from $@. | test.cpp:182:21:182:27 | myLocal | myLocal |
|
||||
| test.cpp:190:10:190:13 | StoreValue | test.cpp:189:16:189:16 | p | test.cpp:190:10:190:13 | StoreValue | May return stack-allocated memory from $@. | test.cpp:189:16:189:16 | p | p |
|
||||
| test.cpp:17:9:17:11 | CopyValue: & ... | test.cpp:17:10:17:11 | mc | test.cpp:17:9:17:11 | & ... | May return stack-allocated memory from $@. | test.cpp:17:10:17:11 | mc | mc |
|
||||
| test.cpp:25:9:25:11 | Load: ptr | test.cpp:23:18:23:19 | mc | test.cpp:25:9:25:11 | ptr | May return stack-allocated memory from $@. | test.cpp:23:18:23:19 | mc | mc |
|
||||
| test.cpp:41:9:41:12 | CopyValue: & ... | test.cpp:39:17:39:18 | mc | test.cpp:41:9:41:12 | & ... | May return stack-allocated memory from $@. | test.cpp:39:17:39:18 | mc | mc |
|
||||
| test.cpp:47:9:47:10 | CopyValue: (reference to) | test.cpp:47:9:47:10 | mc | test.cpp:47:9:47:10 | (reference to) | May return stack-allocated memory from $@. | test.cpp:47:9:47:10 | mc | mc |
|
||||
| test.cpp:54:9:54:15 | CopyValue: & ... | test.cpp:54:11:54:12 | mc | test.cpp:54:9:54:15 | & ... | May return stack-allocated memory from $@. | test.cpp:54:11:54:12 | mc | mc |
|
||||
| test.cpp:92:9:92:11 | Load: ptr | test.cpp:89:10:89:11 | mc | test.cpp:92:9:92:11 | ptr | May return stack-allocated memory from $@. | test.cpp:89:10:89:11 | mc | mc |
|
||||
| test.cpp:112:9:112:11 | Convert: array to pointer conversion | test.cpp:112:9:112:11 | arr | test.cpp:112:9:112:11 | array to pointer conversion | May return stack-allocated memory from $@. | test.cpp:112:9:112:11 | arr | arr |
|
||||
| test.cpp:119:9:119:18 | CopyValue: & ... | test.cpp:119:11:119:13 | arr | test.cpp:119:9:119:18 | & ... | May return stack-allocated memory from $@. | test.cpp:119:11:119:13 | arr | arr |
|
||||
| test.cpp:137:9:137:11 | Load: ptr | test.cpp:134:8:134:10 | arr | test.cpp:137:9:137:11 | ptr | May return stack-allocated memory from $@. | test.cpp:134:8:134:10 | arr | arr |
|
||||
| test.cpp:171:10:171:23 | Load: pointerToLocal | test.cpp:170:35:170:41 | myLocal | test.cpp:171:10:171:23 | pointerToLocal | May return stack-allocated memory from $@. | test.cpp:170:35:170:41 | myLocal | myLocal |
|
||||
| test.cpp:177:10:177:23 | Convert: (void *)... | test.cpp:176:25:176:34 | localArray | test.cpp:177:10:177:23 | (void *)... | May return stack-allocated memory from $@. | test.cpp:176:25:176:34 | localArray | localArray |
|
||||
| test.cpp:183:10:183:19 | CopyValue: (reference to) | test.cpp:182:21:182:27 | myLocal | test.cpp:183:10:183:19 | (reference to) | May return stack-allocated memory from $@. | test.cpp:182:21:182:27 | myLocal | myLocal |
|
||||
| test.cpp:190:10:190:13 | CopyValue: (reference to) | test.cpp:189:16:189:16 | p | test.cpp:190:10:190:13 | (reference to) | May return stack-allocated memory from $@. | test.cpp:189:16:189:16 | p | p |
|
||||
|
||||
@@ -1,19 +1,8 @@
|
||||
edges
|
||||
| CWE23_Relative_Path_Traversal__char_console_fopen_11.cpp:55:27:55:38 | ... + ... | CWE23_Relative_Path_Traversal__char_console_fopen_11.cpp:77:23:77:26 | (const char *)... |
|
||||
| CWE23_Relative_Path_Traversal__char_console_fopen_11.cpp:55:27:55:38 | ... + ... | CWE23_Relative_Path_Traversal__char_console_fopen_11.cpp:77:23:77:26 | data |
|
||||
| CWE23_Relative_Path_Traversal__char_console_fopen_11.cpp:55:27:55:38 | ... + ... | CWE23_Relative_Path_Traversal__char_console_fopen_11.cpp:77:23:77:26 | data |
|
||||
| CWE23_Relative_Path_Traversal__char_console_fopen_11.cpp:55:27:55:38 | ... + ... | CWE23_Relative_Path_Traversal__char_console_fopen_11.cpp:77:23:77:26 | data indirection |
|
||||
| CWE23_Relative_Path_Traversal__char_console_fopen_11.cpp:55:27:55:38 | fgets output argument | CWE23_Relative_Path_Traversal__char_console_fopen_11.cpp:77:23:77:26 | (const char *)... |
|
||||
| CWE23_Relative_Path_Traversal__char_console_fopen_11.cpp:55:27:55:38 | fgets output argument | CWE23_Relative_Path_Traversal__char_console_fopen_11.cpp:77:23:77:26 | data |
|
||||
| CWE23_Relative_Path_Traversal__char_console_fopen_11.cpp:55:27:55:38 | fgets output argument | CWE23_Relative_Path_Traversal__char_console_fopen_11.cpp:77:23:77:26 | data |
|
||||
| CWE23_Relative_Path_Traversal__char_console_fopen_11.cpp:55:27:55:38 | fgets output argument | CWE23_Relative_Path_Traversal__char_console_fopen_11.cpp:77:23:77:26 | data indirection |
|
||||
subpaths
|
||||
nodes
|
||||
| CWE23_Relative_Path_Traversal__char_console_fopen_11.cpp:55:27:55:38 | ... + ... | semmle.label | ... + ... |
|
||||
| CWE23_Relative_Path_Traversal__char_console_fopen_11.cpp:55:27:55:38 | fgets output argument | semmle.label | fgets output argument |
|
||||
| CWE23_Relative_Path_Traversal__char_console_fopen_11.cpp:77:23:77:26 | (const char *)... | semmle.label | (const char *)... |
|
||||
| CWE23_Relative_Path_Traversal__char_console_fopen_11.cpp:77:23:77:26 | data | semmle.label | data |
|
||||
| CWE23_Relative_Path_Traversal__char_console_fopen_11.cpp:77:23:77:26 | data | semmle.label | data |
|
||||
| CWE23_Relative_Path_Traversal__char_console_fopen_11.cpp:77:23:77:26 | data indirection | semmle.label | data indirection |
|
||||
subpaths
|
||||
#select
|
||||
| CWE23_Relative_Path_Traversal__char_console_fopen_11.cpp:77:23:77:26 | data | CWE23_Relative_Path_Traversal__char_console_fopen_11.cpp:55:27:55:38 | ... + ... | CWE23_Relative_Path_Traversal__char_console_fopen_11.cpp:77:23:77:26 | data | This argument to a file access function is derived from $@ and then passed to fopen(filename). | CWE23_Relative_Path_Traversal__char_console_fopen_11.cpp:55:27:55:38 | ... + ... | user input (fgets) |
|
||||
| CWE23_Relative_Path_Traversal__char_console_fopen_11.cpp:77:23:77:26 | data | CWE23_Relative_Path_Traversal__char_console_fopen_11.cpp:55:27:55:38 | fgets output argument | CWE23_Relative_Path_Traversal__char_console_fopen_11.cpp:77:23:77:26 | data indirection | This argument to a file access function is derived from $@ and then passed to fopen(filename). | CWE23_Relative_Path_Traversal__char_console_fopen_11.cpp:55:27:55:38 | fgets output argument | user input (string read by fgets) |
|
||||
|
||||
@@ -1,19 +1,20 @@
|
||||
edges
|
||||
| test.c:9:23:9:26 | argv | test.c:17:11:17:18 | (const char *)... |
|
||||
| test.c:9:23:9:26 | argv | test.c:17:11:17:18 | (const char *)... |
|
||||
| test.c:9:23:9:26 | argv | test.c:17:11:17:18 | fileName |
|
||||
| test.c:9:23:9:26 | argv | test.c:17:11:17:18 | fileName |
|
||||
| test.c:9:23:9:26 | argv | test.c:17:11:17:18 | fileName |
|
||||
| test.c:9:23:9:26 | argv | test.c:17:11:17:18 | fileName |
|
||||
| test.c:9:23:9:26 | argv | test.c:17:11:17:18 | fileName indirection |
|
||||
| test.c:9:23:9:26 | argv | test.c:17:11:17:18 | fileName indirection |
|
||||
subpaths
|
||||
| test.c:31:22:31:25 | argv | test.c:32:11:32:18 | fileName indirection |
|
||||
| test.c:37:17:37:24 | scanf output argument | test.c:38:11:38:18 | fileName indirection |
|
||||
| test.c:43:17:43:24 | scanf output argument | test.c:44:11:44:18 | fileName indirection |
|
||||
nodes
|
||||
| test.c:9:23:9:26 | argv | semmle.label | argv |
|
||||
| test.c:9:23:9:26 | argv | semmle.label | argv |
|
||||
| test.c:17:11:17:18 | (const char *)... | semmle.label | (const char *)... |
|
||||
| test.c:17:11:17:18 | fileName | semmle.label | fileName |
|
||||
| test.c:17:11:17:18 | fileName | semmle.label | fileName |
|
||||
| test.c:17:11:17:18 | fileName indirection | semmle.label | fileName indirection |
|
||||
| test.c:31:22:31:25 | argv | semmle.label | argv |
|
||||
| test.c:32:11:32:18 | fileName indirection | semmle.label | fileName indirection |
|
||||
| test.c:37:17:37:24 | scanf output argument | semmle.label | scanf output argument |
|
||||
| test.c:38:11:38:18 | fileName indirection | semmle.label | fileName indirection |
|
||||
| test.c:43:17:43:24 | scanf output argument | semmle.label | scanf output argument |
|
||||
| test.c:44:11:44:18 | fileName indirection | semmle.label | fileName indirection |
|
||||
subpaths
|
||||
#select
|
||||
| test.c:17:11:17:18 | fileName | test.c:9:23:9:26 | argv | test.c:17:11:17:18 | fileName | This argument to a file access function is derived from $@ and then passed to fopen(filename). | test.c:9:23:9:26 | argv | user input (argv) |
|
||||
| test.c:17:11:17:18 | fileName | test.c:9:23:9:26 | argv | test.c:17:11:17:18 | fileName indirection | This argument to a file access function is derived from $@ and then passed to fopen(filename). | test.c:9:23:9:26 | argv | user input (a command-line argument) |
|
||||
| test.c:32:11:32:18 | fileName | test.c:31:22:31:25 | argv | test.c:32:11:32:18 | fileName indirection | This argument to a file access function is derived from $@ and then passed to fopen(filename). | test.c:31:22:31:25 | argv | user input (a command-line argument) |
|
||||
| test.c:38:11:38:18 | fileName | test.c:37:17:37:24 | scanf output argument | test.c:38:11:38:18 | fileName indirection | This argument to a file access function is derived from $@ and then passed to fopen(filename). | test.c:37:17:37:24 | scanf output argument | user input (value read by scanf) |
|
||||
| test.c:44:11:44:18 | fileName | test.c:43:17:43:24 | scanf output argument | test.c:44:11:44:18 | fileName indirection | This argument to a file access function is derived from $@ and then passed to fopen(filename). | test.c:43:17:43:24 | scanf output argument | user input (value read by scanf) |
|
||||
|
||||
@@ -11,3 +11,7 @@ FILE *fopen(const char *filename, const char *mode);
|
||||
int sprintf(char *s, const char *format, ...);
|
||||
size_t strlen(const char *s);
|
||||
char *strncat(char *s1, const char *s2, size_t n);
|
||||
int scanf(const char *format, ...);
|
||||
void *malloc(size_t size);
|
||||
double strtod(const char *ptr, char **endptr);
|
||||
char *getenv(const char *name);
|
||||
|
||||
@@ -26,5 +26,38 @@ int main(int argc, char** argv) {
|
||||
strncat(fileName+len, fixed, FILENAME_MAX-len-1);
|
||||
fopen(fileName, "wb+");
|
||||
}
|
||||
|
||||
{
|
||||
char *fileName = argv[1];
|
||||
fopen(fileName, "wb+"); // BAD
|
||||
}
|
||||
|
||||
{
|
||||
char fileName[20];
|
||||
scanf("%s", fileName);
|
||||
fopen(fileName, "wb+"); // BAD
|
||||
}
|
||||
|
||||
{
|
||||
char *fileName = (char*)malloc(20 * sizeof(char));
|
||||
scanf("%s", fileName);
|
||||
fopen(fileName, "wb+"); // BAD
|
||||
}
|
||||
|
||||
{
|
||||
char *aNumber = getenv("A_NUMBER");
|
||||
double number = strtod(aNumber, 0);
|
||||
char fileName[20];
|
||||
sprintf(fileName, "/foo/%f", number);
|
||||
fopen(fileName, "wb+"); // GOOD
|
||||
}
|
||||
|
||||
{
|
||||
void read(const char *fileName);
|
||||
read(argv[1]); // BAD [NOT DETECTED]
|
||||
}
|
||||
}
|
||||
|
||||
void read(char *fileName) {
|
||||
fopen(fileName, "wb+");
|
||||
}
|
||||
|
||||
@@ -163,17 +163,17 @@ subpaths
|
||||
#select
|
||||
| test.cpp:23:12:23:19 | command1 | test.cpp:16:20:16:23 | argv | test.cpp:23:12:23:19 | command1 indirection | This argument to an OS command is derived from $@, dangerously concatenated into $@, and then passed to system(string). | test.cpp:16:20:16:23 | argv | user input (a command-line argument) | test.cpp:22:13:22:20 | sprintf output argument | sprintf output argument |
|
||||
| test.cpp:51:10:51:16 | command | test.cpp:47:21:47:26 | call to getenv | test.cpp:51:10:51:16 | command indirection | This argument to an OS command is derived from $@, dangerously concatenated into $@, and then passed to system(string). | test.cpp:47:21:47:26 | call to getenv | user input (an environment variable) | test.cpp:50:11:50:17 | sprintf output argument | sprintf output argument |
|
||||
| test.cpp:65:10:65:16 | command | test.cpp:62:9:62:16 | fread output argument | test.cpp:65:10:65:16 | command indirection | This argument to an OS command is derived from $@, dangerously concatenated into $@, and then passed to system(string). | test.cpp:62:9:62:16 | fread output argument | user input (String read by fread) | test.cpp:64:11:64:17 | strncat output argument | strncat output argument |
|
||||
| test.cpp:85:32:85:38 | command | test.cpp:82:9:82:16 | fread output argument | test.cpp:85:32:85:38 | command indirection | This argument to an OS command is derived from $@, dangerously concatenated into $@, and then passed to execl. | test.cpp:82:9:82:16 | fread output argument | user input (String read by fread) | test.cpp:84:11:84:17 | strncat output argument | strncat output argument |
|
||||
| test.cpp:94:45:94:48 | path | test.cpp:91:9:91:16 | fread output argument | test.cpp:94:45:94:48 | path indirection | This argument to an OS command is derived from $@, dangerously concatenated into $@, and then passed to execl. | test.cpp:91:9:91:16 | fread output argument | user input (String read by fread) | test.cpp:93:11:93:14 | strncat output argument | strncat output argument |
|
||||
| test.cpp:65:10:65:16 | command | test.cpp:62:9:62:16 | fread output argument | test.cpp:65:10:65:16 | command indirection | This argument to an OS command is derived from $@, dangerously concatenated into $@, and then passed to system(string). | test.cpp:62:9:62:16 | fread output argument | user input (string read by fread) | test.cpp:64:11:64:17 | strncat output argument | strncat output argument |
|
||||
| test.cpp:85:32:85:38 | command | test.cpp:82:9:82:16 | fread output argument | test.cpp:85:32:85:38 | command indirection | This argument to an OS command is derived from $@, dangerously concatenated into $@, and then passed to execl. | test.cpp:82:9:82:16 | fread output argument | user input (string read by fread) | test.cpp:84:11:84:17 | strncat output argument | strncat output argument |
|
||||
| test.cpp:94:45:94:48 | path | test.cpp:91:9:91:16 | fread output argument | test.cpp:94:45:94:48 | path indirection | This argument to an OS command is derived from $@, dangerously concatenated into $@, and then passed to execl. | test.cpp:91:9:91:16 | fread output argument | user input (string read by fread) | test.cpp:93:11:93:14 | strncat output argument | strncat output argument |
|
||||
| test.cpp:108:18:108:22 | call to c_str | test.cpp:106:20:106:25 | call to getenv | test.cpp:108:18:108:22 | call to c_str indirection | This argument to an OS command is derived from $@, dangerously concatenated into $@, and then passed to system(string). | test.cpp:106:20:106:25 | call to getenv | user input (an environment variable) | test.cpp:107:31:107:31 | call to operator+ | call to operator+ |
|
||||
| test.cpp:114:25:114:29 | call to c_str | test.cpp:113:20:113:25 | call to getenv | test.cpp:114:25:114:29 | call to c_str indirection | This argument to an OS command is derived from $@, dangerously concatenated into $@, and then passed to system(string). | test.cpp:113:20:113:25 | call to getenv | user input (an environment variable) | test.cpp:114:17:114:17 | Call | Call |
|
||||
| test.cpp:120:25:120:28 | call to data | test.cpp:119:20:119:25 | call to getenv | test.cpp:120:10:120:30 | call to data indirection | This argument to an OS command is derived from $@, dangerously concatenated into $@, and then passed to system(string). | test.cpp:119:20:119:25 | call to getenv | user input (an environment variable) | test.cpp:120:17:120:17 | Call | Call |
|
||||
| test.cpp:143:10:143:16 | command | test.cpp:140:9:140:11 | fread output argument | test.cpp:143:10:143:16 | command indirection | This argument to an OS command is derived from $@, dangerously concatenated into $@, and then passed to system(string). | test.cpp:140:9:140:11 | fread output argument | user input (String read by fread) | test.cpp:142:11:142:17 | sprintf output argument | sprintf output argument |
|
||||
| test.cpp:183:32:183:38 | command | test.cpp:174:9:174:16 | fread output argument | test.cpp:183:32:183:38 | command indirection | This argument to an OS command is derived from $@, dangerously concatenated into $@, and then passed to execl. | test.cpp:174:9:174:16 | fread output argument | user input (String read by fread) | test.cpp:177:13:177:17 | strncat output argument | strncat output argument |
|
||||
| test.cpp:183:32:183:38 | command | test.cpp:174:9:174:16 | fread output argument | test.cpp:183:32:183:38 | command indirection | This argument to an OS command is derived from $@, dangerously concatenated into $@, and then passed to execl. | test.cpp:174:9:174:16 | fread output argument | user input (String read by fread) | test.cpp:178:13:178:19 | strncat output argument | strncat output argument |
|
||||
| test.cpp:183:32:183:38 | command | test.cpp:174:9:174:16 | fread output argument | test.cpp:183:32:183:38 | command indirection | This argument to an OS command is derived from $@, dangerously concatenated into $@, and then passed to execl. | test.cpp:174:9:174:16 | fread output argument | user input (String read by fread) | test.cpp:180:13:180:19 | strncat output argument | strncat output argument |
|
||||
| test.cpp:198:32:198:38 | command | test.cpp:194:9:194:16 | fread output argument | test.cpp:198:32:198:38 | command indirection | This argument to an OS command is derived from $@, dangerously concatenated into $@, and then passed to execl. | test.cpp:194:9:194:16 | fread output argument | user input (String read by fread) | test.cpp:187:11:187:15 | strncat output argument | strncat output argument |
|
||||
| test.cpp:198:32:198:38 | command | test.cpp:194:9:194:16 | fread output argument | test.cpp:198:32:198:38 | command indirection | This argument to an OS command is derived from $@, dangerously concatenated into $@, and then passed to execl. | test.cpp:194:9:194:16 | fread output argument | user input (String read by fread) | test.cpp:188:11:188:17 | strncat output argument | strncat output argument |
|
||||
| test.cpp:222:32:222:38 | command | test.cpp:218:9:218:16 | fread output argument | test.cpp:222:32:222:38 | command indirection | This argument to an OS command is derived from $@, dangerously concatenated into $@, and then passed to execl. | test.cpp:218:9:218:16 | fread output argument | user input (String read by fread) | test.cpp:220:10:220:16 | strncat output argument | strncat output argument |
|
||||
| test.cpp:222:32:222:38 | command | test.cpp:218:9:218:16 | fread output argument | test.cpp:222:32:222:38 | command indirection | This argument to an OS command is derived from $@, dangerously concatenated into $@, and then passed to execl. | test.cpp:218:9:218:16 | fread output argument | user input (String read by fread) | test.cpp:220:10:220:16 | strncat output argument | strncat output argument |
|
||||
| test.cpp:143:10:143:16 | command | test.cpp:140:9:140:11 | fread output argument | test.cpp:143:10:143:16 | command indirection | This argument to an OS command is derived from $@, dangerously concatenated into $@, and then passed to system(string). | test.cpp:140:9:140:11 | fread output argument | user input (string read by fread) | test.cpp:142:11:142:17 | sprintf output argument | sprintf output argument |
|
||||
| test.cpp:183:32:183:38 | command | test.cpp:174:9:174:16 | fread output argument | test.cpp:183:32:183:38 | command indirection | This argument to an OS command is derived from $@, dangerously concatenated into $@, and then passed to execl. | test.cpp:174:9:174:16 | fread output argument | user input (string read by fread) | test.cpp:177:13:177:17 | strncat output argument | strncat output argument |
|
||||
| test.cpp:183:32:183:38 | command | test.cpp:174:9:174:16 | fread output argument | test.cpp:183:32:183:38 | command indirection | This argument to an OS command is derived from $@, dangerously concatenated into $@, and then passed to execl. | test.cpp:174:9:174:16 | fread output argument | user input (string read by fread) | test.cpp:178:13:178:19 | strncat output argument | strncat output argument |
|
||||
| test.cpp:183:32:183:38 | command | test.cpp:174:9:174:16 | fread output argument | test.cpp:183:32:183:38 | command indirection | This argument to an OS command is derived from $@, dangerously concatenated into $@, and then passed to execl. | test.cpp:174:9:174:16 | fread output argument | user input (string read by fread) | test.cpp:180:13:180:19 | strncat output argument | strncat output argument |
|
||||
| test.cpp:198:32:198:38 | command | test.cpp:194:9:194:16 | fread output argument | test.cpp:198:32:198:38 | command indirection | This argument to an OS command is derived from $@, dangerously concatenated into $@, and then passed to execl. | test.cpp:194:9:194:16 | fread output argument | user input (string read by fread) | test.cpp:187:11:187:15 | strncat output argument | strncat output argument |
|
||||
| test.cpp:198:32:198:38 | command | test.cpp:194:9:194:16 | fread output argument | test.cpp:198:32:198:38 | command indirection | This argument to an OS command is derived from $@, dangerously concatenated into $@, and then passed to execl. | test.cpp:194:9:194:16 | fread output argument | user input (string read by fread) | test.cpp:188:11:188:17 | strncat output argument | strncat output argument |
|
||||
| test.cpp:222:32:222:38 | command | test.cpp:218:9:218:16 | fread output argument | test.cpp:222:32:222:38 | command indirection | This argument to an OS command is derived from $@, dangerously concatenated into $@, and then passed to execl. | test.cpp:218:9:218:16 | fread output argument | user input (string read by fread) | test.cpp:220:10:220:16 | strncat output argument | strncat output argument |
|
||||
| test.cpp:222:32:222:38 | command | test.cpp:218:9:218:16 | fread output argument | test.cpp:222:32:222:38 | command indirection | This argument to an OS command is derived from $@, dangerously concatenated into $@, and then passed to execl. | test.cpp:218:9:218:16 | fread output argument | user input (string read by fread) | test.cpp:220:10:220:16 | strncat output argument | strncat output argument |
|
||||
|
||||
@@ -7,42 +7,12 @@ edges
|
||||
| tests.c:28:22:28:25 | argv | tests.c:28:22:28:28 | access to array |
|
||||
| tests.c:28:22:28:25 | argv | tests.c:28:22:28:28 | access to array indirection |
|
||||
| tests.c:28:22:28:25 | argv | tests.c:28:22:28:28 | access to array indirection |
|
||||
| tests.c:28:22:28:25 | argv | tests.c:31:15:31:23 | buffer100 |
|
||||
| tests.c:28:22:28:25 | argv | tests.c:31:15:31:23 | buffer100 |
|
||||
| tests.c:28:22:28:25 | argv | tests.c:31:15:31:23 | buffer100 indirection |
|
||||
| tests.c:28:22:28:25 | argv | tests.c:31:15:31:23 | buffer100 indirection |
|
||||
| tests.c:28:22:28:25 | argv | tests.c:33:21:33:29 | buffer100 |
|
||||
| tests.c:28:22:28:25 | argv | tests.c:33:21:33:29 | buffer100 |
|
||||
| tests.c:28:22:28:25 | argv | tests.c:33:21:33:29 | buffer100 indirection |
|
||||
| tests.c:28:22:28:25 | argv | tests.c:33:21:33:29 | buffer100 indirection |
|
||||
| tests.c:29:28:29:31 | argv | tests.c:29:28:29:34 | access to array |
|
||||
| tests.c:29:28:29:31 | argv | tests.c:29:28:29:34 | access to array |
|
||||
| tests.c:29:28:29:31 | argv | tests.c:29:28:29:34 | access to array |
|
||||
| tests.c:29:28:29:31 | argv | tests.c:29:28:29:34 | access to array |
|
||||
| tests.c:29:28:29:31 | argv | tests.c:29:28:29:34 | access to array indirection |
|
||||
| tests.c:29:28:29:31 | argv | tests.c:29:28:29:34 | access to array indirection |
|
||||
| tests.c:29:28:29:31 | argv | tests.c:31:15:31:23 | buffer100 |
|
||||
| tests.c:29:28:29:31 | argv | tests.c:31:15:31:23 | buffer100 |
|
||||
| tests.c:29:28:29:31 | argv | tests.c:31:15:31:23 | buffer100 indirection |
|
||||
| tests.c:29:28:29:31 | argv | tests.c:31:15:31:23 | buffer100 indirection |
|
||||
| tests.c:29:28:29:31 | argv | tests.c:33:21:33:29 | buffer100 |
|
||||
| tests.c:29:28:29:31 | argv | tests.c:33:21:33:29 | buffer100 |
|
||||
| tests.c:29:28:29:31 | argv | tests.c:33:21:33:29 | buffer100 indirection |
|
||||
| tests.c:29:28:29:31 | argv | tests.c:33:21:33:29 | buffer100 indirection |
|
||||
| tests.c:31:15:31:23 | array to pointer conversion | tests.c:31:15:31:23 | buffer100 |
|
||||
| tests.c:31:15:31:23 | array to pointer conversion | tests.c:31:15:31:23 | buffer100 indirection |
|
||||
| tests.c:31:15:31:23 | array to pointer conversion | tests.c:33:21:33:29 | buffer100 |
|
||||
| tests.c:31:15:31:23 | array to pointer conversion | tests.c:33:21:33:29 | buffer100 indirection |
|
||||
| tests.c:31:15:31:23 | buffer100 | tests.c:31:15:31:23 | buffer100 |
|
||||
| tests.c:31:15:31:23 | buffer100 | tests.c:31:15:31:23 | buffer100 indirection |
|
||||
| tests.c:31:15:31:23 | buffer100 | tests.c:33:21:33:29 | buffer100 |
|
||||
| tests.c:31:15:31:23 | buffer100 | tests.c:33:21:33:29 | buffer100 indirection |
|
||||
| tests.c:31:15:31:23 | scanf output argument | tests.c:33:21:33:29 | buffer100 |
|
||||
| tests.c:31:15:31:23 | scanf output argument | tests.c:33:21:33:29 | buffer100 indirection |
|
||||
| tests.c:33:21:33:29 | array to pointer conversion | tests.c:33:21:33:29 | buffer100 |
|
||||
| tests.c:33:21:33:29 | array to pointer conversion | tests.c:33:21:33:29 | buffer100 indirection |
|
||||
| tests.c:33:21:33:29 | buffer100 | tests.c:33:21:33:29 | buffer100 |
|
||||
| tests.c:33:21:33:29 | buffer100 | tests.c:33:21:33:29 | buffer100 indirection |
|
||||
| tests.c:34:10:34:13 | argv | tests.c:34:10:34:16 | (const char *)... |
|
||||
| tests.c:34:10:34:13 | argv | tests.c:34:10:34:16 | (const char *)... |
|
||||
| tests.c:34:10:34:13 | argv | tests.c:34:10:34:16 | access to array |
|
||||
@@ -65,16 +35,11 @@ nodes
|
||||
| tests.c:29:28:29:34 | access to array | semmle.label | access to array |
|
||||
| tests.c:29:28:29:34 | access to array indirection | semmle.label | access to array indirection |
|
||||
| tests.c:31:15:31:23 | array to pointer conversion | semmle.label | array to pointer conversion |
|
||||
| tests.c:31:15:31:23 | array to pointer conversion | semmle.label | array to pointer conversion |
|
||||
| tests.c:31:15:31:23 | buffer100 | semmle.label | buffer100 |
|
||||
| tests.c:31:15:31:23 | buffer100 | semmle.label | buffer100 |
|
||||
| tests.c:31:15:31:23 | buffer100 indirection | semmle.label | buffer100 indirection |
|
||||
| tests.c:31:15:31:23 | scanf output argument | semmle.label | scanf output argument |
|
||||
| tests.c:33:21:33:29 | array to pointer conversion | semmle.label | array to pointer conversion |
|
||||
| tests.c:33:21:33:29 | array to pointer conversion | semmle.label | array to pointer conversion |
|
||||
| tests.c:33:21:33:29 | buffer100 | semmle.label | buffer100 |
|
||||
| tests.c:33:21:33:29 | buffer100 | semmle.label | buffer100 |
|
||||
| tests.c:33:21:33:29 | buffer100 indirection | semmle.label | buffer100 indirection |
|
||||
| tests.c:34:10:34:13 | argv | semmle.label | argv |
|
||||
| tests.c:34:10:34:13 | argv | semmle.label | argv |
|
||||
| tests.c:34:10:34:16 | (const char *)... | semmle.label | (const char *)... |
|
||||
@@ -84,11 +49,6 @@ nodes
|
||||
#select
|
||||
| tests.c:28:3:28:9 | call to sprintf | tests.c:28:22:28:25 | argv | tests.c:28:22:28:28 | access to array | This 'call to sprintf' with input from $@ may overflow the destination. | tests.c:28:22:28:25 | argv | argv |
|
||||
| tests.c:29:3:29:9 | call to sprintf | tests.c:29:28:29:31 | argv | tests.c:29:28:29:34 | access to array | This 'call to sprintf' with input from $@ may overflow the destination. | tests.c:29:28:29:31 | argv | argv |
|
||||
| tests.c:31:15:31:23 | buffer100 | tests.c:28:22:28:25 | argv | tests.c:31:15:31:23 | buffer100 | This 'scanf string argument' with input from $@ may overflow the destination. | tests.c:28:22:28:25 | argv | argv |
|
||||
| tests.c:31:15:31:23 | buffer100 | tests.c:29:28:29:31 | argv | tests.c:31:15:31:23 | buffer100 | This 'scanf string argument' with input from $@ may overflow the destination. | tests.c:29:28:29:31 | argv | argv |
|
||||
| tests.c:31:15:31:23 | buffer100 | tests.c:31:15:31:23 | buffer100 | tests.c:31:15:31:23 | buffer100 | This 'scanf string argument' with input from $@ may overflow the destination. | tests.c:31:15:31:23 | buffer100 | buffer100 |
|
||||
| tests.c:33:21:33:29 | buffer100 | tests.c:28:22:28:25 | argv | tests.c:33:21:33:29 | buffer100 | This 'scanf string argument' with input from $@ may overflow the destination. | tests.c:28:22:28:25 | argv | argv |
|
||||
| tests.c:33:21:33:29 | buffer100 | tests.c:29:28:29:31 | argv | tests.c:33:21:33:29 | buffer100 | This 'scanf string argument' with input from $@ may overflow the destination. | tests.c:29:28:29:31 | argv | argv |
|
||||
| tests.c:33:21:33:29 | buffer100 | tests.c:31:15:31:23 | buffer100 | tests.c:33:21:33:29 | buffer100 | This 'scanf string argument' with input from $@ may overflow the destination. | tests.c:31:15:31:23 | buffer100 | buffer100 |
|
||||
| tests.c:33:21:33:29 | buffer100 | tests.c:33:21:33:29 | buffer100 | tests.c:33:21:33:29 | buffer100 | This 'scanf string argument' with input from $@ may overflow the destination. | tests.c:33:21:33:29 | buffer100 | buffer100 |
|
||||
| tests.c:34:25:34:33 | buffer100 | tests.c:34:10:34:13 | argv | tests.c:34:10:34:16 | access to array | This 'sscanf string argument' with input from $@ may overflow the destination. | tests.c:34:10:34:13 | argv | argv |
|
||||
|
||||
@@ -332,4 +332,13 @@ void ptr_diff_case() {
|
||||
char* admin_begin_pos = strstr(user, "ADMIN");
|
||||
int offset = admin_begin_pos ? user - admin_begin_pos : 0;
|
||||
malloc(offset); // GOOD
|
||||
}
|
||||
}
|
||||
|
||||
void equality_barrier() {
|
||||
int size1 = atoi(getenv("USER"));
|
||||
int size2 = atoi(getenv("USER"));
|
||||
|
||||
if (size1 == size2) {
|
||||
int* a = (int*)malloc(size1 * sizeof(int)); // GOOD
|
||||
}
|
||||
}
|
||||
|
||||
@@ -95,5 +95,12 @@ int main(int argc, char** argv) {
|
||||
}
|
||||
}
|
||||
|
||||
// GOOD: check the user input first
|
||||
int maxConnections3 = atoi(argv[1]);
|
||||
int maxConnections4 = atoi(argv[1]);
|
||||
if (maxConnections3 == maxConnections4) {
|
||||
startServer(maxConnections3 * 1000);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -6,5 +6,5 @@ nodes
|
||||
| test.cpp:58:25:58:29 | input | semmle.label | input |
|
||||
subpaths
|
||||
#select
|
||||
| test2.cpp:110:3:110:6 | call to gets | test2.cpp:110:3:110:6 | call to gets | test2.cpp:110:3:110:6 | call to gets | This write into buffer 'password' may contain unencrypted data from $@. | test2.cpp:110:3:110:6 | call to gets | user input (String read by gets) |
|
||||
| test2.cpp:110:3:110:6 | call to gets | test2.cpp:110:3:110:6 | call to gets | test2.cpp:110:3:110:6 | call to gets | This write into buffer 'password' may contain unencrypted data from $@. | test2.cpp:110:3:110:6 | call to gets | user input (string read by gets) |
|
||||
| test.cpp:58:3:58:9 | call to sprintf | test.cpp:54:17:54:20 | argv | test.cpp:58:25:58:29 | input | This write into buffer 'passwd' may contain unencrypted data from $@. | test.cpp:54:17:54:20 | argv | user input (a command-line argument) |
|
||||
|
||||
Reference in New Issue
Block a user