Merge pull request #15599 from aschackmull/dataflow/fieldflowbranchlimit-v2

Dataflow: update fieldFlowBranchLimit semantics
This commit is contained in:
Anders Schack-Mulligen
2024-04-23 10:08:05 +02:00
committed by GitHub
19 changed files with 302 additions and 46 deletions

View File

@@ -290,6 +290,8 @@ predicate knownSourceModel(Node source, string model) { none() }
predicate knownSinkModel(Node sink, 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 * 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. * side-effect, resulting in a summary from `p` to itself.

View File

@@ -22,6 +22,8 @@ module CppDataFlow implements InputSig<Location> {
predicate getAdditionalFlowIntoCallNodeTerm = Private::getAdditionalFlowIntoCallNodeTerm/2; predicate getAdditionalFlowIntoCallNodeTerm = Private::getAdditionalFlowIntoCallNodeTerm/2;
predicate getSecondLevelScope = Private::getSecondLevelScope/1;
predicate validParameterAliasStep = Private::validParameterAliasStep/2; predicate validParameterAliasStep = Private::validParameterAliasStep/2;
predicate mayBenefitFromCallContext = Private::mayBenefitFromCallContext/1; predicate mayBenefitFromCallContext = Private::mayBenefitFromCallContext/1;

View File

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

View File

@@ -84,7 +84,7 @@ void test_copy_assignment_operator()
swap(z1, z2); 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 sink(z1.data1); // $ SPURIOUS: ir ast=81:27 ast=82:16
} }

View File

@@ -2882,6 +2882,8 @@ predicate knownSourceModel(Node source, string model) { sourceNode(source, _, mo
predicate knownSinkModel(Node sink, string model) { sinkNode(sink, _, model) } 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 * 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. * side-effect, resulting in a summary from `p` to itself.

View File

@@ -14,8 +14,6 @@ module ArrayFlowConfig implements DataFlow::ConfigSig {
mc.getAnArgument() = sink.asExpr() mc.getAnArgument() = sink.asExpr()
) )
} }
int fieldFlowBranchLimit() { result = 100 }
} }
module ArrayFlow = DataFlow::Global<ArrayFlowConfig>; module ArrayFlow = DataFlow::Global<ArrayFlowConfig>;

View File

@@ -18,8 +18,6 @@ module TypesConfig implements DataFlow::ConfigSig {
mc.getAnArgument() = sink.asExpr() mc.getAnArgument() = sink.asExpr()
) )
} }
int fieldFlowBranchLimit() { result = 1000 }
} }
import ValueFlowTest<TypesConfig> import ValueFlowTest<TypesConfig>

View File

