Merge branch 'main' into call-graph-code

This commit is contained in:
Rasmus Wriedt Larsen
2022-12-08 11:39:30 +01:00
1482 changed files with 86902 additions and 91835 deletions

View File

@@ -0,0 +1,5 @@
---
category: minorAnalysis
---
* The data-flow library has been rewritten to no longer rely on the points-to analysis in order to
resolve references to modules. Improvements in the module resolution can lead to more results.

View File

@@ -9,6 +9,7 @@ private import semmle.python.dataflow.new.DataFlow
private import semmle.python.dataflow.new.RemoteFlowSources
private import semmle.python.dataflow.new.TaintTracking
private import semmle.python.Frameworks
private import semmle.python.security.internal.EncryptionKeySizes
/**
* A data-flow node that executes an operating system command,
@@ -1141,21 +1142,21 @@ module Cryptography {
abstract class RsaRange extends Range {
final override string getName() { result = "RSA" }
final override int minimumSecureKeySize() { result = 2048 }
final override int minimumSecureKeySize() { result = minSecureKeySizeRsa() }
}
/** A data-flow node that generates a new DSA key-pair. */
abstract class DsaRange extends Range {
final override string getName() { result = "DSA" }
final override int minimumSecureKeySize() { result = 2048 }
final override int minimumSecureKeySize() { result = minSecureKeySizeDsa() }
}
/** A data-flow node that generates a new ECC key-pair. */
abstract class EccRange extends Range {
final override string getName() { result = "ECC" }
final override int minimumSecureKeySize() { result = 224 }
final override int minimumSecureKeySize() { result = minSecureKeySizeEcc() }
}
}
}

View File

