mirror of
https://github.com/github/codeql.git
synced 2025-12-16 16:53:25 +01:00
Merge pull request #15599 from aschackmull/dataflow/fieldflowbranchlimit-v2
Dataflow: update fieldFlowBranchLimit semantics
This commit is contained in:
@@ -290,6 +290,8 @@ predicate knownSourceModel(Node source, string model) { none() }
|
||||
|
||||
predicate knownSinkModel(Node sink, string model) { none() }
|
||||
|
||||
class DataFlowSecondLevelScope = Unit;
|
||||
|
||||
/**
|
||||
* Holds if flow is allowed to pass from parameter `p` and back to itself as a
|
||||
* side-effect, resulting in a summary from `p` to itself.
|
||||
|
||||
@@ -22,6 +22,8 @@ module CppDataFlow implements InputSig<Location> {
|
||||
|
||||
predicate getAdditionalFlowIntoCallNodeTerm = Private::getAdditionalFlowIntoCallNodeTerm/2;
|
||||
|
||||
predicate getSecondLevelScope = Private::getSecondLevelScope/1;
|
||||
|
||||
predicate validParameterAliasStep = Private::validParameterAliasStep/2;
|
||||
|
||||
predicate mayBenefitFromCallContext = Private::mayBenefitFromCallContext/1;
|
||||
|
||||
@@ -1583,3 +1583,74 @@ predicate validParameterAliasStep(Node node1, Node node2) {
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
private predicate isTopLevel(Cpp::Stmt s) { any(Function f).getBlock().getAStmt() = s }
|
||||
|
||||
private Cpp::Stmt getAChainedBranch(Cpp::IfStmt s) {
|
||||
result = s.getThen()
|
||||
or
|
||||
exists(Cpp::Stmt elseBranch | s.getElse() = elseBranch |
|
||||
result = getAChainedBranch(elseBranch)
|
||||
or
|
||||
result = elseBranch and not elseBranch instanceof Cpp::IfStmt
|
||||
)
|
||||
}
|
||||
|
||||
private Instruction getInstruction(Node n) {
|
||||
result = n.asInstruction() or
|
||||
result = n.asOperand().getUse() or
|
||||
result = n.(SsaPhiNode).getPhiNode().getBasicBlock().getFirstInstruction() or
|
||||
n.(IndirectInstruction).hasInstructionAndIndirectionIndex(result, _) or
|
||||
result = getInstruction(n.(PostUpdateNode).getPreUpdateNode())
|
||||
}
|
||||
|
||||
private newtype TDataFlowSecondLevelScope =
|
||||
TTopLevelIfBranch(Cpp::Stmt s) {
|
||||
exists(Cpp::IfStmt ifstmt | s = getAChainedBranch(ifstmt) and isTopLevel(ifstmt))
|
||||
} or
|
||||
TTopLevelSwitchCase(Cpp::SwitchCase s) {
|
||||
exists(Cpp::SwitchStmt switchstmt | s = switchstmt.getASwitchCase() and isTopLevel(switchstmt))
|
||||
}
|
||||
|
||||
/**
|
||||
* A second-level control-flow scope in a `switch` or a chained `if` statement.
|
||||
*
|
||||
* This is a `switch` case or a branch of a chained `if` statement, given that
|
||||
* the `switch` or `if` statement is top level, that is, it is not nested inside
|
||||
* other CFG constructs.
|
||||
*/
|
||||
class DataFlowSecondLevelScope extends TDataFlowSecondLevelScope {
|
||||
/** Gets a textual representation of this element. */
|
||||
string toString() {
|
||||
exists(Cpp::Stmt s | this = TTopLevelIfBranch(s) | result = s.toString())
|
||||
or
|
||||
exists(Cpp::SwitchCase s | this = TTopLevelSwitchCase(s) | result = s.toString())
|
||||
}
|
||||
|
||||
/** Gets the primary location of this element. */
|
||||
Cpp::Location getLocation() {
|
||||
exists(Cpp::Stmt s | this = TTopLevelIfBranch(s) | result = s.getLocation())
|
||||
or
|
||||
exists(Cpp::SwitchCase s | this = TTopLevelSwitchCase(s) | result = s.getLocation())
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a statement directly contained in this scope. For an `if` branch, this
|
||||
* is the branch itself, and for a `switch case`, this is one the statements
|
||||
* of that case branch.
|
||||
*/
|
||||
private Cpp::Stmt getAStmt() {
|
||||
exists(Cpp::Stmt s | this = TTopLevelIfBranch(s) | result = s)
|
||||
or
|
||||
exists(Cpp::SwitchCase s | this = TTopLevelSwitchCase(s) | result = s.getAStmt())
|
||||
}
|
||||
|
||||
/** Gets a data-flow node nested within this scope. */
|
||||
Node getANode() {
|
||||
getInstruction(result).getAst().(Cpp::ControlFlowNode).getEnclosingStmt().getParentStmt*() =
|
||||
this.getAStmt()
|
||||
}
|
||||
}
|
||||
|
||||
/** Gets the second-level scope containing the node `n`, if any. */
|
||||
DataFlowSecondLevelScope getSecondLevelScope(Node n) { result.getANode() = n }
|
||||
|
||||
@@ -84,7 +84,7 @@ void test_copy_assignment_operator()
|
||||
|
||||
swap(z1, z2);
|
||||
|
||||
sink(z2.data1); // $ ir MISSING: ast
|
||||
sink(z2.data1); // $ ir ast
|
||||
sink(z1.data1); // $ SPURIOUS: ir ast=81:27 ast=82:16
|
||||
}
|
||||
|
||||
|
||||
@@ -2882,6 +2882,8 @@ predicate knownSourceModel(Node source, string model) { sourceNode(source, _, mo
|
||||
|
||||
predicate knownSinkModel(Node sink, string model) { sinkNode(sink, _, model) }
|
||||
|
||||
class DataFlowSecondLevelScope = Unit;
|
||||
|
||||
/**
|
||||
* Holds if flow is allowed to pass from parameter `p` and back to itself as a
|
||||
* side-effect, resulting in a summary from `p` to itself.
|
||||
|
||||
@@ -14,8 +14,6 @@ module ArrayFlowConfig implements DataFlow::ConfigSig {
|
||||
mc.getAnArgument() = sink.asExpr()
|
||||
)
|
||||
}
|
||||
|
||||
int fieldFlowBranchLimit() { result = 100 }
|
||||
}
|
||||
|
||||
module ArrayFlow = DataFlow::Global<ArrayFlowConfig>;
|
||||
|
||||
@@ -18,8 +18,6 @@ module TypesConfig implements DataFlow::ConfigSig {
|
||||
mc.getAnArgument() = sink.asExpr()
|
||||
)
|
||||
}
|
||||
|
||||
int fieldFlowBranchLimit() { result = 1000 }
|
||||
}
|
||||
|
||||
import ValueFlowTest<TypesConfig>
|
||||
|
||||
@@ -415,6 +415,8 @@ predicate knownSourceModel(Node source, string model) { sourceNode(source, _, mo
|
||||
|
||||
predicate knownSinkModel(Node sink, string model) { sinkNode(sink, _, model) }
|
||||
|
||||
class DataFlowSecondLevelScope = Unit;
|
||||
|
||||
/**
|
||||
* Holds if flow is allowed to pass from parameter `p` and back to itself as a
|
||||
* side-effect, resulting in a summary from `p` to itself.
|
||||
|
||||
@@ -20,6 +20,8 @@ module JavaDataFlow implements InputSig<Location> {
|
||||
|
||||
Node exprNode(DataFlowExpr e) { result = Public::exprNode(e) }
|
||||
|
||||
predicate getSecondLevelScope = Private::getSecondLevelScope/1;
|
||||
|
||||
predicate mayBenefitFromCallContext = Private::mayBenefitFromCallContext/1;
|
||||
|
||||
predicate viableImplInCallContext = Private::viableImplInCallContext/2;
|
||||
|
||||
@@ -581,6 +581,81 @@ predicate knownSourceModel(Node source, string model) { sourceNode(source, _, mo
|
||||
|
||||
predicate knownSinkModel(Node sink, string model) { sinkNode(sink, _, model) }
|
||||
|
||||
private predicate isTopLevel(Stmt s) {
|
||||
any(Callable c).getBody() = s
|
||||
or
|
||||
exists(BlockStmt b | s = b.getAStmt() and isTopLevel(b))
|
||||
}
|
||||
|
||||
private Stmt getAChainedBranch(IfStmt s) {
|
||||
result = s.getThen()
|
||||
or
|
||||
exists(Stmt elseBranch | s.getElse() = elseBranch |
|
||||
result = getAChainedBranch(elseBranch)
|
||||
or
|
||||
result = elseBranch and not elseBranch instanceof IfStmt
|
||||
)
|
||||
}
|
||||
|
||||
private newtype TDataFlowSecondLevelScope =
|
||||
TTopLevelIfBranch(Stmt s) {
|
||||
exists(IfStmt ifstmt | s = getAChainedBranch(ifstmt) and isTopLevel(ifstmt))
|
||||
} or
|
||||
TTopLevelSwitchCase(SwitchCase s) {
|
||||
exists(SwitchStmt switchstmt | s = switchstmt.getACase() and isTopLevel(switchstmt))
|
||||
}
|
||||
|
||||
private SwitchCase getPrecedingCase(Stmt s) {
|
||||
result = s
|
||||
or
|
||||
exists(SwitchStmt switch, int i |
|
||||
s = switch.getStmt(i) and
|
||||
not s instanceof SwitchCase and
|
||||
result = getPrecedingCase(switch.getStmt(i - 1))
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* A second-level control-flow scope in a `switch` or a chained `if` statement.
|
||||
*
|
||||
* This is a `switch` case or a branch of a chained `if` statement, given that
|
||||
* the `switch` or `if` statement is top level, that is, it is not nested inside
|
||||
* other CFG constructs.
|
||||
*/
|
||||
class DataFlowSecondLevelScope extends TDataFlowSecondLevelScope {
|
||||
/** Gets a textual representation of this element. */
|
||||
string toString() {
|
||||
exists(Stmt s | this = TTopLevelIfBranch(s) | result = s.toString())
|
||||
or
|
||||
exists(SwitchCase s | this = TTopLevelSwitchCase(s) | result = s.toString())
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a statement directly contained in this scope. For an `if` branch, this
|
||||
* is the branch itself, and for a `switch case`, this is one the statements
|
||||
* of that case branch.
|
||||
*/
|
||||
private Stmt getAStmt() {
|
||||
exists(Stmt s | this = TTopLevelIfBranch(s) | result = s)
|
||||
or
|
||||
exists(SwitchCase s | this = TTopLevelSwitchCase(s) |
|
||||
result = s.getRuleStatement() or
|
||||
s = getPrecedingCase(result)
|
||||
)
|
||||
}
|
||||
|
||||
/** Gets a data-flow node nested within this scope. */
|
||||
Node getANode() { getRelatedExpr(result).getAnEnclosingStmt() = this.getAStmt() }
|
||||
}
|
||||
|
||||
private Expr getRelatedExpr(Node n) {
|
||||
n.asExpr() = result or
|
||||
n.(PostUpdateNode).getPreUpdateNode().asExpr() = result
|
||||
}
|
||||
|
||||
/** Gets the second-level scope containing the node `n`, if any. */
|
||||
DataFlowSecondLevelScope getSecondLevelScope(Node n) { result.getANode() = n }
|
||||
|
||||
/**
|
||||
* Holds if flow is allowed to pass from parameter `p` and back to itself as a
|
||||
* side-effect, resulting in a summary from `p` to itself.
|
||||
|
||||
@@ -18,8 +18,6 @@ module ValueFlowConfig implements DataFlow::ConfigSig {
|
||||
predicate isSink(DataFlow::Node n) {
|
||||
exists(MethodCall ma | ma.getMethod().hasName("sink") | n.asExpr() = ma.getAnArgument())
|
||||
}
|
||||
|
||||
int fieldFlowBranchLimit() { result = 100 }
|
||||
}
|
||||
|
||||
module ValueFlow = DataFlow::Global<ValueFlowConfig>;
|
||||
|
||||
@@ -1087,6 +1087,8 @@ predicate knownSinkModel(Node sink, string model) {
|
||||
sink = ModelOutput::getASinkNode(_, model).asSink()
|
||||
}
|
||||
|
||||
class DataFlowSecondLevelScope = Unit;
|
||||
|
||||
/**
|
||||
* Holds if flow is allowed to pass from parameter `p` and back to itself as a
|
||||
* side-effect, resulting in a summary from `p` to itself.
|
||||
|
||||
@@ -2254,6 +2254,8 @@ predicate knownSinkModel(Node sink, string model) {
|
||||
sink = ModelOutput::getASinkNode(_, model).asSink()
|
||||
}
|
||||
|
||||
class DataFlowSecondLevelScope = Unit;
|
||||
|
||||
/**
|
||||
* Holds if flow is allowed to pass from parameter `p` and back to itself as a
|
||||
* side-effect, resulting in a summary from `p` to itself.
|
||||
|
||||
@@ -0,0 +1,4 @@
|
||||
---
|
||||
category: majorAnalysis
|
||||
---
|
||||
* The data flow library performs heuristic filtering of code paths that have a high degree of control-flow uncertainty for improved performance in cases that are deemed unlikely to yield true positive flow paths. This filtering can be controlled with the `fieldFlowBranchLimit` predicate in configurations. Two bugs have been fixed in relation to this: Some cases of high uncertainty were not being correctly identified. This fix improves performance in certain scenarios. Another group of cases of low uncertainty were also being misidentified, which led to false negatives. Taken together, we generally expect some additional query results with more true positives and fewer false positives.
|
||||
@@ -308,6 +308,24 @@ signature module InputSig<LocationSig Location> {
|
||||
*/
|
||||
default int getAdditionalFlowIntoCallNodeTerm(ArgumentNode arg, ParameterNode p) { none() }
|
||||
|
||||
/**
|
||||
* A second-level control-flow scope in a callable.
|
||||
*
|
||||
* This is used to provide a more fine-grained separation of a callable
|
||||
* context for the purpose of identifying uncertain control flow. For most
|
||||
* languages, this is not needed, as this separation is handled through
|
||||
* virtual dispatch, but for some cases (for example, C++) this can be used to
|
||||
* identify, for example, large top-level switch statements acting like
|
||||
* virtual dispatch.
|
||||
*/
|
||||
class DataFlowSecondLevelScope {
|
||||
/** Gets a textual representation of this element. */
|
||||
string toString();
|
||||
}
|
||||
|
||||
/** Gets the second-level scope containing the node `n`, if any. */
|
||||
default DataFlowSecondLevelScope getSecondLevelScope(Node n) { none() }
|
||||
|
||||
bindingset[call, p, arg]
|
||||
default predicate golangSpecificParamArgFilter(
|
||||
DataFlowCall call, ParameterNode p, ArgumentNode arg
|
||||
|
||||
@@ -722,7 +722,7 @@ module MakeImpl<LocationSig Location, InputSig<Location> Lang> {
|
||||
* the enclosing callable in order to reach a sink.
|
||||
*/
|
||||
pragma[nomagic]
|
||||
private predicate revFlow(NodeEx node, boolean toReturn) {
|
||||
additional predicate revFlow(NodeEx node, boolean toReturn) {
|
||||
revFlow0(node, toReturn) and
|
||||
fwdFlow(node)
|
||||
}
|
||||
@@ -1113,16 +1113,74 @@ module MakeImpl<LocationSig Location, InputSig<Location> Lang> {
|
||||
result = getAdditionalFlowIntoCallNodeTerm(arg.projectToNode(), p.projectToNode())
|
||||
}
|
||||
|
||||
private module SndLevelScopeOption = Option<DataFlowSecondLevelScope>;
|
||||
|
||||
private class SndLevelScopeOption = SndLevelScopeOption::Option;
|
||||
|
||||
pragma[nomagic]
|
||||
private SndLevelScopeOption getScope(RetNodeEx ret) {
|
||||
result = SndLevelScopeOption::some(getSecondLevelScope(ret.asNode()))
|
||||
or
|
||||
result instanceof SndLevelScopeOption::None and not exists(getSecondLevelScope(ret.asNode()))
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate returnCallEdge1(
|
||||
DataFlowCallable c, SndLevelScopeOption scope, DataFlowCall call, NodeEx out
|
||||
) {
|
||||
exists(RetNodeEx ret |
|
||||
flowOutOfCallNodeCand1(call, ret, _, out) and
|
||||
c = ret.getEnclosingCallable() and
|
||||
scope = getScope(ret)
|
||||
)
|
||||
}
|
||||
|
||||
private int simpleDispatchFanoutOnReturn(DataFlowCall call, NodeEx out) {
|
||||
result =
|
||||
strictcount(DataFlowCallable c, SndLevelScopeOption scope |
|
||||
returnCallEdge1(c, scope, call, out)
|
||||
)
|
||||
}
|
||||
|
||||
private int ctxDispatchFanoutOnReturn(NodeEx out, DataFlowCall ctx) {
|
||||
exists(DataFlowCall call, DataFlowCallable c |
|
||||
simpleDispatchFanoutOnReturn(call, out) > 1 and
|
||||
not Stage1::revFlow(out, false) and
|
||||
call.getEnclosingCallable() = c and
|
||||
returnCallEdge1(c, _, ctx, _) and
|
||||
mayBenefitFromCallContextExt(call, _) and
|
||||
result =
|
||||
count(DataFlowCallable tgt, SndLevelScopeOption scope |
|
||||
tgt = viableImplInCallContextExt(call, ctx) and
|
||||
returnCallEdge1(tgt, scope, call, out)
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
private int ctxDispatchFanoutOnReturn(NodeEx out) {
|
||||
result = max(DataFlowCall ctx | | ctxDispatchFanoutOnReturn(out, ctx))
|
||||
}
|
||||
|
||||
private int dispatchFanoutOnReturn(NodeEx out) {
|
||||
result = ctxDispatchFanoutOnReturn(out)
|
||||
or
|
||||
not exists(ctxDispatchFanoutOnReturn(out)) and
|
||||
result = simpleDispatchFanoutOnReturn(_, out)
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the amount of forward branching on the origin of a cross-call path
|
||||
* edge in the graph of paths between sources and sinks that ignores call
|
||||
* contexts.
|
||||
*/
|
||||
pragma[nomagic]
|
||||
private int branch(NodeEx n1) {
|
||||
private int branch(ArgNodeEx n1) {
|
||||
result =
|
||||
strictcount(NodeEx n |
|
||||
flowOutOfCallNodeCand1(_, n1, _, n) or flowIntoCallNodeCand1(_, n1, n)
|
||||
strictcount(DataFlowCallable c |
|
||||
exists(NodeEx n |
|
||||
flowIntoCallNodeCand1(_, n1, n) and
|
||||
c = n.getEnclosingCallable()
|
||||
)
|
||||
) + sum(ParamNodeEx p1 | | getLanguageSpecificFlowIntoCallNodeCand1(n1, p1))
|
||||
}
|
||||
|
||||
@@ -1132,10 +1190,13 @@ module MakeImpl<LocationSig Location, InputSig<Location> Lang> {
|
||||
* contexts.
|
||||
*/
|
||||
pragma[nomagic]
|
||||
private int join(NodeEx n2) {
|
||||
private int join(ParamNodeEx n2) {
|
||||
result =
|
||||
strictcount(NodeEx n |
|
||||
flowOutOfCallNodeCand1(_, n, _, n2) or flowIntoCallNodeCand1(_, n, n2)
|
||||
strictcount(DataFlowCallable c |
|
||||
exists(NodeEx n |
|
||||
flowIntoCallNodeCand1(_, n, n2) and
|
||||
c = n.getEnclosingCallable()
|
||||
)
|
||||
) + sum(ArgNodeEx arg2 | | getLanguageSpecificFlowIntoCallNodeCand1(arg2, n2))
|
||||
}
|
||||
|
||||
@@ -1151,17 +1212,25 @@ module MakeImpl<LocationSig Location, InputSig<Location> Lang> {
|
||||
DataFlowCall call, RetNodeEx ret, ReturnKindExt kind, NodeEx out, boolean allowsFieldFlow
|
||||
) {
|
||||
flowOutOfCallNodeCand1(call, ret, kind, out) and
|
||||
exists(int b, int j |
|
||||
b = branch(ret) and
|
||||
j = join(out) and
|
||||
exists(int j |
|
||||
j = dispatchFanoutOnReturn(out) and
|
||||
j > 0 and
|
||||
if
|
||||
b.minimum(j) <= Config::fieldFlowBranchLimit() or
|
||||
j <= Config::fieldFlowBranchLimit() or
|
||||
ignoreFieldFlowBranchLimit(ret.getEnclosingCallable())
|
||||
then allowsFieldFlow = true
|
||||
else allowsFieldFlow = false
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate allowsFieldFlowThrough(DataFlowCall call, DataFlowCallable c) {
|
||||
exists(RetNodeEx ret |
|
||||
flowOutOfCallNodeCand1(call, ret, _, _, true) and
|
||||
c = ret.getEnclosingCallable()
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if data can flow into `call` and that this step is part of a
|
||||
* path from a source to a sink. The `allowsFieldFlow` flag indicates whether
|
||||
@@ -1412,8 +1481,9 @@ module MakeImpl<LocationSig Location, InputSig<Location> Lang> {
|
||||
)
|
||||
or
|
||||
// flow into a callable
|
||||
fwdFlowIn(node, apa, state, cc, t, ap) and
|
||||
if PrevStage::parameterMayFlowThrough(node, apa)
|
||||
exists(boolean allowsFlowThrough |
|
||||
fwdFlowIn(node, apa, state, cc, t, ap, allowsFlowThrough) and
|
||||
if allowsFlowThrough = true
|
||||
then (
|
||||
summaryCtx = TParamNodeSome(node.asNode()) and
|
||||
argT = TypOption::some(t) and
|
||||
@@ -1421,6 +1491,7 @@ module MakeImpl<LocationSig Location, InputSig<Location> Lang> {
|
||||
) else (
|
||||
summaryCtx = TParamNodeNone() and argT instanceof TypOption::None and argAp = apNone()
|
||||
)
|
||||
)
|
||||
or
|
||||
// flow out of a callable
|
||||
fwdFlowOut(_, _, node, state, cc, summaryCtx, argT, argAp, t, ap, apa)
|
||||
@@ -1604,7 +1675,7 @@ module MakeImpl<LocationSig Location, InputSig<Location> Lang> {
|
||||
private predicate fwdFlowInCand(
|
||||
DataFlowCall call, ArgNodeEx arg, FlowState state, Cc outercc, DataFlowCallable inner,
|
||||
ParamNodeEx p, ParamNodeOption summaryCtx, TypOption argT, ApOption argAp, Typ t, Ap ap,
|
||||
boolean emptyAp, ApApprox apa, boolean cc
|
||||
boolean emptyAp, ApApprox apa, boolean cc, boolean allowsFlowThrough
|
||||
) {
|
||||
exists(boolean allowsFieldFlow |
|
||||
fwdFlowIntoArg(arg, state, outercc, summaryCtx, argT, argAp, t, ap, emptyAp, apa, cc) and
|
||||
@@ -1614,7 +1685,10 @@ module MakeImpl<LocationSig Location, InputSig<Location> Lang> {
|
||||
viableImplArgNotCallContextReduced(call, arg, outercc)
|
||||
) and
|
||||
callEdgeArgParamRestrictedInlineLate(call, inner, arg, p, allowsFieldFlow, apa) and
|
||||
if allowsFieldFlow = false then emptyAp = true else any()
|
||||
(if allowsFieldFlow = false then emptyAp = true else any()) and
|
||||
if allowsFieldFlowThrough(call, inner)
|
||||
then allowsFlowThrough = true
|
||||
else allowsFlowThrough = emptyAp
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1622,20 +1696,21 @@ module MakeImpl<LocationSig Location, InputSig<Location> Lang> {
|
||||
private predicate fwdFlowInCandTypeFlowDisabled(
|
||||
DataFlowCall call, ArgNodeEx arg, FlowState state, Cc outercc, DataFlowCallable inner,
|
||||
ParamNodeEx p, ParamNodeOption summaryCtx, TypOption argT, ApOption argAp, Typ t, Ap ap,
|
||||
ApApprox apa, boolean cc
|
||||
ApApprox apa, boolean cc, boolean allowsFlowThrough
|
||||
) {
|
||||
not enableTypeFlow() and
|
||||
fwdFlowInCand(call, arg, state, outercc, inner, p, summaryCtx, argT, argAp, t, ap, _,
|
||||
apa, cc)
|
||||
apa, cc, allowsFlowThrough)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate fwdFlowInCandTypeFlowEnabled(
|
||||
DataFlowCall call, ArgNodeEx arg, Cc outercc, DataFlowCallable inner, ParamNodeEx p,
|
||||
boolean emptyAp, ApApprox apa, boolean cc
|
||||
boolean emptyAp, ApApprox apa, boolean cc, boolean allowsFlowThrough
|
||||
) {
|
||||
enableTypeFlow() and
|
||||
fwdFlowInCand(call, arg, _, outercc, inner, p, _, _, _, _, _, emptyAp, apa, cc)
|
||||
fwdFlowInCand(call, arg, _, outercc, inner, p, _, _, _, _, _, emptyAp, apa, cc,
|
||||
allowsFlowThrough)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
@@ -1650,9 +1725,10 @@ module MakeImpl<LocationSig Location, InputSig<Location> Lang> {
|
||||
pragma[nomagic]
|
||||
private predicate fwdFlowInValidEdgeTypeFlowEnabled(
|
||||
DataFlowCall call, ArgNodeEx arg, Cc outercc, DataFlowCallable inner, ParamNodeEx p,
|
||||
CcCall innercc, boolean emptyAp, ApApprox apa, boolean cc
|
||||
CcCall innercc, boolean emptyAp, ApApprox apa, boolean cc, boolean allowsFlowThrough
|
||||
) {
|
||||
fwdFlowInCandTypeFlowEnabled(call, arg, outercc, inner, p, emptyAp, apa, cc) and
|
||||
fwdFlowInCandTypeFlowEnabled(call, arg, outercc, inner, p, emptyAp, apa, cc,
|
||||
allowsFlowThrough) and
|
||||
FwdTypeFlow::typeFlowValidEdgeIn(call, inner, cc) and
|
||||
innercc = getCallContextCall(call, inner)
|
||||
}
|
||||
@@ -1661,19 +1737,19 @@ module MakeImpl<LocationSig Location, InputSig<Location> Lang> {
|
||||
predicate fwdFlowIn(
|
||||
DataFlowCall call, DataFlowCallable inner, ParamNodeEx p, FlowState state, Cc outercc,
|
||||
CcCall innercc, ParamNodeOption summaryCtx, TypOption argT, ApOption argAp, Typ t,
|
||||
Ap ap, ApApprox apa, boolean cc
|
||||
Ap ap, ApApprox apa, boolean cc, boolean allowsFlowThrough
|
||||
) {
|
||||
exists(ArgNodeEx arg |
|
||||
// type flow disabled: linear recursion
|
||||
fwdFlowInCandTypeFlowDisabled(call, arg, state, outercc, inner, p, summaryCtx, argT,
|
||||
argAp, t, ap, apa, cc) and
|
||||
argAp, t, ap, apa, cc, allowsFlowThrough) and
|
||||
fwdFlowInValidEdgeTypeFlowDisabled(call, inner, innercc, pragma[only_bind_into](cc))
|
||||
or
|
||||
// type flow enabled: non-linear recursion
|
||||
exists(boolean emptyAp |
|
||||
fwdFlowIntoArg(arg, state, outercc, summaryCtx, argT, argAp, t, ap, emptyAp, apa, cc) and
|
||||
fwdFlowInValidEdgeTypeFlowEnabled(call, arg, outercc, inner, p, innercc, emptyAp,
|
||||
apa, cc)
|
||||
apa, cc, allowsFlowThrough)
|
||||
)
|
||||
)
|
||||
}
|
||||
@@ -1683,10 +1759,16 @@ module MakeImpl<LocationSig Location, InputSig<Location> Lang> {
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate fwdFlowIn(
|
||||
ParamNodeEx p, ApApprox apa, FlowState state, CcCall innercc, Typ t, Ap ap
|
||||
ParamNodeEx p, ApApprox apa, FlowState state, CcCall innercc, Typ t, Ap ap,
|
||||
boolean allowsFlowThrough
|
||||
) {
|
||||
exists(boolean allowsFlowThrough0 |
|
||||
FwdFlowIn<FwdFlowInNoRestriction>::fwdFlowIn(_, _, p, state, _, innercc, _, _, _, t, ap,
|
||||
apa, _)
|
||||
apa, _, allowsFlowThrough0) and
|
||||
if PrevStage::parameterMayFlowThrough(p, apa)
|
||||
then allowsFlowThrough = allowsFlowThrough0
|
||||
else allowsFlowThrough = false
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
@@ -1784,7 +1866,7 @@ module MakeImpl<LocationSig Location, InputSig<Location> Lang> {
|
||||
Typ t, Ap ap, boolean cc
|
||||
) {
|
||||
FwdFlowIn<FwdFlowInNoRestriction>::fwdFlowIn(call, c, p, state, _, innercc, _, _, _, t,
|
||||
ap, _, cc)
|
||||
ap, _, cc, _)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
@@ -1903,7 +1985,7 @@ module MakeImpl<LocationSig Location, InputSig<Location> Lang> {
|
||||
ApOption argAp, ParamNodeEx p, Typ t, Ap ap
|
||||
) {
|
||||
FwdFlowIn<FwdFlowThroughRestriction>::fwdFlowIn(call, _, p, _, cc, innerCc, summaryCtx,
|
||||
argT, argAp, t, ap, _, _)
|
||||
argT, argAp, t, ap, _, _, true)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
|
||||
@@ -55,8 +55,6 @@ module InlineFlowTestMake<
|
||||
predicate isSource(DataFlowLang::Node source) { Impl::defaultSource(source) }
|
||||
|
||||
predicate isSink(DataFlowLang::Node sink) { Impl::defaultSink(sink) }
|
||||
|
||||
int fieldFlowBranchLimit() { result = 1000 }
|
||||
}
|
||||
|
||||
private module NoFlowConfig implements DataFlow::ConfigSig {
|
||||
|
||||
@@ -1409,6 +1409,8 @@ predicate knownSourceModel(Node source, string model) { sourceNode(source, _, mo
|
||||
|
||||
predicate knownSinkModel(Node sink, string model) { sinkNode(sink, _, model) }
|
||||
|
||||
class DataFlowSecondLevelScope = Unit;
|
||||
|
||||
/**
|
||||
* Holds if flow is allowed to pass from parameter `p` and back to itself as a
|
||||
* side-effect, resulting in a summary from `p` to itself.
|
||||
|
||||
@@ -61,8 +61,6 @@ module DefaultFlowConfig implements DataFlow::ConfigSig {
|
||||
predicate isSource(DataFlow::Node source) { defaultSource(source) }
|
||||
|
||||
predicate isSink(DataFlow::Node sink) { defaultSink(sink) }
|
||||
|
||||
int fieldFlowBranchLimit() { result = 1000 }
|
||||
}
|
||||
|
||||
module NoFlowConfig implements DataFlow::ConfigSig {
|
||||
|
||||
Reference in New Issue
Block a user