@@ -415,6 +415,8 @@ predicate knownSourceModel(Node source, string model) { sourceNode(source, _, mo
predicate knownSinkModel(Node sink, string model) { sinkNode(sink, _, model) } 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 * 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. * side-effect, resulting in a summary from `p` to itself.

View File

@@ -20,6 +20,8 @@ module JavaDataFlow implements InputSig<Location> {
Node exprNode(DataFlowExpr e) { result = Public::exprNode(e) } Node exprNode(DataFlowExpr e) { result = Public::exprNode(e) }
predicate getSecondLevelScope = Private::getSecondLevelScope/1;
predicate mayBenefitFromCallContext = Private::mayBenefitFromCallContext/1; predicate mayBenefitFromCallContext = Private::mayBenefitFromCallContext/1;
predicate viableImplInCallContext = Private::viableImplInCallContext/2; predicate viableImplInCallContext = Private::viableImplInCallContext/2;

View File

@@ -581,6 +581,81 @@ predicate knownSourceModel(Node source, string model) { sourceNode(source, _, mo
predicate knownSinkModel(Node sink, string model) { sinkNode(sink, _, model) } 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 * 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. * side-effect, resulting in a summary from `p` to itself.

View File

@@ -18,8 +18,6 @@ module ValueFlowConfig implements DataFlow::ConfigSig {
predicate isSink(DataFlow::Node n) { predicate isSink(DataFlow::Node n) {
exists(MethodCall ma | ma.getMethod().hasName("sink") | n.asExpr() = ma.getAnArgument()) exists(MethodCall ma | ma.getMethod().hasName("sink") | n.asExpr() = ma.getAnArgument())
} }
int fieldFlowBranchLimit() { result = 100 }
} }
module ValueFlow = DataFlow::Global<ValueFlowConfig>; module ValueFlow = DataFlow::Global<ValueFlowConfig>;

View File

@@ -1087,6 +1087,8 @@ predicate knownSinkModel(Node sink, string model) {
sink = ModelOutput::getASinkNode(_, model).asSink() sink = ModelOutput::getASinkNode(_, model).asSink()
} }
class DataFlowSecondLevelScope = Unit;
/** /**
* Holds if flow is allowed to pass from parameter `p` and back to itself as a * 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. * side-effect, resulting in a summary from `p` to itself.

View File

@@ -2254,6 +2254,8 @@ predicate knownSinkModel(Node sink, string model) {
sink = ModelOutput::getASinkNode(_, model).asSink() sink = ModelOutput::getASinkNode(_, model).asSink()
} }
class DataFlowSecondLevelScope = Unit;
/** /**
* Holds if flow is allowed to pass from parameter `p` and back to itself as a * 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. * side-effect, resulting in a summary from `p` to itself.

View File

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

View File

@@ -308,6 +308,24 @@ signature module InputSig<LocationSig Location> {
*/ */
default int getAdditionalFlowIntoCallNodeTerm(ArgumentNode arg, ParameterNode p) { none() } 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] bindingset[call, p, arg]
default predicate golangSpecificParamArgFilter( default predicate golangSpecificParamArgFilter(
DataFlowCall call, ParameterNode p, ArgumentNode arg DataFlowCall call, ParameterNode p, ArgumentNode arg

View File

@@ -722,7 +722,7 @@ module MakeImpl<LocationSig Location, InputSig<Location> Lang> {
* the enclosing callable in order to reach a sink. * the enclosing callable in order to reach a sink.
*/ */
pragma[nomagic] pragma[nomagic]
private predicate revFlow(NodeEx node, boolean toReturn) { additional predicate revFlow(NodeEx node, boolean toReturn) {
revFlow0(node, toReturn) and revFlow0(node, toReturn) and
fwdFlow(node) fwdFlow(node)
} }
@@ -1113,16 +1113,74 @@ module MakeImpl<LocationSig Location, InputSig<Location> Lang> {
result = getAdditionalFlowIntoCallNodeTerm(arg.projectToNode(), p.projectToNode()) 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 * 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 * edge in the graph of paths between sources and sinks that ignores call
* contexts. * contexts.
*/ */
pragma[nomagic] pragma[nomagic]
private int branch(NodeEx n1) { private int branch(ArgNodeEx n1) {
result = result =
strictcount(NodeEx n | strictcount(DataFlowCallable c |
flowOutOfCallNodeCand1(_, n1, _, n) or flowIntoCallNodeCand1(_, n1, n) exists(NodeEx n |
flowIntoCallNodeCand1(_, n1, n) and
c = n.getEnclosingCallable()
)
) + sum(ParamNodeEx p1 | | getLanguageSpecificFlowIntoCallNodeCand1(n1, p1)) ) + sum(ParamNodeEx p1 | | getLanguageSpecificFlowIntoCallNodeCand1(n1, p1))
} }
@@ -1132,10 +1190,13 @@ module MakeImpl<LocationSig Location, InputSig<Location> Lang> {
* contexts. * contexts.
*/ */
pragma[nomagic] pragma[nomagic]
private int join(NodeEx n2) { private int join(ParamNodeEx n2) {
result = result =
strictcount(NodeEx n | strictcount(DataFlowCallable c |
flowOutOfCallNodeCand1(_, n, _, n2) or flowIntoCallNodeCand1(_, n, n2) exists(NodeEx n |
flowIntoCallNodeCand1(_, n, n2) and
c = n.getEnclosingCallable()
)
) + sum(ArgNodeEx arg2 | | getLanguageSpecificFlowIntoCallNodeCand1(arg2, n2)) ) + 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 DataFlowCall call, RetNodeEx ret, ReturnKindExt kind, NodeEx out, boolean allowsFieldFlow
) { ) {
flowOutOfCallNodeCand1(call, ret, kind, out) and flowOutOfCallNodeCand1(call, ret, kind, out) and
exists(int b, int j | exists(int j |
b = branch(ret) and j = dispatchFanoutOnReturn(out) and
j = join(out) and j > 0 and
if if
b.minimum(j) <= Config::fieldFlowBranchLimit() or j <= Config::fieldFlowBranchLimit() or
ignoreFieldFlowBranchLimit(ret.getEnclosingCallable()) ignoreFieldFlowBranchLimit(ret.getEnclosingCallable())
then allowsFieldFlow = true then allowsFieldFlow = true
else allowsFieldFlow = false 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 * 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 * path from a source to a sink. The `allowsFieldFlow` flag indicates whether
@@ -1412,14 +1481,16 @@ module MakeImpl<LocationSig Location, InputSig<Location> Lang> {
) )
or or
// flow into a callable // flow into a callable
fwdFlowIn(node, apa, state, cc, t, ap) and exists(boolean allowsFlowThrough |
if PrevStage::parameterMayFlowThrough(node, apa) fwdFlowIn(node, apa, state, cc, t, ap, allowsFlowThrough) and
then ( if allowsFlowThrough = true
summaryCtx = TParamNodeSome(node.asNode()) and then (
argT = TypOption::some(t) and summaryCtx = TParamNodeSome(node.asNode()) and
argAp = apSome(ap) argT = TypOption::some(t) and
) else ( argAp = apSome(ap)
summaryCtx = TParamNodeNone() and argT instanceof TypOption::None and argAp = apNone() ) else (
summaryCtx = TParamNodeNone() and argT instanceof TypOption::None and argAp = apNone()
)
) )
or or
// flow out of a callable // flow out of a callable
@@ -1604,7 +1675,7 @@ module MakeImpl<LocationSig Location, InputSig<Location> Lang> {
private predicate fwdFlowInCand( private predicate fwdFlowInCand(
DataFlowCall call, ArgNodeEx arg, FlowState state, Cc outercc, DataFlowCallable inner, DataFlowCall call, ArgNodeEx arg, FlowState state, Cc outercc, DataFlowCallable inner,
ParamNodeEx p, ParamNodeOption summaryCtx, TypOption argT, ApOption argAp, Typ t, Ap ap, 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 | exists(boolean allowsFieldFlow |
fwdFlowIntoArg(arg, state, outercc, summaryCtx, argT, argAp, t, ap, emptyAp, apa, cc) and 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) viableImplArgNotCallContextReduced(call, arg, outercc)
) and ) and
callEdgeArgParamRestrictedInlineLate(call, inner, arg, p, allowsFieldFlow, apa) 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( private predicate fwdFlowInCandTypeFlowDisabled(
DataFlowCall call, ArgNodeEx arg, FlowState state, Cc outercc, DataFlowCallable inner, DataFlowCall call, ArgNodeEx arg, FlowState state, Cc outercc, DataFlowCallable inner,
ParamNodeEx p, ParamNodeOption summaryCtx, TypOption argT, ApOption argAp, Typ t, Ap ap, 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 not enableTypeFlow() and
fwdFlowInCand(call, arg, state, outercc, inner, p, summaryCtx, argT, argAp, t, ap, _, fwdFlowInCand(call, arg, state, outercc, inner, p, summaryCtx, argT, argAp, t, ap, _,
apa, cc) apa, cc, allowsFlowThrough)
} }
pragma[nomagic] pragma[nomagic]
private predicate fwdFlowInCandTypeFlowEnabled( private predicate fwdFlowInCandTypeFlowEnabled(
DataFlowCall call, ArgNodeEx arg, Cc outercc, DataFlowCallable inner, ParamNodeEx p, 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 enableTypeFlow() and
fwdFlowInCand(call, arg, _, outercc, inner, p, _, _, _, _, _, emptyAp, apa, cc) fwdFlowInCand(call, arg, _, outercc, inner, p, _, _, _, _, _, emptyAp, apa, cc,
allowsFlowThrough)
} }
pragma[nomagic] pragma[nomagic]
@@ -1650,9 +1725,10 @@ module MakeImpl<LocationSig Location, InputSig<Location> Lang> {
pragma[nomagic] pragma[nomagic]
private predicate fwdFlowInValidEdgeTypeFlowEnabled( private predicate fwdFlowInValidEdgeTypeFlowEnabled(
DataFlowCall call, ArgNodeEx arg, Cc outercc, DataFlowCallable inner, ParamNodeEx p, 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 FwdTypeFlow::typeFlowValidEdgeIn(call, inner, cc) and
innercc = getCallContextCall(call, inner) innercc = getCallContextCall(call, inner)
} }
@@ -1661,19 +1737,19 @@ module MakeImpl<LocationSig Location, InputSig<Location> Lang> {
predicate fwdFlowIn( predicate fwdFlowIn(
DataFlowCall call, DataFlowCallable inner, ParamNodeEx p, FlowState state, Cc outercc, DataFlowCall call, DataFlowCallable inner, ParamNodeEx p, FlowState state, Cc outercc,
CcCall innercc, ParamNodeOption summaryCtx, TypOption argT, ApOption argAp, Typ t, 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 | exists(ArgNodeEx arg |
// type flow disabled: linear recursion // type flow disabled: linear recursion
fwdFlowInCandTypeFlowDisabled(call, arg, state, outercc, inner, p, summaryCtx, argT, 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)) fwdFlowInValidEdgeTypeFlowDisabled(call, inner, innercc, pragma[only_bind_into](cc))
or or
// type flow enabled: non-linear recursion // type flow enabled: non-linear recursion
exists(boolean emptyAp | exists(boolean emptyAp |
fwdFlowIntoArg(arg, state, outercc, summaryCtx, argT, argAp, t, ap, emptyAp, apa, cc) and fwdFlowIntoArg(arg, state, outercc, summaryCtx, argT, argAp, t, ap, emptyAp, apa, cc) and
fwdFlowInValidEdgeTypeFlowEnabled(call, arg, outercc, inner, p, innercc, emptyAp, 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] pragma[nomagic]
private predicate fwdFlowIn( 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
) { ) {
FwdFlowIn<FwdFlowInNoRestriction>::fwdFlowIn(_, _, p, state, _, innercc, _, _, _, t, ap, exists(boolean allowsFlowThrough0 |
apa, _) FwdFlowIn<FwdFlowInNoRestriction>::fwdFlowIn(_, _, p, state, _, innercc, _, _, _, t, ap,
apa, _, allowsFlowThrough0) and
if PrevStage::parameterMayFlowThrough(p, apa)
then allowsFlowThrough = allowsFlowThrough0
else allowsFlowThrough = false
)
} }
pragma[nomagic] pragma[nomagic]
@@ -1784,7 +1866,7 @@ module MakeImpl<LocationSig Location, InputSig<Location> Lang> {
Typ t, Ap ap, boolean cc Typ t, Ap ap, boolean cc
) { ) {
FwdFlowIn<FwdFlowInNoRestriction>::fwdFlowIn(call, c, p, state, _, innercc, _, _, _, t, FwdFlowIn<FwdFlowInNoRestriction>::fwdFlowIn(call, c, p, state, _, innercc, _, _, _, t,
ap, _, cc) ap, _, cc, _)
} }
pragma[nomagic] pragma[nomagic]
@@ -1903,7 +1985,7 @@ module MakeImpl<LocationSig Location, InputSig<Location> Lang> {
ApOption argAp, ParamNodeEx p, Typ t, Ap ap ApOption argAp, ParamNodeEx p, Typ t, Ap ap
) { ) {
FwdFlowIn<FwdFlowThroughRestriction>::fwdFlowIn(call, _, p, _, cc, innerCc, summaryCtx, FwdFlowIn<FwdFlowThroughRestriction>::fwdFlowIn(call, _, p, _, cc, innerCc, summaryCtx,
argT, argAp, t, ap, _, _) argT, argAp, t, ap, _, _, true)
} }
pragma[nomagic] pragma[nomagic]

View File

@@ -55,8 +55,6 @@ module InlineFlowTestMake<
predicate isSource(DataFlowLang::Node source) { Impl::defaultSource(source) } predicate isSource(DataFlowLang::Node source) { Impl::defaultSource(source) }
predicate isSink(DataFlowLang::Node sink) { Impl::defaultSink(sink) } predicate isSink(DataFlowLang::Node sink) { Impl::defaultSink(sink) }
int fieldFlowBranchLimit() { result = 1000 }
} }
private module NoFlowConfig implements DataFlow::ConfigSig { private module NoFlowConfig implements DataFlow::ConfigSig {

View File

@@ -1409,6 +1409,8 @@ predicate knownSourceModel(Node source, string model) { sourceNode(source, _, mo
predicate knownSinkModel(Node sink, string model) { sinkNode(sink, _, model) } 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 * 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. * side-effect, resulting in a summary from `p` to itself.

View File

@@ -61,8 +61,6 @@ module DefaultFlowConfig implements DataFlow::ConfigSig {
predicate isSource(DataFlow::Node source) { defaultSource(source) } predicate isSource(DataFlow::Node source) { defaultSource(source) }
predicate isSink(DataFlow::Node sink) { defaultSink(sink) } predicate isSink(DataFlow::Node sink) { defaultSink(sink) }
int fieldFlowBranchLimit() { result = 1000 }
} }
module NoFlowConfig implements DataFlow::ConfigSig { module NoFlowConfig implements DataFlow::ConfigSig {