@@ -454,7 +454,7 @@ module Impl implements RegexTreeViewSig {
override string getPrimaryQLClass() { result = "RegExpAlt" }
}
class RegExpCharEscape = RegExpEscape;
additional class RegExpCharEscape = RegExpEscape;
/**
* An escaped regular expression term, that is, a regular expression
@@ -684,7 +684,7 @@ module Impl implements RegexTreeViewSig {
* \t
* ```
*/
class RegExpNormalChar extends RegExpTerm, TRegExpNormalChar {
additional class RegExpNormalChar extends RegExpTerm, TRegExpNormalChar {
RegExpNormalChar() { this = TRegExpNormalChar(re, start, end) }
/**
@@ -792,7 +792,7 @@ module Impl implements RegexTreeViewSig {
* .
* ```
*/
class RegExpSpecialChar extends RegExpTerm, TRegExpSpecialChar {
additional class RegExpSpecialChar extends RegExpTerm, TRegExpSpecialChar {
string char;
RegExpSpecialChar() {
@@ -868,7 +868,7 @@ module Impl implements RegexTreeViewSig {
* (?=\w)
* ```
*/
class RegExpZeroWidthMatch extends RegExpGroup {
additional class RegExpZeroWidthMatch extends RegExpGroup {
RegExpZeroWidthMatch() { re.zeroWidthMatch(start, end) }
override RegExpTerm getChild(int i) { none() }
@@ -937,7 +937,7 @@ module Impl implements RegexTreeViewSig {
* (?!\n)
* ```
*/
class RegExpNegativeLookahead extends RegExpLookahead {
additional class RegExpNegativeLookahead extends RegExpLookahead {
RegExpNegativeLookahead() { re.negativeLookaheadAssertionGroup(start, end) }
override string getPrimaryQLClass() { result = "RegExpNegativeLookahead" }
@@ -979,7 +979,7 @@ module Impl implements RegexTreeViewSig {
* (?<!\\)
* ```
*/
class RegExpNegativeLookbehind extends RegExpLookbehind {
additional class RegExpNegativeLookbehind extends RegExpLookbehind {
RegExpNegativeLookbehind() { re.negativeLookbehindAssertionGroup(start, end) }
override string getPrimaryQLClass() { result = "RegExpNegativeLookbehind" }

View File

@@ -1,4 +1,5 @@
/* This file contains test-related utility functions */
/** This file contains test-related utility functions */
import python
/** Removes everything up to the occurrence of `sub` in the string `str` */

View File

@@ -8,6 +8,7 @@ private import semmle.python.dataflow.new.DataFlow
// Need to import `semmle.python.Frameworks` since frameworks can extend `SensitiveDataSource::Range`
private import semmle.python.Frameworks
private import semmle.python.security.internal.SensitiveDataHeuristics as SensitiveDataHeuristics
private import semmle.python.ApiGraphs
// We export these explicitly, so we don't also export the `HeuristicNames` module.
class SensitiveDataClassification = SensitiveDataHeuristics::SensitiveDataClassification;
@@ -23,7 +24,17 @@ module SensitiveDataClassification = SensitiveDataHeuristics::SensitiveDataClass
class SensitiveDataSource extends DataFlow::Node {
SensitiveDataSource::Range range;
SensitiveDataSource() { this = range }
SensitiveDataSource() {
this = range and
// ignore sensitive password sources in getpass.py, that can escape through `getpass.getpass()` return value,
// since `getpass.getpass()` is considered a source itself.
not exists(Module getpass |
getpass.getName() = "getpass" and
this.getScope().getEnclosingModule() = getpass and
// do allow this call if we're analyzing getpass.py as part of CPython though
not exists(getpass.getFile().getRelativePath())
)
}
/**
* Gets the classification of the sensitive data.
@@ -312,6 +323,17 @@ private module SensitiveDataModeling {
override SensitiveDataClassification getClassification() { result = classification }
}
/**
* A call to `getpass.getpass`, see https://docs.python.org/3.10/library/getpass.html#getpass.getpass
*/
class GetPassCall extends SensitiveDataSource::Range, API::CallNode {
GetPassCall() { this = API::moduleImport("getpass").getMember("getpass").getACall() }
override SensitiveDataClassification getClassification() {
result = SensitiveDataClassification::password()
}
}
}
predicate sensitiveDataExtraStepForCalls = SensitiveDataModeling::extraStepForCalls/2;

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -520,7 +520,8 @@ module Private {
predicate summaryParameterNodeRange(SummarizedCallable c, ParameterPosition pos) {
parameterReadState(c, _, pos)
or
isParameterPostUpdate(_, c, pos)
// Same as `isParameterPostUpdate(_, c, pos)`, but can be used in a negative context
any(SummaryNodeState state).isOutputState(c, SummaryComponentStack::argument(pos))
}
private predicate callbackOutput(

View File

@@ -13,35 +13,36 @@ private import semmle.python.frameworks.data.ModelsAsData
private module Asyncpg {
class AsyncpgModel extends ModelInput::TypeModelCsv {
override predicate row(string row) {
// package1;type1;package2;type2;path
// type1;type2;path
row =
[
// a `ConnectionPool` that is created when the result of `asyncpg.create_pool()` is awaited.
"asyncpg;ConnectionPool;asyncpg;;Member[create_pool].ReturnValue.Awaited",
"asyncpg.ConnectionPool;asyncpg;Member[create_pool].ReturnValue.Awaited",
// a `Connection` that is created when
// * - the result of `asyncpg.connect()` is awaited.
// * - the result of calling `acquire` on a `ConnectionPool` is awaited.
"asyncpg;Connection;asyncpg;;Member[connect].ReturnValue.Awaited",
"asyncpg;Connection;asyncpg;ConnectionPool;Member[acquire].ReturnValue.Awaited",
"asyncpg.Connection;asyncpg;Member[connect].ReturnValue.Awaited",
"asyncpg.Connection;asyncpg.ConnectionPool;Member[acquire].ReturnValue.Awaited",
// Creating an internal `~Connection` type that contains both `Connection` and `ConnectionPool`.
"asyncpg;~Connection;asyncpg;Connection;", "asyncpg;~Connection;asyncpg;ConnectionPool;"
"asyncpg.~Connection;asyncpg.Connection;", //
"asyncpg.~Connection;asyncpg.ConnectionPool;"
]
}
}
class AsyncpgSink extends ModelInput::SinkModelCsv {
// package;type;path;kind
// type;path;kind
override predicate row(string row) {
row =
[
// `Connection`s and `ConnectionPool`s provide some methods that execute SQL.
"asyncpg;~Connection;Member[copy_from_query,execute,fetch,fetchrow,fetchval].Argument[0,query:];sql-injection",
"asyncpg;~Connection;Member[executemany].Argument[0,command:];sql-injection",
"asyncpg.~Connection;Member[copy_from_query,execute,fetch,fetchrow,fetchval].Argument[0,query:];sql-injection",
"asyncpg.~Connection;Member[executemany].Argument[0,command:];sql-injection",
// A model of `Connection` and `ConnectionPool`, which provide some methods that access the file system.
"asyncpg;~Connection;Member[copy_from_query,copy_from_table].Argument[output:];path-injection",
"asyncpg;~Connection;Member[copy_to_table].Argument[source:];path-injection",
"asyncpg.~Connection;Member[copy_from_query,copy_from_table].Argument[output:];path-injection",
"asyncpg.~Connection;Member[copy_to_table].Argument[source:];path-injection",
// the `PreparedStatement` class in `asyncpg`.
"asyncpg;Connection;Member[prepare].Argument[0,query:];sql-injection",
"asyncpg.Connection;Member[prepare].Argument[0,query:];sql-injection",
]
}
}
@@ -58,7 +59,7 @@ private module Asyncpg {
module Cursor {
class CursorConstruction extends SqlConstruction::Range, API::CallNode {
CursorConstruction() {
this = ModelOutput::getATypeNode("asyncpg", "Connection").getMember("cursor").getACall()
this = ModelOutput::getATypeNode("asyncpg.Connection").getMember("cursor").getACall()
}
override DataFlow::Node getSql() { result = this.getParameter(0, "query").asSink() }
@@ -76,7 +77,7 @@ private module Asyncpg {
or
exists(API::CallNode prepareCall |
prepareCall =
ModelOutput::getATypeNode("asyncpg", "Connection").getMember("prepare").getACall()
ModelOutput::getATypeNode("asyncpg.Connection").getMember("prepare").getACall()
|
sql = prepareCall.getParameter(0, "query").asSink() and
this =

View File

@@ -5,23 +5,20 @@
*
* The CSV specification has the following columns:
* - Sources:
* `package; type; path; kind`
* `type; path; kind`
* - Sinks:
* `package; type; path; kind`
* `type; path; kind`
* - Summaries:
* `package; type; path; input; output; kind`
* `type; path; input; output; kind`
* - Types:
* `package1; type1; package2; type2; path`
* `type1; type2; path`
*
* The interpretation of a row is similar to API-graphs with a left-to-right
* reading.
* 1. The `package` column selects a package name, as it would be referenced in the source code,
* such as an NPM package, PIP package, or Ruby gem. (See `ModelsAsData.qll` for language-specific details).
* It may also be a synthetic package used for a type definition (see type definitions below).
* 2. The `type` column selects all instances of a named type originating from that package,
* or the empty string if referring to the package itself.
* 1. The `type` column selects all instances of a named type. The syntax of this column is language-specific.
* The language defines some type names that the analysis knows how to identify without models.
* It can also be a synthetic type name defined by a type definition (see type definitions below).
* 3. The `path` column is a `.`-separated list of "access path tokens" to resolve, starting at the node selected by `package` and `type`.
* 2. The `path` column is a `.`-separated list of "access path tokens" to resolve, starting at the node selected by `type`.
*
* Every language supports the following tokens:
* - Argument[n]: the n-th argument to a call. May be a range of form `x..y` (inclusive) and/or a comma-separated list.
@@ -42,10 +39,10 @@
*
* For the time being, please consult `ApiGraphModelsSpecific.qll` to see which language-specific tokens are currently supported.
*
* 4. The `input` and `output` columns specify how data enters and leaves the element selected by the
* first `(package, type, path)` tuple. Both strings are `.`-separated access paths
* 3. The `input` and `output` columns specify how data enters and leaves the element selected by the
* first `(type, path)` tuple. Both strings are `.`-separated access paths
* of the same syntax as the `path` column.
* 5. The `kind` column is a tag that can be referenced from QL to determine to
* 4. The `kind` column is a tag that can be referenced from QL to determine to
* which classes the interpreted elements should be added. For example, for
* sources `"remote"` indicates a default remote flow source, and for summaries
* `"taint"` indicates a default additional taint step and `"value"` indicates a
@@ -53,17 +50,17 @@
*
* ### Types
*
* A type row of form `package1; type1; package2; type2; path` indicates that `package2; type2; path`
* should be seen as an instance of the type `package1; type1`.
* A type row of form `type1; type2; path` indicates that `type2; path`
* should be seen as an instance of the type `type1`.
*
* A `(package,type)` pair may refer to a static type or a synthetic type name used internally in the model.
* A type may refer to a static type or a synthetic type name used internally in the model.
* Synthetic type names can be used to reuse intermediate sub-paths, when there are multiple ways to access the same
* element.
* See `ModelsAsData.qll` for the language-specific interpretation of packages and static type names.
* See `ModelsAsData.qll` for the language-specific interpretation of type names.
*
* By convention, if one wants to avoid clashes with static types from the package, the type name
* should be prefixed with a tilde character (`~`). For example, `(foo, ~Bar)` can be used to indicate that
* the type is related to the `foo` package but is not intended to match a static type.
* By convention, if one wants to avoid clashes with static types, the type name
* should be prefixed with a tilde character (`~`). For example, `~Bar` can be used to indicate that
* the type is not intended to match a static type.
*/
private import ApiGraphModelsSpecific as Specific
@@ -89,9 +86,9 @@ module ModelInput {
*
* A row of form
* ```
* package;type;path;kind
* type;path;kind
* ```
* indicates that the value at `(package, type, path)` should be seen as a flow
* indicates that the value at `(type, path)` should be seen as a flow
* source of the given `kind`.
*
* The kind `remote` represents a general remote flow source.
@@ -110,9 +107,9 @@ module ModelInput {
*
* A row of form
* ```
* package;type;path;kind
* type;path;kind
* ```
* indicates that the value at `(package, type, path)` should be seen as a sink
* indicates that the value at `(type, path)` should be seen as a sink
* of the given `kind`.
*/
abstract predicate row(string row);
@@ -129,9 +126,9 @@ module ModelInput {
*
* A row of form
* ```
* package;type;path;input;output;kind
* type;path;input;output;kind
* ```
* indicates that for each call to `(package, type, path)`, the value referred to by `input`
* indicates that for each call to `(type, path)`, the value referred to by `input`
* can flow to the value referred to by `output`.
*
* `kind` should be either `value` or `taint`, for value-preserving or taint-preserving steps,
@@ -151,9 +148,9 @@ module ModelInput {
*
* A row of form,
* ```
* package1;type1;package2;type2;path
* type1;type2;path
* ```
* indicates that `(package2, type2, path)` should be seen as an instance of `(package1, type1)`.
* indicates that `(type2, path)` should be seen as an instance of `type1`.
*/
abstract predicate row(string row);
}
@@ -163,28 +160,28 @@ module ModelInput {
*/
class TypeModel extends Unit {
/**
* Gets a data-flow node that is a source of the type `package;type`.
* Gets a data-flow node that is a source of the given `type`.
*
* This must not depend on API graphs, but ensures that an API node is generated for
* the source.
*/
DataFlow::Node getASource(string package, string type) { none() }
DataFlow::Node getASource(string type) { none() }
/**
* Gets a data-flow node that is a sink of the type `package;type`,
* Gets a data-flow node that is a sink of the given `type`,
* usually because it is an argument passed to a parameter of that type.
*
* This must not depend on API graphs, but ensures that an API node is generated for
* the sink.
*/
DataFlow::Node getASink(string package, string type) { none() }
DataFlow::Node getASink(string type) { none() }
/**
* Gets an API node that is a source or sink of the type `package;type`.
* Gets an API node that is a source or sink of the given `type`.
*
* Unlike `getASource` and `getASink`, this may depend on API graphs.
*/
API::Node getAnApiNode(string package, string type) { none() }
API::Node getAnApiNode(string type) { none() }
}
/**
@@ -209,7 +206,7 @@ private import ModelInput
/**
* An empty class, except in specific tests.
*
* If this is non-empty, all models are parsed even if the package is not
* If this is non-empty, all models are parsed even if the type name is not
* considered relevant for the current database.
*/
abstract class TestAllModels extends Unit { }
@@ -232,53 +229,44 @@ private predicate typeModel(string row) { any(TypeModelCsv s).row(inversePad(row
private predicate typeVariableModel(string row) { any(TypeVariableModelCsv s).row(inversePad(row)) }
/** Holds if a source model exists for the given parameters. */
predicate sourceModel(string package, string type, string path, string kind) {
predicate sourceModel(string type, string path, string kind) {
exists(string row |
sourceModel(row) and
row.splitAt(";", 0) = package and
row.splitAt(";", 1) = type and
row.splitAt(";", 2) = path and
row.splitAt(";", 3) = kind
row.splitAt(";", 0) = type and
row.splitAt(";", 1) = path and
row.splitAt(";", 2) = kind
)
}
/** Holds if a sink model exists for the given parameters. */
private predicate sinkModel(string package, string type, string path, string kind) {
private predicate sinkModel(string type, string path, string kind) {
exists(string row |
sinkModel(row) and
row.splitAt(";", 0) = package and
row.splitAt(";", 1) = type and
row.splitAt(";", 2) = path and
row.splitAt(";", 3) = kind
row.splitAt(";", 0) = type and
row.splitAt(";", 1) = path and
row.splitAt(";", 2) = kind
)
}
/** Holds if a summary model `row` exists for the given parameters. */
private predicate summaryModel(
string package, string type, string path, string input, string output, string kind
) {
private predicate summaryModel(string type, string path, string input, string output, string kind) {
exists(string row |
summaryModel(row) and
row.splitAt(";", 0) = package and
row.splitAt(";", 1) = type and
row.splitAt(";", 2) = path and
row.splitAt(";", 3) = input and
row.splitAt(";", 4) = output and
row.splitAt(";", 5) = kind
row.splitAt(";", 0) = type and
row.splitAt(";", 1) = path and
row.splitAt(";", 2) = input and
row.splitAt(";", 3) = output and
row.splitAt(";", 4) = kind
)
}
/** Holds if a type model exists for the given parameters. */
private predicate typeModel(
string package1, string type1, string package2, string type2, string path
) {
private predicate typeModel(string type1, string type2, string path) {
exists(string row |
typeModel(row) and
row.splitAt(";", 0) = package1 and
row.splitAt(";", 1) = type1 and
row.splitAt(";", 2) = package2 and
row.splitAt(";", 3) = type2 and
row.splitAt(";", 4) = path
row.splitAt(";", 0) = type1 and
row.splitAt(";", 1) = type2 and
row.splitAt(";", 2) = path
)
}
@@ -292,61 +280,50 @@ private predicate typeVariableModel(string name, string path) {
}
/**
* Gets a package that should be seen as an alias for the given other `package`,
* or the `package` itself.
* Holds if CSV rows involving `type` might be relevant for the analysis of this database.
*/
bindingset[package]
bindingset[result]
string getAPackageAlias(string package) {
typeModel(package, "", result, "", "")
or
result = package
}
/**
* Holds if CSV rows involving `package` might be relevant for the analysis of this database.
*/
private predicate isRelevantPackage(string package) {
predicate isRelevantType(string type) {
(
sourceModel(package, _, _, _) or
sinkModel(package, _, _, _) or
summaryModel(package, _, _, _, _, _) or
typeModel(_, _, package, _, _)
sourceModel(type, _, _) or
sinkModel(type, _, _) or
summaryModel(type, _, _, _, _) or
typeModel(_, type, _)
) and
(
Specific::isPackageUsed(package)
Specific::isTypeUsed(type)
or
exists(TestAllModels t)
)
or
exists(string other |
isRelevantPackage(other) and
typeModel(package, _, other, _, _)
exists(string other | isRelevantType(other) |
typeModel(type, other, _)
or
Specific::hasImplicitTypeModel(type, other)
)
}
/**
* Holds if `package,type,path` is used in some CSV row.
* Holds if `type,path` is used in some CSV row.
*/
pragma[nomagic]
predicate isRelevantFullPath(string package, string type, string path) {
isRelevantPackage(package) and
predicate isRelevantFullPath(string type, string path) {
isRelevantType(type) and
(
sourceModel(package, type, path, _) or
sinkModel(package, type, path, _) or
summaryModel(package, type, path, _, _, _) or
typeModel(_, _, package, type, path)
sourceModel(type, path, _) or
sinkModel(type, path, _) or
summaryModel(type, path, _, _, _) or
typeModel(_, type, path)
)
}
/** A string from a CSV row that should be parsed as an access path. */
private class AccessPathRange extends AccessPath::Range {
AccessPathRange() {
isRelevantFullPath(_, _, this)
isRelevantFullPath(_, this)
or
exists(string package | isRelevantPackage(package) |
summaryModel(package, _, _, this, _, _) or
summaryModel(package, _, _, _, this, _)
exists(string type | isRelevantType(type) |
summaryModel(type, _, this, _, _) or
summaryModel(type, _, _, this, _)
)
or
typeVariableModel(_, this)
@@ -400,83 +377,73 @@ private predicate invocationMatchesCallSiteFilter(Specific::InvokeNode invoke, A
}
private class TypeModelUseEntry extends API::EntryPoint {
private string package;
private string type;
TypeModelUseEntry() {
exists(any(TypeModel tm).getASource(package, type)) and
this = "TypeModelUseEntry;" + package + ";" + type
exists(any(TypeModel tm).getASource(type)) and
this = "TypeModelUseEntry;" + type
}
override DataFlow::LocalSourceNode getASource() {
result = any(TypeModel tm).getASource(package, type)
}
override DataFlow::LocalSourceNode getASource() { result = any(TypeModel tm).getASource(type) }
API::Node getNodeForType(string package_, string type_) {
package = package_ and type = type_ and result = this.getANode()
}
API::Node getNodeForType(string type_) { type = type_ and result = this.getANode() }
}
private class TypeModelDefEntry extends API::EntryPoint {
private string package;
private string type;
TypeModelDefEntry() {
exists(any(TypeModel tm).getASink(package, type)) and
this = "TypeModelDefEntry;" + package + ";" + type
exists(any(TypeModel tm).getASink(type)) and
this = "TypeModelDefEntry;" + type
}
override DataFlow::Node getASink() { result = any(TypeModel tm).getASink(package, type) }
override DataFlow::Node getASink() { result = any(TypeModel tm).getASink(type) }
API::Node getNodeForType(string package_, string type_) {
package = package_ and type = type_ and result = this.getANode()
}
API::Node getNodeForType(string type_) { type = type_ and result = this.getANode() }
}
/**
* Gets an API node identified by the given `(package,type)` pair.
* Gets an API node identified by the given `type`.
*/
pragma[nomagic]
private API::Node getNodeFromType(string package, string type) {
exists(string package2, string type2, AccessPath path2 |
typeModel(package, type, package2, type2, path2) and
result = getNodeFromPath(package2, type2, path2)
private API::Node getNodeFromType(string type) {
exists(string type2, AccessPath path2 |
typeModel(type, type2, path2) and
result = getNodeFromPath(type2, path2)
)
or
result = any(TypeModelUseEntry e).getNodeForType(package, type)
result = any(TypeModelUseEntry e).getNodeForType(type)
or
result = any(TypeModelDefEntry e).getNodeForType(package, type)
result = any(TypeModelDefEntry e).getNodeForType(type)
or
result = any(TypeModel t).getAnApiNode(package, type)
result = any(TypeModel t).getAnApiNode(type)
or
result = Specific::getExtraNodeFromType(package, type)
result = Specific::getExtraNodeFromType(type)
}
/**
* Gets the API node identified by the first `n` tokens of `path` in the given `(package, type, path)` tuple.
* Gets the API node identified by the first `n` tokens of `path` in the given `(type, path)` tuple.
*/
pragma[nomagic]
private API::Node getNodeFromPath(string package, string type, AccessPath path, int n) {
isRelevantFullPath(package, type, path) and
private API::Node getNodeFromPath(string type, AccessPath path, int n) {
isRelevantFullPath(type, path) and
(
n = 0 and
result = getNodeFromType(package, type)
result = getNodeFromType(type)
or
result = Specific::getExtraNodeFromPath(package, type, path, n)
result = Specific::getExtraNodeFromPath(type, path, n)
)
or
result = getSuccessorFromNode(getNodeFromPath(package, type, path, n - 1), path.getToken(n - 1))
result = getSuccessorFromNode(getNodeFromPath(type, path, n - 1), path.getToken(n - 1))
or
// Similar to the other recursive case, but where the path may have stepped through one or more call-site filters
result =
getSuccessorFromInvoke(getInvocationFromPath(package, type, path, n - 1), path.getToken(n - 1))
result = getSuccessorFromInvoke(getInvocationFromPath(type, path, n - 1), path.getToken(n - 1))
or
// Apply a subpath
result =
getNodeFromSubPath(getNodeFromPath(package, type, path, n - 1), getSubPathAt(path, n - 1))
result = getNodeFromSubPath(getNodeFromPath(type, path, n - 1), getSubPathAt(path, n - 1))
or
// Apply a type step
typeStep(getNodeFromPath(package, type, path, n), result)
typeStep(getNodeFromPath(type, path, n), result)
}
/**
@@ -496,15 +463,15 @@ private AccessPath getSubPathAt(AccessPath path, int n) {
pragma[nomagic]
private API::Node getNodeFromSubPath(API::Node base, AccessPath subPath, int n) {
exists(AccessPath path, int k |
base = [getNodeFromPath(_, _, path, k), getNodeFromSubPath(_, path, k)] and
base = [getNodeFromPath(_, path, k), getNodeFromSubPath(_, path, k)] and
subPath = getSubPathAt(path, k) and
result = base and
n = 0
)
or
exists(string package, string type, AccessPath basePath |
typeStepModel(package, type, basePath, subPath) and
base = getNodeFromPath(package, type, basePath) and
exists(string type, AccessPath basePath |
typeStepModel(type, basePath, subPath) and
base = getNodeFromPath(type, basePath) and
result = base and
n = 0
)
@@ -543,42 +510,40 @@ private API::Node getNodeFromSubPath(API::Node base, AccessPath subPath) {
result = getNodeFromSubPath(base, subPath, subPath.getNumToken())
}
/** Gets the node identified by the given `(package, type, path)` tuple. */
private API::Node getNodeFromPath(string package, string type, AccessPath path) {
result = getNodeFromPath(package, type, path, path.getNumToken())
/** Gets the node identified by the given `(type, path)` tuple. */
private API::Node getNodeFromPath(string type, AccessPath path) {
result = getNodeFromPath(type, path, path.getNumToken())
}
pragma[nomagic]
private predicate typeStepModel(string package, string type, AccessPath basePath, AccessPath output) {
summaryModel(package, type, basePath, "", output, "type")
private predicate typeStepModel(string type, AccessPath basePath, AccessPath output) {
summaryModel(type, basePath, "", output, "type")
}
pragma[nomagic]
private predicate typeStep(API::Node pred, API::Node succ) {
exists(string package, string type, AccessPath basePath, AccessPath output |
typeStepModel(package, type, basePath, output) and
pred = getNodeFromPath(package, type, basePath) and
exists(string type, AccessPath basePath, AccessPath output |
typeStepModel(type, basePath, output) and
pred = getNodeFromPath(type, basePath) and
succ = getNodeFromSubPath(pred, output)
)
}
/**
* Gets an invocation identified by the given `(package, type, path)` tuple.
* Gets an invocation identified by the given `(type, path)` tuple.
*
* Unlike `getNodeFromPath`, the `path` may end with one or more call-site filters.
*/
private Specific::InvokeNode getInvocationFromPath(
string package, string type, AccessPath path, int n
) {
result = Specific::getAnInvocationOf(getNodeFromPath(package, type, path, n))
private Specific::InvokeNode getInvocationFromPath(string type, AccessPath path, int n) {
result = Specific::getAnInvocationOf(getNodeFromPath(type, path, n))
or
result = getInvocationFromPath(package, type, path, n - 1) and
result = getInvocationFromPath(type, path, n - 1) and
invocationMatchesCallSiteFilter(result, path.getToken(n - 1))
}
/** Gets an invocation identified by the given `(package, type, path)` tuple. */
private Specific::InvokeNode getInvocationFromPath(string package, string type, AccessPath path) {
result = getInvocationFromPath(package, type, path, path.getNumToken())
/** Gets an invocation identified by the given `(type, path)` tuple. */
private Specific::InvokeNode getInvocationFromPath(string type, AccessPath path) {
result = getInvocationFromPath(type, path, path.getNumToken())
}
/**
@@ -631,9 +596,9 @@ module ModelOutput {
*/
cached
API::Node getASourceNode(string kind) {
exists(string package, string type, string path |
sourceModel(package, type, path, kind) and
result = getNodeFromPath(package, type, path)
exists(string type, string path |
sourceModel(type, path, kind) and
result = getNodeFromPath(type, path)
)
}
@@ -642,9 +607,9 @@ module ModelOutput {
*/
cached
API::Node getASinkNode(string kind) {
exists(string package, string type, string path |
sinkModel(package, type, path, kind) and
result = getNodeFromPath(package, type, path)
exists(string type, string path |
sinkModel(type, path, kind) and
result = getNodeFromPath(type, path)
)
}
@@ -653,32 +618,31 @@ module ModelOutput {
*/
cached
predicate relevantSummaryModel(
string package, string type, string path, string input, string output, string kind
string type, string path, string input, string output, string kind
) {
isRelevantPackage(package) and
summaryModel(package, type, path, input, output, kind)
isRelevantType(type) and
summaryModel(type, path, input, output, kind)
}
/**
* Holds if a `baseNode` is an invocation identified by the `package,type,path` part of a summary row.
* Holds if a `baseNode` is an invocation identified by the `type,path` part of a summary row.
*/
cached
predicate resolvedSummaryBase(
string package, string type, string path, Specific::InvokeNode baseNode
) {
summaryModel(package, type, path, _, _, _) and
baseNode = getInvocationFromPath(package, type, path)
predicate resolvedSummaryBase(string type, string path, Specific::InvokeNode baseNode) {
summaryModel(type, path, _, _, _) and
baseNode = getInvocationFromPath(type, path)
}
/**
* Holds if `node` is seen as an instance of `(package,type)` due to a type definition
* Holds if `node` is seen as an instance of `type` due to a type definition
* contributed by a CSV model.
*/
cached
API::Node getATypeNode(string package, string type) { result = getNodeFromType(package, type) }
API::Node getATypeNode(string type) { result = getNodeFromType(type) }
}
import Cached
import Specific::ModelOutputSpecific
/**
* Gets an error message relating to an invalid CSV row in a model.
@@ -686,13 +650,13 @@ module ModelOutput {
string getAWarning() {
// Check number of columns
exists(string row, string kind, int expectedArity, int actualArity |
any(SourceModelCsv csv).row(row) and kind = "source" and expectedArity = 4
any(SourceModelCsv csv).row(row) and kind = "source" and expectedArity = 3
or
any(SinkModelCsv csv).row(row) and kind = "sink" and expectedArity = 4
any(SinkModelCsv csv).row(row) and kind = "sink" and expectedArity = 3
or
any(SummaryModelCsv csv).row(row) and kind = "summary" and expectedArity = 6
any(SummaryModelCsv csv).row(row) and kind = "summary" and expectedArity = 5
or
any(TypeModelCsv csv).row(row) and kind = "type" and expectedArity = 5
any(TypeModelCsv csv).row(row) and kind = "type" and expectedArity = 3
or
any(TypeVariableModelCsv csv).row(row) and kind = "type-variable" and expectedArity = 2
|
@@ -705,7 +669,7 @@ module ModelOutput {
or
// Check names and arguments of access path tokens
exists(AccessPath path, AccessPathToken token |
(isRelevantFullPath(_, _, path) or typeVariableModel(_, path)) and
(isRelevantFullPath(_, path) or typeVariableModel(_, path)) and
token = path.getToken(_)
|
not isValidTokenNameInIdentifyingAccessPath(token.getName()) and

View File

@@ -31,19 +31,22 @@ import semmle.python.dataflow.new.DataFlow::DataFlow as DataFlow
private import AccessPathSyntax
/**
* Holds if models describing `package` may be relevant for the analysis of this database.
* Holds if models describing `type` may be relevant for the analysis of this database.
*/
predicate isPackageUsed(string package) { API::moduleImportExists(package) }
predicate isTypeUsed(string type) { API::moduleImportExists(type) }
/** Gets a Python-specific interpretation of the `(package, type, path)` tuple after resolving the first `n` access path tokens. */
bindingset[package, type, path]
API::Node getExtraNodeFromPath(string package, string type, AccessPath path, int n) { none() }
/**
* Holds if `type` can be obtained from an instance of `otherType` due to
* language semantics modeled by `getExtraNodeFromType`.
*/
predicate hasImplicitTypeModel(string type, string otherType) { none() }
/** Gets a Python-specific interpretation of the `(package, type)` tuple. */
API::Node getExtraNodeFromType(string package, string type) {
type = "" and
result = API::moduleImport(package)
}
/** Gets a Python-specific interpretation of the `(type, path)` tuple after resolving the first `n` access path tokens. */
bindingset[type, path]
API::Node getExtraNodeFromPath(string type, AccessPath path, int n) { none() }
/** Gets a Python-specific interpretation of the given `type`. */
API::Node getExtraNodeFromType(string type) { result = API::moduleImport(type) }
/**
* Gets a Python-specific API graph successor of `node` reachable by resolving `token`.
@@ -121,9 +124,9 @@ predicate invocationMatchesExtraCallSiteFilter(API::CallNode invoke, AccessPathT
*/
pragma[nomagic]
private predicate relevantInputOutputPath(API::CallNode base, AccessPath inputOrOutput) {
exists(string package, string type, string input, string output, string path |
ModelOutput::relevantSummaryModel(package, type, path, input, output, _) and
ModelOutput::resolvedSummaryBase(package, type, path, base) and
exists(string type, string input, string output, string path |
ModelOutput::relevantSummaryModel(type, path, input, output, _) and
ModelOutput::resolvedSummaryBase(type, path, base) and
inputOrOutput = [input, output]
)
}
@@ -153,12 +156,9 @@ private API::Node getNodeFromInputOutputPath(API::CallNode baseNode, AccessPath
* Holds if a CSV summary contributed the step `pred -> succ` of the given `kind`.
*/
predicate summaryStep(API::Node pred, API::Node succ, string kind) {
exists(
string package, string type, string path, API::CallNode base, AccessPath input,
AccessPath output
|
ModelOutput::relevantSummaryModel(package, type, path, input, output, kind) and
ModelOutput::resolvedSummaryBase(package, type, path, base) and
exists(string type, string path, API::CallNode base, AccessPath input, AccessPath output |
ModelOutput::relevantSummaryModel(type, path, input, output, kind) and
ModelOutput::resolvedSummaryBase(type, path, base) and
pred = getNodeFromInputOutputPath(base, input) and
succ = getNodeFromInputOutputPath(base, output)
)
@@ -201,3 +201,5 @@ predicate isExtraValidTokenArgumentInIdentifyingAccessPath(string name, string a
argument.regexpMatch("\\w+:") // keyword argument
)
}
module ModelOutputSpecific { }

View File

@@ -0,0 +1,21 @@
/**
* INTERNAL: Do not use.
*
* Provides predicates for recommended encryption key sizes.
* Such that we can share this logic across our CodeQL analysis of different languages.
*/
/** Returns the minimum recommended key size for RSA. */
int minSecureKeySizeRsa() { result = 2048 }
/** Returns the minimum recommended key size for DSA. */
int minSecureKeySizeDsa() { result = 2048 }
/** Returns the minimum recommended key size for DH. */
int minSecureKeySizeDh() { result = 2048 }
/** Returns the minimum recommended key size for elliptic curve cryptography. */
int minSecureKeySizeEcc() { result = 256 }
/** Returns the minimum recommended key size for AES. */
int minSecureKeySizeAes() { result = 128 }

View File

@@ -13,17 +13,15 @@
*/
import python
import semmle.python.dataflow.new.DataFlow
import semmle.python.ApiGraphs
from CallNode call_to_super, string name
from DataFlow::CallCfgNode call_to_super, string name
where
exists(GlobalVariable gv, ControlFlowNode cn |
call_to_super = ClassValue::super_().getACall() and
gv.getId() = "super" and
cn = call_to_super.getArg(0) and
name = call_to_super.getScope().getScope().(Class).getName() and
exists(ClassValue other |
cn.pointsTo(other) and
not other.getScope().getName() = name
)
call_to_super = API::builtin("super").getACall() and
name = call_to_super.getScope().getScope().(Class).getName() and
exists(DataFlow::Node arg |
arg = call_to_super.getArg(0) and
arg.getALocalSource().asExpr().(Name).getId() != name
)
select call_to_super.getNode(), "First argument to super() should be " + name + "."

View File

@@ -26,9 +26,10 @@ However, it is worth investigating why a module containing a syntax error
was able to persist and address that problem as well.
</p>
<p>If you suspect that the syntax error is caused by the analysis using the
wrong version of Python, consider specifying the version explicitly. For
LGTM.com, you can customize extraction using an <code>lgtm.yml</code> file as
described <a href="https://lgtm.com/help/lgtm/python-extraction">here</a>.
wrong version of Python, consider specifying the version explicitly. When
you run code scanning using the CodeQL action, you can configure the Python
version to use. For more information, see
<a href="https://docs.github.com/en/code-security/code-scanning/automatically-scanning-your-code-for-vulnerabilities-and-errors/configuring-code-scanning#analyzing-python-dependencies">Analyzing Python dependencies</a>.
</p>
</recommendation>
<references>

View File

@@ -11,13 +11,13 @@ As computational power increases, the ability to break ciphers grows and keys ne
<p>
The three main asymmetric key algorithms currently in use are RivestShamirAdleman (RSA) cryptography, Digital Signature Algorithm (DSA), and Elliptic-curve cryptography (ECC).
With current technology, key sizes of 2048 bits for RSA and DSA,
or 224 bits for ECC, are regarded as unbreakable.
or 256 bits for ECC, are regarded as unbreakable.
</p>
</overview>
<recommendation>
<p>
Increase the key size to the recommended amount or larger. For RSA or DSA this is at least 2048 bits, for ECC this is at least 224 bits.
Increase the key size to the recommended amount or larger. For RSA or DSA this is at least 2048 bits, for ECC this is at least 256 bits.
</p>
</recommendation>
@@ -45,4 +45,3 @@ Recommendation for Transitioning the Use of Cryptographic Algorithms and Key Len
</li>
</references>
</qhelp>

View File

@@ -0,0 +1,4 @@
---
category: minorAnalysis
---
* Added modeling of `getpass.getpass` as a source of passwords, which will be an additional source for `py/clear-text-logging-sensitive-data`, `py/clear-text-storage-sensitive-data`, and `py/weak-sensitive-data-hashing`.

View File

@@ -0,0 +1,4 @@
---
category: minorAnalysis
---
* Bumped the minimum keysize we consider secure for elliptic curve cryptography from 224 to 256 bits, following current best practices. This might effect results from the _Use of weak cryptographic key_ (`py/weak-crypto-key`) query.

View File

@@ -0,0 +1,87 @@
/**
* @name Taint sinks
* @description Sinks from TaintTracking queries.
* @kind problem
* @problem.severity recommendation
* @id py/meta/alerts/taint-sinks
* @tags meta
* @precision very-low
*/
private import python
private import semmle.python.dataflow.new.DataFlow
private import meta.MetaMetrics
import semmle.python.security.dataflow.CleartextLoggingCustomizations
import semmle.python.security.dataflow.CleartextStorageCustomizations
import semmle.python.security.dataflow.CodeInjectionCustomizations
import semmle.python.security.dataflow.CommandInjectionCustomizations
import semmle.python.security.dataflow.LdapInjectionCustomizations
import semmle.python.security.dataflow.LogInjectionCustomizations
import semmle.python.security.dataflow.PathInjectionCustomizations
import semmle.python.security.dataflow.PolynomialReDoSCustomizations
import semmle.python.security.dataflow.ReflectedXSSCustomizations
import semmle.python.security.dataflow.RegexInjectionCustomizations
import semmle.python.security.dataflow.ServerSideRequestForgeryCustomizations
import semmle.python.security.dataflow.SqlInjectionCustomizations
import semmle.python.security.dataflow.StackTraceExposureCustomizations
import semmle.python.security.dataflow.TarSlipCustomizations
import semmle.python.security.dataflow.UnsafeDeserializationCustomizations
import semmle.python.security.dataflow.UrlRedirectCustomizations
import semmle.python.security.dataflow.WeakSensitiveDataHashingCustomizations
import semmle.python.security.dataflow.XmlBombCustomizations
import semmle.python.security.dataflow.XpathInjectionCustomizations
import semmle.python.security.dataflow.XxeCustomizations
DataFlow::Node relevantTaintSink(string kind) {
not result.getLocation().getFile() instanceof IgnoredFile and
(
kind = "CleartextLogging" and result instanceof CleartextLogging::Sink
or
kind = "CleartextStorage" and result instanceof CleartextStorage::Sink
or
kind = "CodeInjection" and result instanceof CodeInjection::Sink
or
kind = "CommandInjection" and result instanceof CommandInjection::Sink
or
kind = "LdapInjection (DN)" and result instanceof LdapInjection::DnSink
or
kind = "LdapInjection (Filter)" and result instanceof LdapInjection::FilterSink
or
kind = "LogInjection" and result instanceof LogInjection::Sink
or
kind = "PathInjection" and result instanceof PathInjection::Sink
or
kind = "PolynomialReDoS" and result instanceof PolynomialReDoS::Sink
or
kind = "ReflectedXss" and result instanceof ReflectedXss::Sink
or
kind = "RegexInjection" and result instanceof RegexInjection::Sink
or
kind = "ServerSideRequestForgery" and result instanceof ServerSideRequestForgery::Sink
or
kind = "SqlInjection" and result instanceof SqlInjection::Sink
or
kind = "StackTraceExposure" and result instanceof StackTraceExposure::Sink
or
kind = "TarSlip" and result instanceof TarSlip::Sink
or
kind = "UnsafeDeserialization" and result instanceof UnsafeDeserialization::Sink
or
kind = "UrlRedirect" and result instanceof UrlRedirect::Sink
or
kind = "WeakSensitiveDataHashing (NormalHashFunction)" and
result instanceof NormalHashFunction::Sink
or
kind = "WeakSensitiveDataHashing (ComputationallyExpensiveHashFunction)" and
result instanceof ComputationallyExpensiveHashFunction::Sink
or
kind = "XmlBomb" and result instanceof XmlBomb::Sink
or
kind = "XpathInjection" and result instanceof XpathInjection::Sink
or
kind = "Xxe" and result instanceof Xxe::Sink
)
}
from string kind
select relevantTaintSink(kind), kind + " sink"

View File

@@ -10,7 +10,11 @@
import python
import semmle.python.dataflow.new.internal.DataFlowPrivate
import meta.MetaMetrics
from DataFlowCall call, DataFlowCallable target
where target = viableCallable(call)
where
target = viableCallable(call) and
not call.getLocation().getFile() instanceof IgnoredFile and
not target.getScope().getLocation().getFile() instanceof IgnoredFile
select call, "Call to $@", target.getScope(), target.toString()

View File

@@ -19,3 +19,5 @@ reverseRead
argHasPostUpdate
postWithInFlow
viableImplInCallContextTooLarge
uniqueParameterNodeAtPosition
uniqueParameterNodePosition

View File

@@ -19,3 +19,5 @@ reverseRead
argHasPostUpdate
postWithInFlow
viableImplInCallContextTooLarge
uniqueParameterNodeAtPosition
uniqueParameterNodePosition

View File

@@ -19,3 +19,5 @@ reverseRead
argHasPostUpdate
postWithInFlow
viableImplInCallContextTooLarge
uniqueParameterNodeAtPosition
uniqueParameterNodePosition

View File

@@ -19,3 +19,5 @@ reverseRead
argHasPostUpdate
postWithInFlow
viableImplInCallContextTooLarge
uniqueParameterNodeAtPosition
uniqueParameterNodePosition

View File

@@ -19,3 +19,5 @@ reverseRead
argHasPostUpdate
postWithInFlow
viableImplInCallContextTooLarge
uniqueParameterNodeAtPosition
uniqueParameterNodePosition

View File

@@ -19,3 +19,5 @@ reverseRead
argHasPostUpdate
postWithInFlow
viableImplInCallContextTooLarge
uniqueParameterNodeAtPosition
uniqueParameterNodePosition

View File

@@ -19,3 +19,5 @@ reverseRead
argHasPostUpdate
postWithInFlow
viableImplInCallContextTooLarge
uniqueParameterNodeAtPosition
uniqueParameterNodePosition

View File

@@ -19,3 +19,5 @@ reverseRead
argHasPostUpdate
postWithInFlow
viableImplInCallContextTooLarge
uniqueParameterNodeAtPosition
uniqueParameterNodePosition

View File

@@ -19,3 +19,5 @@ reverseRead
argHasPostUpdate
postWithInFlow
viableImplInCallContextTooLarge
uniqueParameterNodeAtPosition
uniqueParameterNodePosition

View File

@@ -19,3 +19,5 @@ reverseRead
argHasPostUpdate
postWithInFlow
viableImplInCallContextTooLarge
uniqueParameterNodeAtPosition
uniqueParameterNodePosition

View File

@@ -19,3 +19,5 @@ reverseRead
argHasPostUpdate
postWithInFlow
viableImplInCallContextTooLarge
uniqueParameterNodeAtPosition
uniqueParameterNodePosition

View File

@@ -19,3 +19,5 @@ reverseRead
argHasPostUpdate
postWithInFlow
viableImplInCallContextTooLarge
uniqueParameterNodeAtPosition
uniqueParameterNodePosition

View File

@@ -19,3 +19,5 @@ reverseRead
argHasPostUpdate
postWithInFlow
viableImplInCallContextTooLarge
uniqueParameterNodeAtPosition
uniqueParameterNodePosition

View File

@@ -19,3 +19,5 @@ reverseRead
argHasPostUpdate
postWithInFlow
viableImplInCallContextTooLarge
uniqueParameterNodeAtPosition
uniqueParameterNodePosition

View File

@@ -19,3 +19,5 @@ reverseRead
argHasPostUpdate
postWithInFlow
viableImplInCallContextTooLarge
uniqueParameterNodeAtPosition
uniqueParameterNodePosition

View File

@@ -19,3 +19,5 @@ reverseRead
argHasPostUpdate
postWithInFlow
viableImplInCallContextTooLarge
uniqueParameterNodeAtPosition
uniqueParameterNodePosition

View File

@@ -19,3 +19,5 @@ reverseRead
argHasPostUpdate
postWithInFlow
viableImplInCallContextTooLarge
uniqueParameterNodeAtPosition
uniqueParameterNodePosition

View File

@@ -19,3 +19,5 @@ reverseRead
argHasPostUpdate
postWithInFlow
viableImplInCallContextTooLarge
uniqueParameterNodeAtPosition
uniqueParameterNodePosition

View File

@@ -93,26 +93,29 @@ class ResolutionTest extends InlineExpectationsTest {
}
}
class ResolutionTest3 extends InlineExpectationsTest {
ResolutionTest3() { this = "ResolutionTest3" }
private string getTagForVersion(int version) {
result = "prints" + version and
version = major_version()
}
override string getARelevantTag() { result = "prints3" and major_version() = 3 }
class VersionSpecificResolutionTest extends InlineExpectationsTest {
VersionSpecificResolutionTest() { this = "VersionSpecificResolutionTest" }
override string getARelevantTag() { result = getTagForVersion(_) }
override predicate hasActualResult(Location location, string element, string tag, string value) {
(
exists(DataFlow::PathNode source, DataFlow::PathNode sink, ImportConfiguration config |
config.hasFlowPath(source, sink) and
sink.getNode().(VersionGuardedNode).getVersion() = 3 and
tag = "prints3" and
tag = getTagForVersion(sink.getNode().(VersionGuardedNode).getVersion()) and
location = sink.getNode().getLocation() and
value = source.getNode().(SourceString).getContents() and
element = sink.getNode().toString()
)
or
exists(ModuleRef ref |
ref.(VersionGuardedNode).getVersion() = 3 and
ref instanceof CheckArgument and
tag = "prints3" and
tag = getTagForVersion(ref.(VersionGuardedNode).getVersion()) and
location = ref.getLocation() and
value = "\"<module " + ref.getName() + ">\"" and
element = ref.toString()

View File

@@ -19,3 +19,5 @@ reverseRead
argHasPostUpdate
postWithInFlow
viableImplInCallContextTooLarge
uniqueParameterNodeAtPosition
uniqueParameterNodePosition

View File

@@ -7,78 +7,78 @@ private import semmle.python.ApiGraphs
class Steps extends ModelInput::SummaryModelCsv {
override predicate row(string row) {
// package;type;path;input;output;kind
// type;path;input;output;kind
row =
[
"testlib;;Member[Steps].Member[preserveTaint].Call;Argument[0];ReturnValue;taint",
"testlib;;Member[Steps].Member[taintIntoCallback];Argument[0];Argument[1..2].Parameter[0];taint",
"testlib;;Member[Steps].Member[preserveArgZeroAndTwo];Argument[0,2];ReturnValue;taint",
"testlib;;Member[Steps].Member[preserveAllButFirstArgument].Call;Argument[1..];ReturnValue;taint",
"testlib;Member[Steps].Member[preserveTaint].Call;Argument[0];ReturnValue;taint",
"testlib;Member[Steps].Member[taintIntoCallback];Argument[0];Argument[1..2].Parameter[0];taint",
"testlib;Member[Steps].Member[preserveArgZeroAndTwo];Argument[0,2];ReturnValue;taint",
"testlib;Member[Steps].Member[preserveAllButFirstArgument].Call;Argument[1..];ReturnValue;taint",
]
}
}
class Types extends ModelInput::TypeModelCsv {
override predicate row(string row) {
// package1;type1;package2;type2;path
// type1;type2;path
row =
[
"testlib;Alias;testlib;;Member[alias].ReturnValue",
"testlib;Alias;testlib;Alias;Member[chain].ReturnValue",
"testlib.Alias;testlib;Member[alias].ReturnValue",
"testlib.Alias;testlib.Alias;Member[chain].ReturnValue",
]
}
}
class Sinks extends ModelInput::SinkModelCsv {
override predicate row(string row) {
// package;type;path;kind
// type;path;kind
row =
[
"testlib;;Member[mySink].Argument[0,sinkName:];test-sink",
"testlib;Member[mySink].Argument[0,sinkName:];test-sink",
// testing argument syntax
"testlib;;Member[Args].Member[arg0].Argument[0];test-sink", //
"testlib;;Member[Args].Member[arg1to3].Argument[1..3];test-sink", //
"testlib;;Member[Args].Member[lastarg].Argument[N-1];test-sink", //
"testlib;;Member[Args].Member[nonFist].Argument[1..];test-sink", //
"testlib;Member[Args].Member[arg0].Argument[0];test-sink", //
"testlib;Member[Args].Member[arg1to3].Argument[1..3];test-sink", //
"testlib;Member[Args].Member[lastarg].Argument[N-1];test-sink", //
"testlib;Member[Args].Member[nonFist].Argument[1..];test-sink", //
// callsite filter.
"testlib;;Member[CallFilter].Member[arityOne].WithArity[1].Argument[any];test-sink", //
"testlib;;Member[CallFilter].Member[twoOrMore].WithArity[2..].Argument[0..];test-sink", //
"testlib;Member[CallFilter].Member[arityOne].WithArity[1].Argument[any];test-sink", //
"testlib;Member[CallFilter].Member[twoOrMore].WithArity[2..].Argument[0..];test-sink", //
// testing non-positional arguments
"testlib;;Member[ArgPos].Instance.Member[self_thing].Argument[self];test-sink", //
"testlib;Member[ArgPos].Instance.Member[self_thing].Argument[self];test-sink", //
// any argument
"testlib;;Member[ArgPos].Member[anyParam].Argument[any];test-sink", //
"testlib;;Member[ArgPos].Member[anyNamed].Argument[any-named];test-sink", //
"testlib;Member[ArgPos].Member[anyParam].Argument[any];test-sink", //
"testlib;Member[ArgPos].Member[anyNamed].Argument[any-named];test-sink", //
// testing package syntax
"foo1.bar;;Member[baz1].Argument[any];test-sink", //
"foo2;;Member[bar].Member[baz2].Argument[any];test-sink", //
"foo1.bar;Member[baz1].Argument[any];test-sink", //
"foo2;Member[bar].Member[baz2].Argument[any];test-sink", //
]
}
}
class Sources extends ModelInput::SourceModelCsv {
// package;type;path;kind
// type;path;kind
override predicate row(string row) {
row =
[
"testlib;;Member[getSource].ReturnValue;test-source", //
"testlib;Alias;;test-source",
"testlib;Member[getSource].ReturnValue;test-source", //
"testlib.Alias;;test-source",
// testing parameter syntax
"testlib;;Member[Callbacks].Member[first].Argument[0].Parameter[0];test-source", //
"testlib;;Member[Callbacks].Member[param1to3].Argument[0].Parameter[1..3];test-source", //
"testlib;;Member[Callbacks].Member[nonFirst].Argument[0].Parameter[1..];test-source", //
"testlib;Member[Callbacks].Member[first].Argument[0].Parameter[0];test-source", //
"testlib;Member[Callbacks].Member[param1to3].Argument[0].Parameter[1..3];test-source", //
"testlib;Member[Callbacks].Member[nonFirst].Argument[0].Parameter[1..];test-source", //
// Common tokens.
"testlib;;Member[CommonTokens].Member[makePromise].ReturnValue.Awaited;test-source", //
"testlib;;Member[CommonTokens].Member[Class].Instance;test-source", //
"testlib;;Member[CommonTokens].Member[Super].Subclass.Instance;test-source", //
"testlib;Member[CommonTokens].Member[makePromise].ReturnValue.Awaited;test-source", //
"testlib;Member[CommonTokens].Member[Class].Instance;test-source", //
"testlib;Member[CommonTokens].Member[Super].Subclass.Instance;test-source", //
// method
"testlib;;Member[CommonTokens].Member[Class].Instance.Method[foo];test-source", //
"testlib;Member[CommonTokens].Member[Class].Instance.Method[foo];test-source", //
// testing non-positional arguments
"testlib;;Member[ArgPos].Member[MyClass].Subclass.Member[foo].Parameter[self];test-source", //
"testlib;;Member[ArgPos].Member[MyClass].Subclass.Member[foo].Parameter[named:];test-source", //
"testlib;;Member[ArgPos].Member[MyClass].Subclass.Member[secondAndAfter].Parameter[1..];test-source", //
"testlib;;Member[ArgPos].Member[MyClass].Subclass.Member[otherSelfTest].Parameter[0];test-source", //
"testlib;;Member[ArgPos].Member[MyClass].Subclass.Member[anyParam].Parameter[any];test-source", //
"testlib;;Member[ArgPos].Member[MyClass].Subclass.Member[anyNamed].Parameter[any-named];test-source", //
"testlib;Member[ArgPos].Member[MyClass].Subclass.Member[foo].Parameter[self];test-source", //
"testlib;Member[ArgPos].Member[MyClass].Subclass.Member[foo].Parameter[named:];test-source", //
"testlib;Member[ArgPos].Member[MyClass].Subclass.Member[secondAndAfter].Parameter[1..];test-source", //
"testlib;Member[ArgPos].Member[MyClass].Subclass.Member[otherSelfTest].Parameter[0];test-source", //
"testlib;Member[ArgPos].Member[MyClass].Subclass.Member[anyParam].Parameter[any];test-source", //
"testlib;Member[ArgPos].Member[MyClass].Subclass.Member[anyNamed].Parameter[any-named];test-source", //
]
}
}
@@ -111,13 +111,16 @@ class SyntaxErrorTest extends ModelInput::SinkModelCsv {
override predicate row(string row) {
row =
[
"testlib;;Member[foo],Member[bar];test-sink", "testlib;;Member[foo] Member[bar];test-sink",
"testlib;;Member[foo]. Member[bar];test-sink",
"testlib;;Member[foo], Member[bar];test-sink",
"testlib;;Member[foo]..Member[bar];test-sink",
"testlib;;Member[foo] .Member[bar];test-sink", "testlib;;Member[foo]Member[bar];test-sink",
"testlib;;Member[foo;test-sink", "testlib;;Member[foo]];test-sink",
"testlib;;Member[foo]].Member[bar];test-sink"
"testlib;Member[foo],Member[bar];test-sink", //
"testlib;Member[foo] Member[bar];test-sink", //
"testlib;Member[foo]. Member[bar];test-sink", //
"testlib;Member[foo], Member[bar];test-sink", //
"testlib;Member[foo]..Member[bar];test-sink", //
"testlib;Member[foo] .Member[bar];test-sink", //
"testlib;Member[foo]Member[bar];test-sink", //
"testlib;Member[foo;test-sink", //
"testlib;Member[foo]];test-sink", //
"testlib;Member[foo]].Member[bar];test-sink", //
]
}
}

View File

@@ -1,5 +1,5 @@
| CSV type row should have 5 columns but has 2: test;TooFewColumns |
| CSV type row should have 5 columns but has 8: test;TooManyColumns;;;Member[Foo].Instance;too;many;columns |
| CSV type row should have 3 columns but has 1: test.TooFewColumns |
| CSV type row should have 3 columns but has 6: test.TooManyColumns;;Member[Foo].Instance;too;many;columns |
| Invalid argument '0-1' in token 'Argument[0-1]' in access path: Method[foo].Argument[0-1] |
| Invalid argument '*' in token 'Argument[*]' in access path: Method[foo].Argument[*] |
| Invalid token 'Argument' is missing its arguments, in access path: Method[foo].Argument |

View File

@@ -7,13 +7,13 @@ private class InvalidTypeModel extends ModelInput::TypeModelCsv {
override predicate row(string row) {
row =
[
"test;TooManyColumns;;;Member[Foo].Instance;too;many;columns", //
"test;TooFewColumns", //
"test;X;test;Y;Method[foo].Arg[0]", //
"test;X;test;Y;Method[foo].Argument[0-1]", //
"test;X;test;Y;Method[foo].Argument[*]", //
"test;X;test;Y;Method[foo].Argument", //
"test;X;test;Y;Method[foo].Member", //
"test.TooManyColumns;;Member[Foo].Instance;too;many;columns", //
"test.TooFewColumns", //
"test.X;test.Y;Method[foo].Arg[0]", //
"test.X;test.Y;Method[foo].Argument[0-1]", //
"test.X;test.Y;Method[foo].Argument[*]", //
"test.X;test.Y;Method[foo].Argument", //
"test.X;test.Y;Method[foo].Member", //
]
}
}

View File

@@ -67,3 +67,5 @@ reverseRead
argHasPostUpdate
postWithInFlow
viableImplInCallContextTooLarge
uniqueParameterNodeAtPosition
uniqueParameterNodePosition

View File

@@ -1 +1 @@
| test.py:10:9:10:27 | super() | First argument to super() should be NotMyDict. |
| test.py:10:9:10:27 | ControlFlowNode for super() | First argument to super() should be NotMyDict. |

View File

@@ -4,8 +4,9 @@ edges
| test.py:19:16:19:29 | ControlFlowNode for get_password() | test.py:23:58:23:65 | ControlFlowNode for password |
| test.py:19:16:19:29 | ControlFlowNode for get_password() | test.py:27:40:27:47 | ControlFlowNode for password |
| test.py:19:16:19:29 | ControlFlowNode for get_password() | test.py:30:58:30:65 | ControlFlowNode for password |
| test.py:65:14:68:5 | ControlFlowNode for Dict | test.py:69:11:69:31 | ControlFlowNode for Subscript |
| test.py:67:21:67:37 | ControlFlowNode for Attribute | test.py:65:14:68:5 | ControlFlowNode for Dict |
| test.py:44:9:44:25 | ControlFlowNode for Attribute() | test.py:45:11:45:11 | ControlFlowNode for x |
| test.py:70:14:73:5 | ControlFlowNode for Dict | test.py:74:11:74:31 | ControlFlowNode for Subscript |
| test.py:72:21:72:37 | ControlFlowNode for Attribute | test.py:70:14:73:5 | ControlFlowNode for Dict |
nodes
| test.py:19:16:19:29 | ControlFlowNode for get_password() | semmle.label | ControlFlowNode for get_password() |
| test.py:20:48:20:55 | ControlFlowNode for password | semmle.label | ControlFlowNode for password |
@@ -17,9 +18,11 @@ nodes
| test.py:37:11:37:24 | ControlFlowNode for get_password() | semmle.label | ControlFlowNode for get_password() |
| test.py:39:22:39:35 | ControlFlowNode for get_password() | semmle.label | ControlFlowNode for get_password() |
| test.py:40:22:40:35 | ControlFlowNode for get_password() | semmle.label | ControlFlowNode for get_password() |
| test.py:65:14:68:5 | ControlFlowNode for Dict | semmle.label | ControlFlowNode for Dict |
| test.py:67:21:67:37 | ControlFlowNode for Attribute | semmle.label | ControlFlowNode for Attribute |
| test.py:69:11:69:31 | ControlFlowNode for Subscript | semmle.label | ControlFlowNode for Subscript |
| test.py:44:9:44:25 | ControlFlowNode for Attribute() | semmle.label | ControlFlowNode for Attribute() |
| test.py:45:11:45:11 | ControlFlowNode for x | semmle.label | ControlFlowNode for x |
| test.py:70:14:73:5 | ControlFlowNode for Dict | semmle.label | ControlFlowNode for Dict |
| test.py:72:21:72:37 | ControlFlowNode for Attribute | semmle.label | ControlFlowNode for Attribute |
| test.py:74:11:74:31 | ControlFlowNode for Subscript | semmle.label | ControlFlowNode for Subscript |
subpaths
#select
| test.py:20:48:20:55 | ControlFlowNode for password | test.py:19:16:19:29 | ControlFlowNode for get_password() | test.py:20:48:20:55 | ControlFlowNode for password | This expression logs $@ as clear text. | test.py:19:16:19:29 | ControlFlowNode for get_password() | sensitive data (password) |
@@ -31,4 +34,5 @@ subpaths
| test.py:37:11:37:24 | ControlFlowNode for get_password() | test.py:37:11:37:24 | ControlFlowNode for get_password() | test.py:37:11:37:24 | ControlFlowNode for get_password() | This expression logs $@ as clear text. | test.py:37:11:37:24 | ControlFlowNode for get_password() | sensitive data (password) |
| test.py:39:22:39:35 | ControlFlowNode for get_password() | test.py:39:22:39:35 | ControlFlowNode for get_password() | test.py:39:22:39:35 | ControlFlowNode for get_password() | This expression logs $@ as clear text. | test.py:39:22:39:35 | ControlFlowNode for get_password() | sensitive data (password) |
| test.py:40:22:40:35 | ControlFlowNode for get_password() | test.py:40:22:40:35 | ControlFlowNode for get_password() | test.py:40:22:40:35 | ControlFlowNode for get_password() | This expression logs $@ as clear text. | test.py:40:22:40:35 | ControlFlowNode for get_password() | sensitive data (password) |
| test.py:69:11:69:31 | ControlFlowNode for Subscript | test.py:67:21:67:37 | ControlFlowNode for Attribute | test.py:69:11:69:31 | ControlFlowNode for Subscript | This expression logs $@ as clear text. | test.py:67:21:67:37 | ControlFlowNode for Attribute | sensitive data (password) |
| test.py:45:11:45:11 | ControlFlowNode for x | test.py:44:9:44:25 | ControlFlowNode for Attribute() | test.py:45:11:45:11 | ControlFlowNode for x | This expression logs $@ as clear text. | test.py:44:9:44:25 | ControlFlowNode for Attribute() | sensitive data (password) |
| test.py:74:11:74:31 | ControlFlowNode for Subscript | test.py:72:21:72:37 | ControlFlowNode for Attribute | test.py:74:11:74:31 | ControlFlowNode for Subscript | This expression logs $@ as clear text. | test.py:72:21:72:37 | ControlFlowNode for Attribute | sensitive data (password) |

View File

@@ -39,6 +39,11 @@ def print_password():
sys.stdout.write(get_password()) # NOT OK
sys.stderr.write(get_password()) # NOT OK
import getpass
x = getpass.getpass()
print(x) # NOT OK
def FPs(account, account_id):
# we assume that any account parameter is sensitive (id/username)

View File

@@ -1,8 +1,8 @@
| weak_crypto.py:68:1:68:21 | ControlFlowNode for dsa_gen_key() | Creation of an DSA key uses $@ bits, which is below 2048 and considered breakable. | weak_crypto.py:16:12:16:15 | ControlFlowNode for IntegerLiteral | 1024 |
| weak_crypto.py:69:1:69:19 | ControlFlowNode for ec_gen_key() | Creation of an ECC key uses $@ bits, which is below 224 and considered breakable. | weak_crypto.py:22:11:22:22 | ControlFlowNode for Attribute | 163 |
| weak_crypto.py:69:1:69:19 | ControlFlowNode for ec_gen_key() | Creation of an ECC key uses $@ bits, which is below 256 and considered breakable. | weak_crypto.py:22:11:22:22 | ControlFlowNode for Attribute | 224 |
| weak_crypto.py:70:1:70:28 | ControlFlowNode for rsa_gen_key() | Creation of an RSA key uses $@ bits, which is below 2048 and considered breakable. | weak_crypto.py:12:12:12:15 | ControlFlowNode for IntegerLiteral | 1024 |
| weak_crypto.py:72:1:72:30 | ControlFlowNode for dsa_gen_key() | Creation of an DSA key uses $@ bits, which is below 2048 and considered breakable. | weak_crypto.py:16:12:16:15 | ControlFlowNode for IntegerLiteral | 1024 |
| weak_crypto.py:73:1:73:25 | ControlFlowNode for ec_gen_key() | Creation of an ECC key uses $@ bits, which is below 224 and considered breakable. | weak_crypto.py:22:11:22:22 | ControlFlowNode for Attribute | 163 |
| weak_crypto.py:73:1:73:25 | ControlFlowNode for ec_gen_key() | Creation of an ECC key uses $@ bits, which is below 256 and considered breakable. | weak_crypto.py:22:11:22:22 | ControlFlowNode for Attribute | 224 |
| weak_crypto.py:74:1:74:37 | ControlFlowNode for rsa_gen_key() | Creation of an RSA key uses $@ bits, which is below 2048 and considered breakable. | weak_crypto.py:12:12:12:15 | ControlFlowNode for IntegerLiteral | 1024 |
| weak_crypto.py:76:1:76:22 | ControlFlowNode for Attribute() | Creation of an DSA key uses $@ bits, which is below 2048 and considered breakable. | weak_crypto.py:16:12:16:15 | ControlFlowNode for IntegerLiteral | 1024 |
| weak_crypto.py:77:1:77:22 | ControlFlowNode for Attribute() | Creation of an RSA key uses $@ bits, which is below 2048 and considered breakable. | weak_crypto.py:12:12:12:15 | ControlFlowNode for IntegerLiteral | 1024 |

View File

@@ -19,8 +19,8 @@ DSA_STRONG = 3076
BIG = 10000
EC_WEAK = ec.SECT163K1() # has key size of 163
EC_OK = ec.SECP224R1()
EC_WEAK = ec.SECP224R1()
EC_OK = ec.SECP256R1()
EC_STRONG = ec.SECP384R1()
EC_BIG = ec.SECT571R1()