diff --git a/change-notes/1.26/analysis-cpp.md b/change-notes/1.26/analysis-cpp.md index 6838e8ca642..14f58174a9a 100644 --- a/change-notes/1.26/analysis-cpp.md +++ b/change-notes/1.26/analysis-cpp.md @@ -14,6 +14,7 @@ The following changes in version 1.26 affect C/C++ analysis in all applications. | **Query** | **Expected impact** | **Change** | |----------------------------|------------------------|------------------------------------------------------------------| | Inconsistent direction of for loop (`cpp/inconsistent-loop-direction`) | Fewer false positive results | The query now accounts for intentional wrapping of an unsigned loop counter. | +| Overflow in uncontrolled allocation size (`cpp/uncontrolled-allocation-size`) | | The precision of this query has been decreased from "high" to "medium". As a result, the query is still run but results are no longer displayed on LGTM by default. | | Comparison result is always the same (`cpp/constant-comparison`) | More correct results | Bounds on expressions involving multiplication can now be determined in more cases. | ## Changes to libraries diff --git a/change-notes/1.26/analysis-csharp.md b/change-notes/1.26/analysis-csharp.md new file mode 100644 index 00000000000..5580938dc2a --- /dev/null +++ b/change-notes/1.26/analysis-csharp.md @@ -0,0 +1,29 @@ +# Improvements to C# analysis + +The following changes in version 1.26 affect C# analysis in all applications. + +## New queries + +| **Query** | **Tags** | **Purpose** | +|-----------------------------|-----------|--------------------------------------------------------------------| + + +## Changes to existing queries + +| **Query** | **Expected impact** | **Change** | +|------------------------------|------------------------|-----------------------------------| + + +## Removal of old queries + +## Changes to code extraction + +* Partial method bodies are extracted. Previously, partial method bodies were skipped completely. + +## Changes to libraries + +## Changes to autobuilder + +## Changes to tooling support + +* The Abstract Syntax Tree of C# files can be printed in Visual Studio Code. diff --git a/change-notes/1.26/analysis-javascript.md b/change-notes/1.26/analysis-javascript.md index 2615bb804d8..653d33be915 100644 --- a/change-notes/1.26/analysis-javascript.md +++ b/change-notes/1.26/analysis-javascript.md @@ -24,7 +24,7 @@ | **Query** | **Expected impact** | **Change** | |--------------------------------|------------------------------|---------------------------------------------------------------------------| +| Incomplete URL substring sanitization (`js/incomplete-url-substring-sanitization`) | More results | This query now recognizes additional URLs when the substring check is an inclusion check. | ## Changes to libraries - diff --git a/cpp/ql/src/Security/CWE/CWE-190/TaintedAllocationSize.ql b/cpp/ql/src/Security/CWE/CWE-190/TaintedAllocationSize.ql index 54c6d1e7a96..cc2d52385c7 100644 --- a/cpp/ql/src/Security/CWE/CWE-190/TaintedAllocationSize.ql +++ b/cpp/ql/src/Security/CWE/CWE-190/TaintedAllocationSize.ql @@ -4,7 +4,7 @@ * user can result in integer overflow. * @kind path-problem * @problem.severity error - * @precision high + * @precision medium * @id cpp/uncontrolled-allocation-size * @tags reliability * security diff --git a/cpp/ql/src/experimental/semmle/code/cpp/models/interfaces/SimpleRangeAnalysisExpr.qll b/cpp/ql/src/experimental/semmle/code/cpp/models/interfaces/SimpleRangeAnalysisExpr.qll new file mode 100644 index 00000000000..c8c1110b3af --- /dev/null +++ b/cpp/ql/src/experimental/semmle/code/cpp/models/interfaces/SimpleRangeAnalysisExpr.qll @@ -0,0 +1,78 @@ +/** + * EXPERIMENTAL: The API of this module may change without notice. + * + * Provides a class for modeling `Expr`s with a restricted range. + */ + +import cpp +import semmle.code.cpp.rangeanalysis.SimpleRangeAnalysis + +/** + * EXPERIMENTAL: The API of this class may change without notice. + * + * An expression for which a range can be deduced. Extend this class to add + * functionality to the range analysis library. + */ +abstract class SimpleRangeAnalysisExpr extends Expr { + /** + * Gets the lower bound of the expression. + * + * Implementations of this predicate should use + * `getFullyConvertedLowerBounds` and `getFullyConvertedUpperBounds` for + * recursive calls to get the bounds of their children. + */ + abstract float getLowerBounds(); + + /** + * Gets the upper bound of the expression. + * + * Implementations of this predicate should use + * `getFullyConvertedLowerBounds` and `getFullyConvertedUpperBounds` for + * recursive calls to get the bounds of their children. + */ + abstract float getUpperBounds(); + + /** + * Holds if the range this expression depends on the definition `srcDef` for + * StackVariable `srcVar`. + * + * Because this predicate cannot be recursive, most implementations should + * override `dependsOnChild` instead. + */ + predicate dependsOnDef(RangeSsaDefinition srcDef, StackVariable srcVar) { none() } + + /** + * Holds if this expression depends on the range of its unconverted + * subexpression `child`. This information is used to inform the range + * analysis about cyclic dependencies. Without this information, range + * analysis might work for simple cases but will go into infinite loops on + * complex code. + * + * For example, when modeling a function call whose return value depends on + * all of its arguments, implement this predicate as + * `child = this.getAnArgument()`. + */ + abstract predicate dependsOnChild(Expr child); +} + +import SimpleRangeAnalysisInternal + +/** + * This class exists to prevent the QL front end from emitting compile errors + * inside `SimpleRangeAnalysis.qll` about certain conjuncts being empty + * because the overrides of `SimpleRangeAnalysisExpr` that happen to be in + * scope do not make use of every feature it offers. + */ +private class Empty extends SimpleRangeAnalysisExpr { + Empty() { + // This predicate is complicated enough that the QL type checker doesn't + // see it as empty but simple enough that the optimizer should. + this = this and none() + } + + override float getLowerBounds() { none() } + + override float getUpperBounds() { none() } + + override predicate dependsOnChild(Expr child) { none() } +} diff --git a/cpp/ql/src/semmle/code/cpp/dataflow/internal/DataFlowImpl.qll b/cpp/ql/src/semmle/code/cpp/dataflow/internal/DataFlowImpl.qll index 5042dce683f..8c210edbe5f 100644 --- a/cpp/ql/src/semmle/code/cpp/dataflow/internal/DataFlowImpl.qll +++ b/cpp/ql/src/semmle/code/cpp/dataflow/internal/DataFlowImpl.qll @@ -1066,7 +1066,7 @@ private module LocalFlowBigStep { * Holds if `node` can be the first node in a maximal subsequence of local * flow steps in a dataflow path. */ - private predicate localFlowEntry(Node node, Configuration config) { + predicate localFlowEntry(Node node, Configuration config) { nodeCand2(node, config) and ( config.isSource(node) or @@ -1650,53 +1650,53 @@ private class AccessPathOption extends TAccessPathOption { * Holds if `node` is reachable with access path `ap` from a source in * the configuration `config`. * - * The Boolean `fromArg` records whether the node is reached through an + * 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. */ private predicate flowFwd( - Node node, boolean fromArg, AccessPathOption argAp, AccessPathFront apf, AccessPath ap, + Node node, CallContext cc, AccessPathOption argAp, AccessPathFront apf, AccessPath ap, Configuration config ) { - flowFwd0(node, fromArg, argAp, apf, ap, config) and + flowFwd0(node, cc, argAp, apf, ap, config) and flowCand(node, _, _, apf, config) } private predicate flowFwd0( - Node node, boolean fromArg, AccessPathOption argAp, AccessPathFront apf, AccessPath ap, + Node node, CallContext cc, AccessPathOption argAp, AccessPathFront apf, AccessPath ap, Configuration config ) { flowCand(node, _, _, _, config) and config.isSource(node) and - fromArg = false and + cc instanceof CallContextAny and argAp = TAccessPathNone() and ap = TNil(getNodeType(node)) and apf = ap.(AccessPathNil).getFront() or flowCand(node, _, _, _, unbind(config)) and ( - exists(Node mid | - flowFwd(mid, fromArg, argAp, apf, ap, config) and - localFlowBigStep(mid, node, true, _, config, _) + exists(Node mid, LocalCallContext localCC | + flowFwdLocalEntry(mid, cc, argAp, apf, ap, localCC, config) and + localFlowBigStep(mid, node, true, _, config, localCC) ) or - exists(Node mid, AccessPathNil nil | - flowFwd(mid, fromArg, argAp, _, nil, config) and - localFlowBigStep(mid, node, false, apf, config, _) and + exists(Node mid, AccessPathNil nil, LocalCallContext localCC | + flowFwdLocalEntry(mid, cc, argAp, _, nil, localCC, config) and + localFlowBigStep(mid, node, false, apf, config, localCC) and apf = ap.(AccessPathNil).getFront() ) or exists(Node mid | flowFwd(mid, _, _, apf, ap, config) and jumpStep(mid, node, config) and - fromArg = false and + cc instanceof CallContextAny and argAp = TAccessPathNone() ) or exists(Node mid, AccessPathNil nil | flowFwd(mid, _, _, _, nil, config) and additionalJumpStep(mid, node, config) and - fromArg = false and + cc instanceof CallContextAny and argAp = TAccessPathNone() and ap = TNil(getNodeType(node)) and apf = ap.(AccessPathNil).getFront() @@ -1704,40 +1704,51 @@ private predicate flowFwd0( ) or // store - exists(TypedContent tc | flowFwdStore(node, tc, pop(tc, ap), apf, fromArg, argAp, config)) + exists(TypedContent tc | flowFwdStore(node, tc, pop(tc, ap), apf, cc, argAp, config)) or // read exists(TypedContent tc | - flowFwdRead(node, _, push(tc, ap), apf, fromArg, argAp, config) and + flowFwdRead(node, _, push(tc, ap), apf, cc, argAp, config) and flowFwdConsCand(tc, apf, ap, config) ) or // flow into a callable - flowFwdIn(_, node, _, _, apf, ap, config) and - fromArg = true and + flowFwdIn(_, node, _, cc, _, apf, ap, config) and if flowCand(node, true, _, apf, config) then argAp = TAccessPathSome(ap) else argAp = TAccessPathNone() or // flow out of a callable exists(DataFlowCall call | - flowFwdOut(call, node, fromArg, argAp, apf, ap, config) and - fromArg = false + exists(DataFlowCallable c | + flowFwdOut(call, node, any(CallContextNoCall innercc), c, argAp, apf, ap, config) and + if reducedViableImplInReturn(c, call) then cc = TReturn(c, call) else cc = TAnyCallContext() + ) or exists(AccessPath argAp0 | flowFwdOutFromArg(call, node, argAp0, apf, ap, config) and - flowFwdIsEntered(call, fromArg, argAp, argAp0, config) + flowFwdIsEntered(call, cc, argAp, argAp0, config) ) ) } +pragma[nomagic] +private predicate flowFwdLocalEntry( + Node node, CallContext cc, AccessPathOption argAp, AccessPathFront apf, AccessPath ap, + LocalCallContext localCC, Configuration config +) { + flowFwd(node, cc, argAp, apf, ap, config) and + localFlowEntry(node, config) and + localCC = getLocalCallContext(cc, node.getEnclosingCallable()) +} + pragma[nomagic] private predicate flowFwdStore( - Node node, TypedContent tc, AccessPath ap0, AccessPathFront apf, boolean fromArg, + Node node, TypedContent tc, AccessPath ap0, AccessPathFront apf, CallContext cc, AccessPathOption argAp, Configuration config ) { exists(Node mid, AccessPathFront apf0 | - flowFwd(mid, fromArg, argAp, apf0, ap0, config) and + flowFwd(mid, cc, argAp, apf0, ap0, config) and flowFwdStore0(mid, tc, node, apf0, apf, config) ) } @@ -1764,20 +1775,20 @@ private predicate flowFwdStore0( pragma[nomagic] private predicate flowFwdRead0( - Node node1, TypedContent tc, AccessPathFrontHead apf0, AccessPath ap0, Node node2, - boolean fromArg, AccessPathOption argAp, Configuration config + Node node1, TypedContent tc, AccessPathFrontHead apf0, AccessPath ap0, Node node2, CallContext cc, + AccessPathOption argAp, Configuration config ) { - flowFwd(node1, fromArg, argAp, apf0, ap0, config) and + flowFwd(node1, cc, argAp, apf0, ap0, config) and readCandFwd(node1, tc, apf0, node2, config) } pragma[nomagic] private predicate flowFwdRead( - Node node, AccessPathFrontHead apf0, AccessPath ap0, AccessPathFront apf, boolean fromArg, + Node node, AccessPathFrontHead apf0, AccessPath ap0, AccessPathFront apf, CallContext cc, AccessPathOption argAp, Configuration config ) { exists(Node mid, TypedContent tc | - flowFwdRead0(mid, tc, apf0, ap0, node, fromArg, argAp, config) and + flowFwdRead0(mid, tc, apf0, ap0, node, cc, argAp, config) and flowCand(node, _, _, apf, unbind(config)) and flowCandConsCand(tc, apf, unbind(config)) ) @@ -1795,13 +1806,16 @@ private predicate flowFwdConsCand( pragma[nomagic] private predicate flowFwdIn( - DataFlowCall call, ParameterNode p, boolean fromArg, AccessPathOption argAp, AccessPathFront apf, - AccessPath ap, Configuration config + DataFlowCall call, ParameterNode p, CallContext outercc, CallContext innercc, + AccessPathOption argAp, AccessPathFront apf, AccessPath ap, Configuration config ) { - exists(ArgumentNode arg, boolean allowsFieldFlow | - flowFwd(arg, fromArg, argAp, apf, ap, config) and + exists(ArgumentNode arg, boolean allowsFieldFlow, DataFlowCallable c | + flowFwd(arg, outercc, argAp, apf, ap, config) and flowIntoCallNodeCand2(call, arg, p, allowsFieldFlow, config) and - flowCand(p, _, _, _, unbind(config)) + c = p.getEnclosingCallable() and + c = resolveCall(call, outercc) and + flowCand(p, _, _, _, unbind(config)) and + if recordDataFlowCallSite(call, c) then innercc = TSpecificCall(call) else innercc = TSomeCall() | ap instanceof AccessPathNil or allowsFieldFlow = true ) @@ -1809,13 +1823,19 @@ private predicate flowFwdIn( pragma[nomagic] private predicate flowFwdOut( - DataFlowCall call, Node node, boolean fromArg, AccessPathOption argAp, AccessPathFront apf, - AccessPath ap, Configuration config + DataFlowCall call, Node node, CallContext innercc, DataFlowCallable innerc, + AccessPathOption argAp, AccessPathFront apf, AccessPath ap, Configuration config ) { exists(ReturnNodeExt ret, boolean allowsFieldFlow | - flowFwd(ret, fromArg, argAp, apf, ap, config) and + flowFwd(ret, innercc, argAp, apf, ap, config) and flowOutOfCallNodeCand2(call, ret, node, allowsFieldFlow, config) and - flowCand(node, _, _, _, unbind(config)) + innerc = ret.getEnclosingCallable() and + flowCand(node, _, _, _, unbind(config)) and + ( + resolveReturn(innercc, innerc, call) + or + innercc.(CallContextCall).matchesCall(call) + ) | ap instanceof AccessPathNil or allowsFieldFlow = true ) @@ -1826,7 +1846,7 @@ private predicate flowFwdOutFromArg( DataFlowCall call, Node node, AccessPath argAp, AccessPathFront apf, AccessPath ap, Configuration config ) { - flowFwdOut(call, node, true, TAccessPathSome(argAp), apf, ap, config) + flowFwdOut(call, node, any(CallContextCall ccc), _, TAccessPathSome(argAp), apf, ap, config) } /** @@ -1834,10 +1854,10 @@ private predicate flowFwdOutFromArg( */ pragma[nomagic] private predicate flowFwdIsEntered( - DataFlowCall call, boolean fromArg, AccessPathOption argAp, AccessPath ap, Configuration config + DataFlowCall call, CallContext cc, AccessPathOption argAp, AccessPath ap, Configuration config ) { exists(ParameterNode p, AccessPathFront apf | - flowFwdIn(call, p, fromArg, argAp, apf, ap, config) and + flowFwdIn(call, p, cc, _, argAp, apf, ap, config) and flowCand(p, true, TAccessPathFrontSome(_), apf, config) ) } @@ -1920,7 +1940,7 @@ private predicate flow0( // flow out of a callable flowOut(_, node, _, _, ap, config) and toReturn = true and - if flowFwd(node, true, TAccessPathSome(_), _, ap, config) + if flowFwd(node, any(CallContextCall ccc), TAccessPathSome(_), _, ap, config) then returnAp = TAccessPathSome(ap) else returnAp = TAccessPathNone() } @@ -2006,9 +2026,10 @@ private predicate flowIsReturned( DataFlowCall call, boolean toReturn, AccessPathOption returnAp, AccessPath ap, Configuration config ) { - exists(ReturnNodeExt ret | + exists(ReturnNodeExt ret, CallContextCall ccc | flowOut(call, ret, toReturn, returnAp, ap, config) and - flowFwd(ret, true, TAccessPathSome(_), _, ap, config) + flowFwd(ret, ccc, TAccessPathSome(_), _, ap, config) and + ccc.matchesCall(call) ) } @@ -2031,7 +2052,7 @@ private newtype TSummaryCtx = exists(ReturnNodeExt ret, Configuration config, AccessPath ap0 | parameterFlow(p, ap, ret.getEnclosingCallable(), config) and flow(ret, true, TAccessPathSome(_), ap0, config) and - flowFwd(ret, true, TAccessPathSome(ap), _, ap0, config) + flowFwd(ret, any(CallContextCall ccc), TAccessPathSome(ap), _, ap0, config) ) } @@ -2352,7 +2373,7 @@ private predicate pathOutOfCallable0( ) { pos = getReturnPosition(mid.getNode()) and innercc = mid.getCallContext() and - not innercc instanceof CallContextCall and + innercc instanceof CallContextNoCall and ap = mid.getAp() and config = mid.getConfiguration() } @@ -2867,7 +2888,7 @@ private module FlowExploration { ) { pos = getReturnPosition(mid.getNode()) and innercc = mid.getCallContext() and - not innercc instanceof CallContextCall and + innercc instanceof CallContextNoCall and ap = mid.getAp() and config = mid.getConfiguration() } diff --git a/cpp/ql/src/semmle/code/cpp/dataflow/internal/DataFlowImpl2.qll b/cpp/ql/src/semmle/code/cpp/dataflow/internal/DataFlowImpl2.qll index 5042dce683f..8c210edbe5f 100644 --- a/cpp/ql/src/semmle/code/cpp/dataflow/internal/DataFlowImpl2.qll +++ b/cpp/ql/src/semmle/code/cpp/dataflow/internal/DataFlowImpl2.qll @@ -1066,7 +1066,7 @@ private module LocalFlowBigStep { * Holds if `node` can be the first node in a maximal subsequence of local * flow steps in a dataflow path. */ - private predicate localFlowEntry(Node node, Configuration config) { + predicate localFlowEntry(Node node, Configuration config) { nodeCand2(node, config) and ( config.isSource(node) or @@ -1650,53 +1650,53 @@ private class AccessPathOption extends TAccessPathOption { * Holds if `node` is reachable with access path `ap` from a source in * the configuration `config`. * - * The Boolean `fromArg` records whether the node is reached through an + * 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. */ private predicate flowFwd( - Node node, boolean fromArg, AccessPathOption argAp, AccessPathFront apf, AccessPath ap, + Node node, CallContext cc, AccessPathOption argAp, AccessPathFront apf, AccessPath ap, Configuration config ) { - flowFwd0(node, fromArg, argAp, apf, ap, config) and + flowFwd0(node, cc, argAp, apf, ap, config) and flowCand(node, _, _, apf, config) } private predicate flowFwd0( - Node node, boolean fromArg, AccessPathOption argAp, AccessPathFront apf, AccessPath ap, + Node node, CallContext cc, AccessPathOption argAp, AccessPathFront apf, AccessPath ap, Configuration config ) { flowCand(node, _, _, _, config) and config.isSource(node) and - fromArg = false and + cc instanceof CallContextAny and argAp = TAccessPathNone() and ap = TNil(getNodeType(node)) and apf = ap.(AccessPathNil).getFront() or flowCand(node, _, _, _, unbind(config)) and ( - exists(Node mid | - flowFwd(mid, fromArg, argAp, apf, ap, config) and - localFlowBigStep(mid, node, true, _, config, _) + exists(Node mid, LocalCallContext localCC | + flowFwdLocalEntry(mid, cc, argAp, apf, ap, localCC, config) and + localFlowBigStep(mid, node, true, _, config, localCC) ) or - exists(Node mid, AccessPathNil nil | - flowFwd(mid, fromArg, argAp, _, nil, config) and - localFlowBigStep(mid, node, false, apf, config, _) and + exists(Node mid, AccessPathNil nil, LocalCallContext localCC | + flowFwdLocalEntry(mid, cc, argAp, _, nil, localCC, config) and + localFlowBigStep(mid, node, false, apf, config, localCC) and apf = ap.(AccessPathNil).getFront() ) or exists(Node mid | flowFwd(mid, _, _, apf, ap, config) and jumpStep(mid, node, config) and - fromArg = false and + cc instanceof CallContextAny and argAp = TAccessPathNone() ) or exists(Node mid, AccessPathNil nil | flowFwd(mid, _, _, _, nil, config) and additionalJumpStep(mid, node, config) and - fromArg = false and + cc instanceof CallContextAny and argAp = TAccessPathNone() and ap = TNil(getNodeType(node)) and apf = ap.(AccessPathNil).getFront() @@ -1704,40 +1704,51 @@ private predicate flowFwd0( ) or // store - exists(TypedContent tc | flowFwdStore(node, tc, pop(tc, ap), apf, fromArg, argAp, config)) + exists(TypedContent tc | flowFwdStore(node, tc, pop(tc, ap), apf, cc, argAp, config)) or // read exists(TypedContent tc | - flowFwdRead(node, _, push(tc, ap), apf, fromArg, argAp, config) and + flowFwdRead(node, _, push(tc, ap), apf, cc, argAp, config) and flowFwdConsCand(tc, apf, ap, config) ) or // flow into a callable - flowFwdIn(_, node, _, _, apf, ap, config) and - fromArg = true and + flowFwdIn(_, node, _, cc, _, apf, ap, config) and if flowCand(node, true, _, apf, config) then argAp = TAccessPathSome(ap) else argAp = TAccessPathNone() or // flow out of a callable exists(DataFlowCall call | - flowFwdOut(call, node, fromArg, argAp, apf, ap, config) and - fromArg = false + exists(DataFlowCallable c | + flowFwdOut(call, node, any(CallContextNoCall innercc), c, argAp, apf, ap, config) and + if reducedViableImplInReturn(c, call) then cc = TReturn(c, call) else cc = TAnyCallContext() + ) or exists(AccessPath argAp0 | flowFwdOutFromArg(call, node, argAp0, apf, ap, config) and - flowFwdIsEntered(call, fromArg, argAp, argAp0, config) + flowFwdIsEntered(call, cc, argAp, argAp0, config) ) ) } +pragma[nomagic] +private predicate flowFwdLocalEntry( + Node node, CallContext cc, AccessPathOption argAp, AccessPathFront apf, AccessPath ap, + LocalCallContext localCC, Configuration config +) { + flowFwd(node, cc, argAp, apf, ap, config) and + localFlowEntry(node, config) and + localCC = getLocalCallContext(cc, node.getEnclosingCallable()) +} + pragma[nomagic] private predicate flowFwdStore( - Node node, TypedContent tc, AccessPath ap0, AccessPathFront apf, boolean fromArg, + Node node, TypedContent tc, AccessPath ap0, AccessPathFront apf, CallContext cc, AccessPathOption argAp, Configuration config ) { exists(Node mid, AccessPathFront apf0 | - flowFwd(mid, fromArg, argAp, apf0, ap0, config) and + flowFwd(mid, cc, argAp, apf0, ap0, config) and flowFwdStore0(mid, tc, node, apf0, apf, config) ) } @@ -1764,20 +1775,20 @@ private predicate flowFwdStore0( pragma[nomagic] private predicate flowFwdRead0( - Node node1, TypedContent tc, AccessPathFrontHead apf0, AccessPath ap0, Node node2, - boolean fromArg, AccessPathOption argAp, Configuration config + Node node1, TypedContent tc, AccessPathFrontHead apf0, AccessPath ap0, Node node2, CallContext cc, + AccessPathOption argAp, Configuration config ) { - flowFwd(node1, fromArg, argAp, apf0, ap0, config) and + flowFwd(node1, cc, argAp, apf0, ap0, config) and readCandFwd(node1, tc, apf0, node2, config) } pragma[nomagic] private predicate flowFwdRead( - Node node, AccessPathFrontHead apf0, AccessPath ap0, AccessPathFront apf, boolean fromArg, + Node node, AccessPathFrontHead apf0, AccessPath ap0, AccessPathFront apf, CallContext cc, AccessPathOption argAp, Configuration config ) { exists(Node mid, TypedContent tc | - flowFwdRead0(mid, tc, apf0, ap0, node, fromArg, argAp, config) and + flowFwdRead0(mid, tc, apf0, ap0, node, cc, argAp, config) and flowCand(node, _, _, apf, unbind(config)) and flowCandConsCand(tc, apf, unbind(config)) ) @@ -1795,13 +1806,16 @@ private predicate flowFwdConsCand( pragma[nomagic] private predicate flowFwdIn( - DataFlowCall call, ParameterNode p, boolean fromArg, AccessPathOption argAp, AccessPathFront apf, - AccessPath ap, Configuration config + DataFlowCall call, ParameterNode p, CallContext outercc, CallContext innercc, + AccessPathOption argAp, AccessPathFront apf, AccessPath ap, Configuration config ) { - exists(ArgumentNode arg, boolean allowsFieldFlow | - flowFwd(arg, fromArg, argAp, apf, ap, config) and + exists(ArgumentNode arg, boolean allowsFieldFlow, DataFlowCallable c | + flowFwd(arg, outercc, argAp, apf, ap, config) and flowIntoCallNodeCand2(call, arg, p, allowsFieldFlow, config) and - flowCand(p, _, _, _, unbind(config)) + c = p.getEnclosingCallable() and + c = resolveCall(call, outercc) and + flowCand(p, _, _, _, unbind(config)) and + if recordDataFlowCallSite(call, c) then innercc = TSpecificCall(call) else innercc = TSomeCall() | ap instanceof AccessPathNil or allowsFieldFlow = true ) @@ -1809,13 +1823,19 @@ private predicate flowFwdIn( pragma[nomagic] private predicate flowFwdOut( - DataFlowCall call, Node node, boolean fromArg, AccessPathOption argAp, AccessPathFront apf, - AccessPath ap, Configuration config + DataFlowCall call, Node node, CallContext innercc, DataFlowCallable innerc, + AccessPathOption argAp, AccessPathFront apf, AccessPath ap, Configuration config ) { exists(ReturnNodeExt ret, boolean allowsFieldFlow | - flowFwd(ret, fromArg, argAp, apf, ap, config) and + flowFwd(ret, innercc, argAp, apf, ap, config) and flowOutOfCallNodeCand2(call, ret, node, allowsFieldFlow, config) and - flowCand(node, _, _, _, unbind(config)) + innerc = ret.getEnclosingCallable() and + flowCand(node, _, _, _, unbind(config)) and + ( + resolveReturn(innercc, innerc, call) + or + innercc.(CallContextCall).matchesCall(call) + ) | ap instanceof AccessPathNil or allowsFieldFlow = true ) @@ -1826,7 +1846,7 @@ private predicate flowFwdOutFromArg( DataFlowCall call, Node node, AccessPath argAp, AccessPathFront apf, AccessPath ap, Configuration config ) { - flowFwdOut(call, node, true, TAccessPathSome(argAp), apf, ap, config) + flowFwdOut(call, node, any(CallContextCall ccc), _, TAccessPathSome(argAp), apf, ap, config) } /** @@ -1834,10 +1854,10 @@ private predicate flowFwdOutFromArg( */ pragma[nomagic] private predicate flowFwdIsEntered( - DataFlowCall call, boolean fromArg, AccessPathOption argAp, AccessPath ap, Configuration config + DataFlowCall call, CallContext cc, AccessPathOption argAp, AccessPath ap, Configuration config ) { exists(ParameterNode p, AccessPathFront apf | - flowFwdIn(call, p, fromArg, argAp, apf, ap, config) and + flowFwdIn(call, p, cc, _, argAp, apf, ap, config) and flowCand(p, true, TAccessPathFrontSome(_), apf, config) ) } @@ -1920,7 +1940,7 @@ private predicate flow0( // flow out of a callable flowOut(_, node, _, _, ap, config) and toReturn = true and - if flowFwd(node, true, TAccessPathSome(_), _, ap, config) + if flowFwd(node, any(CallContextCall ccc), TAccessPathSome(_), _, ap, config) then returnAp = TAccessPathSome(ap) else returnAp = TAccessPathNone() } @@ -2006,9 +2026,10 @@ private predicate flowIsReturned( DataFlowCall call, boolean toReturn, AccessPathOption returnAp, AccessPath ap, Configuration config ) { - exists(ReturnNodeExt ret | + exists(ReturnNodeExt ret, CallContextCall ccc | flowOut(call, ret, toReturn, returnAp, ap, config) and - flowFwd(ret, true, TAccessPathSome(_), _, ap, config) + flowFwd(ret, ccc, TAccessPathSome(_), _, ap, config) and + ccc.matchesCall(call) ) } @@ -2031,7 +2052,7 @@ private newtype TSummaryCtx = exists(ReturnNodeExt ret, Configuration config, AccessPath ap0 | parameterFlow(p, ap, ret.getEnclosingCallable(), config) and flow(ret, true, TAccessPathSome(_), ap0, config) and - flowFwd(ret, true, TAccessPathSome(ap), _, ap0, config) + flowFwd(ret, any(CallContextCall ccc), TAccessPathSome(ap), _, ap0, config) ) } @@ -2352,7 +2373,7 @@ private predicate pathOutOfCallable0( ) { pos = getReturnPosition(mid.getNode()) and innercc = mid.getCallContext() and - not innercc instanceof CallContextCall and + innercc instanceof CallContextNoCall and ap = mid.getAp() and config = mid.getConfiguration() } @@ -2867,7 +2888,7 @@ private module FlowExploration { ) { pos = getReturnPosition(mid.getNode()) and innercc = mid.getCallContext() and - not innercc instanceof CallContextCall and + innercc instanceof CallContextNoCall and ap = mid.getAp() and config = mid.getConfiguration() } diff --git a/cpp/ql/src/semmle/code/cpp/dataflow/internal/DataFlowImpl3.qll b/cpp/ql/src/semmle/code/cpp/dataflow/internal/DataFlowImpl3.qll index 5042dce683f..8c210edbe5f 100644 --- a/cpp/ql/src/semmle/code/cpp/dataflow/internal/DataFlowImpl3.qll +++ b/cpp/ql/src/semmle/code/cpp/dataflow/internal/DataFlowImpl3.qll @@ -1066,7 +1066,7 @@ private module LocalFlowBigStep { * Holds if `node` can be the first node in a maximal subsequence of local * flow steps in a dataflow path. */ - private predicate localFlowEntry(Node node, Configuration config) { + predicate localFlowEntry(Node node, Configuration config) { nodeCand2(node, config) and ( config.isSource(node) or @@ -1650,53 +1650,53 @@ private class AccessPathOption extends TAccessPathOption { * Holds if `node` is reachable with access path `ap` from a source in * the configuration `config`. * - * The Boolean `fromArg` records whether the node is reached through an + * 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. */ private predicate flowFwd( - Node node, boolean fromArg, AccessPathOption argAp, AccessPathFront apf, AccessPath ap, + Node node, CallContext cc, AccessPathOption argAp, AccessPathFront apf, AccessPath ap, Configuration config ) { - flowFwd0(node, fromArg, argAp, apf, ap, config) and + flowFwd0(node, cc, argAp, apf, ap, config) and flowCand(node, _, _, apf, config) } private predicate flowFwd0( - Node node, boolean fromArg, AccessPathOption argAp, AccessPathFront apf, AccessPath ap, + Node node, CallContext cc, AccessPathOption argAp, AccessPathFront apf, AccessPath ap, Configuration config ) { flowCand(node, _, _, _, config) and config.isSource(node) and - fromArg = false and + cc instanceof CallContextAny and argAp = TAccessPathNone() and ap = TNil(getNodeType(node)) and apf = ap.(AccessPathNil).getFront() or flowCand(node, _, _, _, unbind(config)) and ( - exists(Node mid | - flowFwd(mid, fromArg, argAp, apf, ap, config) and - localFlowBigStep(mid, node, true, _, config, _) + exists(Node mid, LocalCallContext localCC | + flowFwdLocalEntry(mid, cc, argAp, apf, ap, localCC, config) and + localFlowBigStep(mid, node, true, _, config, localCC) ) or - exists(Node mid, AccessPathNil nil | - flowFwd(mid, fromArg, argAp, _, nil, config) and - localFlowBigStep(mid, node, false, apf, config, _) and + exists(Node mid, AccessPathNil nil, LocalCallContext localCC | + flowFwdLocalEntry(mid, cc, argAp, _, nil, localCC, config) and + localFlowBigStep(mid, node, false, apf, config, localCC) and apf = ap.(AccessPathNil).getFront() ) or exists(Node mid | flowFwd(mid, _, _, apf, ap, config) and jumpStep(mid, node, config) and - fromArg = false and + cc instanceof CallContextAny and argAp = TAccessPathNone() ) or exists(Node mid, AccessPathNil nil | flowFwd(mid, _, _, _, nil, config) and additionalJumpStep(mid, node, config) and - fromArg = false and + cc instanceof CallContextAny and argAp = TAccessPathNone() and ap = TNil(getNodeType(node)) and apf = ap.(AccessPathNil).getFront() @@ -1704,40 +1704,51 @@ private predicate flowFwd0( ) or // store - exists(TypedContent tc | flowFwdStore(node, tc, pop(tc, ap), apf, fromArg, argAp, config)) + exists(TypedContent tc | flowFwdStore(node, tc, pop(tc, ap), apf, cc, argAp, config)) or // read exists(TypedContent tc | - flowFwdRead(node, _, push(tc, ap), apf, fromArg, argAp, config) and + flowFwdRead(node, _, push(tc, ap), apf, cc, argAp, config) and flowFwdConsCand(tc, apf, ap, config) ) or // flow into a callable - flowFwdIn(_, node, _, _, apf, ap, config) and - fromArg = true and + flowFwdIn(_, node, _, cc, _, apf, ap, config) and if flowCand(node, true, _, apf, config) then argAp = TAccessPathSome(ap) else argAp = TAccessPathNone() or // flow out of a callable exists(DataFlowCall call | - flowFwdOut(call, node, fromArg, argAp, apf, ap, config) and - fromArg = false + exists(DataFlowCallable c | + flowFwdOut(call, node, any(CallContextNoCall innercc), c, argAp, apf, ap, config) and + if reducedViableImplInReturn(c, call) then cc = TReturn(c, call) else cc = TAnyCallContext() + ) or exists(AccessPath argAp0 | flowFwdOutFromArg(call, node, argAp0, apf, ap, config) and - flowFwdIsEntered(call, fromArg, argAp, argAp0, config) + flowFwdIsEntered(call, cc, argAp, argAp0, config) ) ) } +pragma[nomagic] +private predicate flowFwdLocalEntry( + Node node, CallContext cc, AccessPathOption argAp, AccessPathFront apf, AccessPath ap, + LocalCallContext localCC, Configuration config +) { + flowFwd(node, cc, argAp, apf, ap, config) and + localFlowEntry(node, config) and + localCC = getLocalCallContext(cc, node.getEnclosingCallable()) +} + pragma[nomagic] private predicate flowFwdStore( - Node node, TypedContent tc, AccessPath ap0, AccessPathFront apf, boolean fromArg, + Node node, TypedContent tc, AccessPath ap0, AccessPathFront apf, CallContext cc, AccessPathOption argAp, Configuration config ) { exists(Node mid, AccessPathFront apf0 | - flowFwd(mid, fromArg, argAp, apf0, ap0, config) and + flowFwd(mid, cc, argAp, apf0, ap0, config) and flowFwdStore0(mid, tc, node, apf0, apf, config) ) } @@ -1764,20 +1775,20 @@ private predicate flowFwdStore0( pragma[nomagic] private predicate flowFwdRead0( - Node node1, TypedContent tc, AccessPathFrontHead apf0, AccessPath ap0, Node node2, - boolean fromArg, AccessPathOption argAp, Configuration config + Node node1, TypedContent tc, AccessPathFrontHead apf0, AccessPath ap0, Node node2, CallContext cc, + AccessPathOption argAp, Configuration config ) { - flowFwd(node1, fromArg, argAp, apf0, ap0, config) and + flowFwd(node1, cc, argAp, apf0, ap0, config) and readCandFwd(node1, tc, apf0, node2, config) } pragma[nomagic] private predicate flowFwdRead( - Node node, AccessPathFrontHead apf0, AccessPath ap0, AccessPathFront apf, boolean fromArg, + Node node, AccessPathFrontHead apf0, AccessPath ap0, AccessPathFront apf, CallContext cc, AccessPathOption argAp, Configuration config ) { exists(Node mid, TypedContent tc | - flowFwdRead0(mid, tc, apf0, ap0, node, fromArg, argAp, config) and + flowFwdRead0(mid, tc, apf0, ap0, node, cc, argAp, config) and flowCand(node, _, _, apf, unbind(config)) and flowCandConsCand(tc, apf, unbind(config)) ) @@ -1795,13 +1806,16 @@ private predicate flowFwdConsCand( pragma[nomagic] private predicate flowFwdIn( - DataFlowCall call, ParameterNode p, boolean fromArg, AccessPathOption argAp, AccessPathFront apf, - AccessPath ap, Configuration config + DataFlowCall call, ParameterNode p, CallContext outercc, CallContext innercc, + AccessPathOption argAp, AccessPathFront apf, AccessPath ap, Configuration config ) { - exists(ArgumentNode arg, boolean allowsFieldFlow | - flowFwd(arg, fromArg, argAp, apf, ap, config) and + exists(ArgumentNode arg, boolean allowsFieldFlow, DataFlowCallable c | + flowFwd(arg, outercc, argAp, apf, ap, config) and flowIntoCallNodeCand2(call, arg, p, allowsFieldFlow, config) and - flowCand(p, _, _, _, unbind(config)) + c = p.getEnclosingCallable() and + c = resolveCall(call, outercc) and + flowCand(p, _, _, _, unbind(config)) and + if recordDataFlowCallSite(call, c) then innercc = TSpecificCall(call) else innercc = TSomeCall() | ap instanceof AccessPathNil or allowsFieldFlow = true ) @@ -1809,13 +1823,19 @@ private predicate flowFwdIn( pragma[nomagic] private predicate flowFwdOut( - DataFlowCall call, Node node, boolean fromArg, AccessPathOption argAp, AccessPathFront apf, - AccessPath ap, Configuration config + DataFlowCall call, Node node, CallContext innercc, DataFlowCallable innerc, + AccessPathOption argAp, AccessPathFront apf, AccessPath ap, Configuration config ) { exists(ReturnNodeExt ret, boolean allowsFieldFlow | - flowFwd(ret, fromArg, argAp, apf, ap, config) and + flowFwd(ret, innercc, argAp, apf, ap, config) and flowOutOfCallNodeCand2(call, ret, node, allowsFieldFlow, config) and - flowCand(node, _, _, _, unbind(config)) + innerc = ret.getEnclosingCallable() and + flowCand(node, _, _, _, unbind(config)) and + ( + resolveReturn(innercc, innerc, call) + or + innercc.(CallContextCall).matchesCall(call) + ) | ap instanceof AccessPathNil or allowsFieldFlow = true ) @@ -1826,7 +1846,7 @@ private predicate flowFwdOutFromArg( DataFlowCall call, Node node, AccessPath argAp, AccessPathFront apf, AccessPath ap, Configuration config ) { - flowFwdOut(call, node, true, TAccessPathSome(argAp), apf, ap, config) + flowFwdOut(call, node, any(CallContextCall ccc), _, TAccessPathSome(argAp), apf, ap, config) } /** @@ -1834,10 +1854,10 @@ private predicate flowFwdOutFromArg( */ pragma[nomagic] private predicate flowFwdIsEntered( - DataFlowCall call, boolean fromArg, AccessPathOption argAp, AccessPath ap, Configuration config + DataFlowCall call, CallContext cc, AccessPathOption argAp, AccessPath ap, Configuration config ) { exists(ParameterNode p, AccessPathFront apf | - flowFwdIn(call, p, fromArg, argAp, apf, ap, config) and + flowFwdIn(call, p, cc, _, argAp, apf, ap, config) and flowCand(p, true, TAccessPathFrontSome(_), apf, config) ) } @@ -1920,7 +1940,7 @@ private predicate flow0( // flow out of a callable flowOut(_, node, _, _, ap, config) and toReturn = true and - if flowFwd(node, true, TAccessPathSome(_), _, ap, config) + if flowFwd(node, any(CallContextCall ccc), TAccessPathSome(_), _, ap, config) then returnAp = TAccessPathSome(ap) else returnAp = TAccessPathNone() } @@ -2006,9 +2026,10 @@ private predicate flowIsReturned( DataFlowCall call, boolean toReturn, AccessPathOption returnAp, AccessPath ap, Configuration config ) { - exists(ReturnNodeExt ret | + exists(ReturnNodeExt ret, CallContextCall ccc | flowOut(call, ret, toReturn, returnAp, ap, config) and - flowFwd(ret, true, TAccessPathSome(_), _, ap, config) + flowFwd(ret, ccc, TAccessPathSome(_), _, ap, config) and + ccc.matchesCall(call) ) } @@ -2031,7 +2052,7 @@ private newtype TSummaryCtx = exists(ReturnNodeExt ret, Configuration config, AccessPath ap0 | parameterFlow(p, ap, ret.getEnclosingCallable(), config) and flow(ret, true, TAccessPathSome(_), ap0, config) and - flowFwd(ret, true, TAccessPathSome(ap), _, ap0, config) + flowFwd(ret, any(CallContextCall ccc), TAccessPathSome(ap), _, ap0, config) ) } @@ -2352,7 +2373,7 @@ private predicate pathOutOfCallable0( ) { pos = getReturnPosition(mid.getNode()) and innercc = mid.getCallContext() and - not innercc instanceof CallContextCall and + innercc instanceof CallContextNoCall and ap = mid.getAp() and config = mid.getConfiguration() } @@ -2867,7 +2888,7 @@ private module FlowExploration { ) { pos = getReturnPosition(mid.getNode()) and innercc = mid.getCallContext() and - not innercc instanceof CallContextCall and + innercc instanceof CallContextNoCall and ap = mid.getAp() and config = mid.getConfiguration() } diff --git a/cpp/ql/src/semmle/code/cpp/dataflow/internal/DataFlowImpl4.qll b/cpp/ql/src/semmle/code/cpp/dataflow/internal/DataFlowImpl4.qll index 5042dce683f..8c210edbe5f 100644 --- a/cpp/ql/src/semmle/code/cpp/dataflow/internal/DataFlowImpl4.qll +++ b/cpp/ql/src/semmle/code/cpp/dataflow/internal/DataFlowImpl4.qll @@ -1066,7 +1066,7 @@ private module LocalFlowBigStep { * Holds if `node` can be the first node in a maximal subsequence of local * flow steps in a dataflow path. */ - private predicate localFlowEntry(Node node, Configuration config) { + predicate localFlowEntry(Node node, Configuration config) { nodeCand2(node, config) and ( config.isSource(node) or @@ -1650,53 +1650,53 @@ private class AccessPathOption extends TAccessPathOption { * Holds if `node` is reachable with access path `ap` from a source in * the configuration `config`. * - * The Boolean `fromArg` records whether the node is reached through an + * 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. */ private predicate flowFwd( - Node node, boolean fromArg, AccessPathOption argAp, AccessPathFront apf, AccessPath ap, + Node node, CallContext cc, AccessPathOption argAp, AccessPathFront apf, AccessPath ap, Configuration config ) { - flowFwd0(node, fromArg, argAp, apf, ap, config) and + flowFwd0(node, cc, argAp, apf, ap, config) and flowCand(node, _, _, apf, config) } private predicate flowFwd0( - Node node, boolean fromArg, AccessPathOption argAp, AccessPathFront apf, AccessPath ap, + Node node, CallContext cc, AccessPathOption argAp, AccessPathFront apf, AccessPath ap, Configuration config ) { flowCand(node, _, _, _, config) and config.isSource(node) and - fromArg = false and + cc instanceof CallContextAny and argAp = TAccessPathNone() and ap = TNil(getNodeType(node)) and apf = ap.(AccessPathNil).getFront() or flowCand(node, _, _, _, unbind(config)) and ( - exists(Node mid | - flowFwd(mid, fromArg, argAp, apf, ap, config) and - localFlowBigStep(mid, node, true, _, config, _) + exists(Node mid, LocalCallContext localCC | + flowFwdLocalEntry(mid, cc, argAp, apf, ap, localCC, config) and + localFlowBigStep(mid, node, true, _, config, localCC) ) or - exists(Node mid, AccessPathNil nil | - flowFwd(mid, fromArg, argAp, _, nil, config) and - localFlowBigStep(mid, node, false, apf, config, _) and + exists(Node mid, AccessPathNil nil, LocalCallContext localCC | + flowFwdLocalEntry(mid, cc, argAp, _, nil, localCC, config) and + localFlowBigStep(mid, node, false, apf, config, localCC) and apf = ap.(AccessPathNil).getFront() ) or exists(Node mid | flowFwd(mid, _, _, apf, ap, config) and jumpStep(mid, node, config) and - fromArg = false and + cc instanceof CallContextAny and argAp = TAccessPathNone() ) or exists(Node mid, AccessPathNil nil | flowFwd(mid, _, _, _, nil, config) and additionalJumpStep(mid, node, config) and - fromArg = false and + cc instanceof CallContextAny and argAp = TAccessPathNone() and ap = TNil(getNodeType(node)) and apf = ap.(AccessPathNil).getFront() @@ -1704,40 +1704,51 @@ private predicate flowFwd0( ) or // store - exists(TypedContent tc | flowFwdStore(node, tc, pop(tc, ap), apf, fromArg, argAp, config)) + exists(TypedContent tc | flowFwdStore(node, tc, pop(tc, ap), apf, cc, argAp, config)) or // read exists(TypedContent tc | - flowFwdRead(node, _, push(tc, ap), apf, fromArg, argAp, config) and + flowFwdRead(node, _, push(tc, ap), apf, cc, argAp, config) and flowFwdConsCand(tc, apf, ap, config) ) or // flow into a callable - flowFwdIn(_, node, _, _, apf, ap, config) and - fromArg = true and + flowFwdIn(_, node, _, cc, _, apf, ap, config) and if flowCand(node, true, _, apf, config) then argAp = TAccessPathSome(ap) else argAp = TAccessPathNone() or // flow out of a callable exists(DataFlowCall call | - flowFwdOut(call, node, fromArg, argAp, apf, ap, config) and - fromArg = false + exists(DataFlowCallable c | + flowFwdOut(call, node, any(CallContextNoCall innercc), c, argAp, apf, ap, config) and + if reducedViableImplInReturn(c, call) then cc = TReturn(c, call) else cc = TAnyCallContext() + ) or exists(AccessPath argAp0 | flowFwdOutFromArg(call, node, argAp0, apf, ap, config) and - flowFwdIsEntered(call, fromArg, argAp, argAp0, config) + flowFwdIsEntered(call, cc, argAp, argAp0, config) ) ) } +pragma[nomagic] +private predicate flowFwdLocalEntry( + Node node, CallContext cc, AccessPathOption argAp, AccessPathFront apf, AccessPath ap, + LocalCallContext localCC, Configuration config +) { + flowFwd(node, cc, argAp, apf, ap, config) and + localFlowEntry(node, config) and + localCC = getLocalCallContext(cc, node.getEnclosingCallable()) +} + pragma[nomagic] private predicate flowFwdStore( - Node node, TypedContent tc, AccessPath ap0, AccessPathFront apf, boolean fromArg, + Node node, TypedContent tc, AccessPath ap0, AccessPathFront apf, CallContext cc, AccessPathOption argAp, Configuration config ) { exists(Node mid, AccessPathFront apf0 | - flowFwd(mid, fromArg, argAp, apf0, ap0, config) and + flowFwd(mid, cc, argAp, apf0, ap0, config) and flowFwdStore0(mid, tc, node, apf0, apf, config) ) } @@ -1764,20 +1775,20 @@ private predicate flowFwdStore0( pragma[nomagic] private predicate flowFwdRead0( - Node node1, TypedContent tc, AccessPathFrontHead apf0, AccessPath ap0, Node node2, - boolean fromArg, AccessPathOption argAp, Configuration config + Node node1, TypedContent tc, AccessPathFrontHead apf0, AccessPath ap0, Node node2, CallContext cc, + AccessPathOption argAp, Configuration config ) { - flowFwd(node1, fromArg, argAp, apf0, ap0, config) and + flowFwd(node1, cc, argAp, apf0, ap0, config) and readCandFwd(node1, tc, apf0, node2, config) } pragma[nomagic] private predicate flowFwdRead( - Node node, AccessPathFrontHead apf0, AccessPath ap0, AccessPathFront apf, boolean fromArg, + Node node, AccessPathFrontHead apf0, AccessPath ap0, AccessPathFront apf, CallContext cc, AccessPathOption argAp, Configuration config ) { exists(Node mid, TypedContent tc | - flowFwdRead0(mid, tc, apf0, ap0, node, fromArg, argAp, config) and + flowFwdRead0(mid, tc, apf0, ap0, node, cc, argAp, config) and flowCand(node, _, _, apf, unbind(config)) and flowCandConsCand(tc, apf, unbind(config)) ) @@ -1795,13 +1806,16 @@ private predicate flowFwdConsCand( pragma[nomagic] private predicate flowFwdIn( - DataFlowCall call, ParameterNode p, boolean fromArg, AccessPathOption argAp, AccessPathFront apf, - AccessPath ap, Configuration config + DataFlowCall call, ParameterNode p, CallContext outercc, CallContext innercc, + AccessPathOption argAp, AccessPathFront apf, AccessPath ap, Configuration config ) { - exists(ArgumentNode arg, boolean allowsFieldFlow | - flowFwd(arg, fromArg, argAp, apf, ap, config) and + exists(ArgumentNode arg, boolean allowsFieldFlow, DataFlowCallable c | + flowFwd(arg, outercc, argAp, apf, ap, config) and flowIntoCallNodeCand2(call, arg, p, allowsFieldFlow, config) and - flowCand(p, _, _, _, unbind(config)) + c = p.getEnclosingCallable() and + c = resolveCall(call, outercc) and + flowCand(p, _, _, _, unbind(config)) and + if recordDataFlowCallSite(call, c) then innercc = TSpecificCall(call) else innercc = TSomeCall() | ap instanceof AccessPathNil or allowsFieldFlow = true ) @@ -1809,13 +1823,19 @@ private predicate flowFwdIn( pragma[nomagic] private predicate flowFwdOut( - DataFlowCall call, Node node, boolean fromArg, AccessPathOption argAp, AccessPathFront apf, - AccessPath ap, Configuration config + DataFlowCall call, Node node, CallContext innercc, DataFlowCallable innerc, + AccessPathOption argAp, AccessPathFront apf, AccessPath ap, Configuration config ) { exists(ReturnNodeExt ret, boolean allowsFieldFlow | - flowFwd(ret, fromArg, argAp, apf, ap, config) and + flowFwd(ret, innercc, argAp, apf, ap, config) and flowOutOfCallNodeCand2(call, ret, node, allowsFieldFlow, config) and - flowCand(node, _, _, _, unbind(config)) + innerc = ret.getEnclosingCallable() and + flowCand(node, _, _, _, unbind(config)) and + ( + resolveReturn(innercc, innerc, call) + or + innercc.(CallContextCall).matchesCall(call) + ) | ap instanceof AccessPathNil or allowsFieldFlow = true ) @@ -1826,7 +1846,7 @@ private predicate flowFwdOutFromArg( DataFlowCall call, Node node, AccessPath argAp, AccessPathFront apf, AccessPath ap, Configuration config ) { - flowFwdOut(call, node, true, TAccessPathSome(argAp), apf, ap, config) + flowFwdOut(call, node, any(CallContextCall ccc), _, TAccessPathSome(argAp), apf, ap, config) } /** @@ -1834,10 +1854,10 @@ private predicate flowFwdOutFromArg( */ pragma[nomagic] private predicate flowFwdIsEntered( - DataFlowCall call, boolean fromArg, AccessPathOption argAp, AccessPath ap, Configuration config + DataFlowCall call, CallContext cc, AccessPathOption argAp, AccessPath ap, Configuration config ) { exists(ParameterNode p, AccessPathFront apf | - flowFwdIn(call, p, fromArg, argAp, apf, ap, config) and + flowFwdIn(call, p, cc, _, argAp, apf, ap, config) and flowCand(p, true, TAccessPathFrontSome(_), apf, config) ) } @@ -1920,7 +1940,7 @@ private predicate flow0( // flow out of a callable flowOut(_, node, _, _, ap, config) and toReturn = true and - if flowFwd(node, true, TAccessPathSome(_), _, ap, config) + if flowFwd(node, any(CallContextCall ccc), TAccessPathSome(_), _, ap, config) then returnAp = TAccessPathSome(ap) else returnAp = TAccessPathNone() } @@ -2006,9 +2026,10 @@ private predicate flowIsReturned( DataFlowCall call, boolean toReturn, AccessPathOption returnAp, AccessPath ap, Configuration config ) { - exists(ReturnNodeExt ret | + exists(ReturnNodeExt ret, CallContextCall ccc | flowOut(call, ret, toReturn, returnAp, ap, config) and - flowFwd(ret, true, TAccessPathSome(_), _, ap, config) + flowFwd(ret, ccc, TAccessPathSome(_), _, ap, config) and + ccc.matchesCall(call) ) } @@ -2031,7 +2052,7 @@ private newtype TSummaryCtx = exists(ReturnNodeExt ret, Configuration config, AccessPath ap0 | parameterFlow(p, ap, ret.getEnclosingCallable(), config) and flow(ret, true, TAccessPathSome(_), ap0, config) and - flowFwd(ret, true, TAccessPathSome(ap), _, ap0, config) + flowFwd(ret, any(CallContextCall ccc), TAccessPathSome(ap), _, ap0, config) ) } @@ -2352,7 +2373,7 @@ private predicate pathOutOfCallable0( ) { pos = getReturnPosition(mid.getNode()) and innercc = mid.getCallContext() and - not innercc instanceof CallContextCall and + innercc instanceof CallContextNoCall and ap = mid.getAp() and config = mid.getConfiguration() } @@ -2867,7 +2888,7 @@ private module FlowExploration { ) { pos = getReturnPosition(mid.getNode()) and innercc = mid.getCallContext() and - not innercc instanceof CallContextCall and + innercc instanceof CallContextNoCall and ap = mid.getAp() and config = mid.getConfiguration() } diff --git a/cpp/ql/src/semmle/code/cpp/dataflow/internal/DataFlowImplCommon.qll b/cpp/ql/src/semmle/code/cpp/dataflow/internal/DataFlowImplCommon.qll index 27ab1d01feb..892250f44bb 100644 --- a/cpp/ql/src/semmle/code/cpp/dataflow/internal/DataFlowImplCommon.qll +++ b/cpp/ql/src/semmle/code/cpp/dataflow/internal/DataFlowImplCommon.qll @@ -512,13 +512,19 @@ abstract class CallContext extends TCallContext { abstract predicate relevantFor(DataFlowCallable callable); } -class CallContextAny extends CallContext, TAnyCallContext { +abstract class CallContextNoCall extends CallContext { } + +class CallContextAny extends CallContextNoCall, TAnyCallContext { override string toString() { result = "CcAny" } override predicate relevantFor(DataFlowCallable callable) { any() } } -abstract class CallContextCall extends CallContext { } +abstract class CallContextCall extends CallContext { + /** Holds if this call context may be `call`. */ + bindingset[call] + abstract predicate matchesCall(DataFlowCall call); +} class CallContextSpecificCall extends CallContextCall, TSpecificCall { override string toString() { @@ -529,6 +535,8 @@ class CallContextSpecificCall extends CallContextCall, TSpecificCall { recordDataFlowCallSite(getCall(), callable) } + override predicate matchesCall(DataFlowCall call) { call = this.getCall() } + DataFlowCall getCall() { this = TSpecificCall(result) } } @@ -538,9 +546,11 @@ class CallContextSomeCall extends CallContextCall, TSomeCall { override predicate relevantFor(DataFlowCallable callable) { exists(ParameterNode p | p.getEnclosingCallable() = callable) } + + override predicate matchesCall(DataFlowCall call) { any() } } -class CallContextReturn extends CallContext, TReturn { +class CallContextReturn extends CallContextNoCall, TReturn { override string toString() { exists(DataFlowCall call | this = TReturn(_, call) | result = "CcReturn(" + call + ")") } diff --git a/cpp/ql/src/semmle/code/cpp/dataflow/internal/DataFlowImplLocal.qll b/cpp/ql/src/semmle/code/cpp/dataflow/internal/DataFlowImplLocal.qll index 5042dce683f..8c210edbe5f 100644 --- a/cpp/ql/src/semmle/code/cpp/dataflow/internal/DataFlowImplLocal.qll +++ b/cpp/ql/src/semmle/code/cpp/dataflow/internal/DataFlowImplLocal.qll @@ -1066,7 +1066,7 @@ private module LocalFlowBigStep { * Holds if `node` can be the first node in a maximal subsequence of local * flow steps in a dataflow path. */ - private predicate localFlowEntry(Node node, Configuration config) { + predicate localFlowEntry(Node node, Configuration config) { nodeCand2(node, config) and ( config.isSource(node) or @@ -1650,53 +1650,53 @@ private class AccessPathOption extends TAccessPathOption { * Holds if `node` is reachable with access path `ap` from a source in * the configuration `config`. * - * The Boolean `fromArg` records whether the node is reached through an + * 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. */ private predicate flowFwd( - Node node, boolean fromArg, AccessPathOption argAp, AccessPathFront apf, AccessPath ap, + Node node, CallContext cc, AccessPathOption argAp, AccessPathFront apf, AccessPath ap, Configuration config ) { - flowFwd0(node, fromArg, argAp, apf, ap, config) and + flowFwd0(node, cc, argAp, apf, ap, config) and flowCand(node, _, _, apf, config) } private predicate flowFwd0( - Node node, boolean fromArg, AccessPathOption argAp, AccessPathFront apf, AccessPath ap, + Node node, CallContext cc, AccessPathOption argAp, AccessPathFront apf, AccessPath ap, Configuration config ) { flowCand(node, _, _, _, config) and config.isSource(node) and - fromArg = false and + cc instanceof CallContextAny and argAp = TAccessPathNone() and ap = TNil(getNodeType(node)) and apf = ap.(AccessPathNil).getFront() or flowCand(node, _, _, _, unbind(config)) and ( - exists(Node mid | - flowFwd(mid, fromArg, argAp, apf, ap, config) and - localFlowBigStep(mid, node, true, _, config, _) + exists(Node mid, LocalCallContext localCC | + flowFwdLocalEntry(mid, cc, argAp, apf, ap, localCC, config) and + localFlowBigStep(mid, node, true, _, config, localCC) ) or - exists(Node mid, AccessPathNil nil | - flowFwd(mid, fromArg, argAp, _, nil, config) and - localFlowBigStep(mid, node, false, apf, config, _) and + exists(Node mid, AccessPathNil nil, LocalCallContext localCC | + flowFwdLocalEntry(mid, cc, argAp, _, nil, localCC, config) and + localFlowBigStep(mid, node, false, apf, config, localCC) and apf = ap.(AccessPathNil).getFront() ) or exists(Node mid | flowFwd(mid, _, _, apf, ap, config) and jumpStep(mid, node, config) and - fromArg = false and + cc instanceof CallContextAny and argAp = TAccessPathNone() ) or exists(Node mid, AccessPathNil nil | flowFwd(mid, _, _, _, nil, config) and additionalJumpStep(mid, node, config) and - fromArg = false and + cc instanceof CallContextAny and argAp = TAccessPathNone() and ap = TNil(getNodeType(node)) and apf = ap.(AccessPathNil).getFront() @@ -1704,40 +1704,51 @@ private predicate flowFwd0( ) or // store - exists(TypedContent tc | flowFwdStore(node, tc, pop(tc, ap), apf, fromArg, argAp, config)) + exists(TypedContent tc | flowFwdStore(node, tc, pop(tc, ap), apf, cc, argAp, config)) or // read exists(TypedContent tc | - flowFwdRead(node, _, push(tc, ap), apf, fromArg, argAp, config) and + flowFwdRead(node, _, push(tc, ap), apf, cc, argAp, config) and flowFwdConsCand(tc, apf, ap, config) ) or // flow into a callable - flowFwdIn(_, node, _, _, apf, ap, config) and - fromArg = true and + flowFwdIn(_, node, _, cc, _, apf, ap, config) and if flowCand(node, true, _, apf, config) then argAp = TAccessPathSome(ap) else argAp = TAccessPathNone() or // flow out of a callable exists(DataFlowCall call | - flowFwdOut(call, node, fromArg, argAp, apf, ap, config) and - fromArg = false + exists(DataFlowCallable c | + flowFwdOut(call, node, any(CallContextNoCall innercc), c, argAp, apf, ap, config) and + if reducedViableImplInReturn(c, call) then cc = TReturn(c, call) else cc = TAnyCallContext() + ) or exists(AccessPath argAp0 | flowFwdOutFromArg(call, node, argAp0, apf, ap, config) and - flowFwdIsEntered(call, fromArg, argAp, argAp0, config) + flowFwdIsEntered(call, cc, argAp, argAp0, config) ) ) } +pragma[nomagic] +private predicate flowFwdLocalEntry( + Node node, CallContext cc, AccessPathOption argAp, AccessPathFront apf, AccessPath ap, + LocalCallContext localCC, Configuration config +) { + flowFwd(node, cc, argAp, apf, ap, config) and + localFlowEntry(node, config) and + localCC = getLocalCallContext(cc, node.getEnclosingCallable()) +} + pragma[nomagic] private predicate flowFwdStore( - Node node, TypedContent tc, AccessPath ap0, AccessPathFront apf, boolean fromArg, + Node node, TypedContent tc, AccessPath ap0, AccessPathFront apf, CallContext cc, AccessPathOption argAp, Configuration config ) { exists(Node mid, AccessPathFront apf0 | - flowFwd(mid, fromArg, argAp, apf0, ap0, config) and + flowFwd(mid, cc, argAp, apf0, ap0, config) and flowFwdStore0(mid, tc, node, apf0, apf, config) ) } @@ -1764,20 +1775,20 @@ private predicate flowFwdStore0( pragma[nomagic] private predicate flowFwdRead0( - Node node1, TypedContent tc, AccessPathFrontHead apf0, AccessPath ap0, Node node2, - boolean fromArg, AccessPathOption argAp, Configuration config + Node node1, TypedContent tc, AccessPathFrontHead apf0, AccessPath ap0, Node node2, CallContext cc, + AccessPathOption argAp, Configuration config ) { - flowFwd(node1, fromArg, argAp, apf0, ap0, config) and + flowFwd(node1, cc, argAp, apf0, ap0, config) and readCandFwd(node1, tc, apf0, node2, config) } pragma[nomagic] private predicate flowFwdRead( - Node node, AccessPathFrontHead apf0, AccessPath ap0, AccessPathFront apf, boolean fromArg, + Node node, AccessPathFrontHead apf0, AccessPath ap0, AccessPathFront apf, CallContext cc, AccessPathOption argAp, Configuration config ) { exists(Node mid, TypedContent tc | - flowFwdRead0(mid, tc, apf0, ap0, node, fromArg, argAp, config) and + flowFwdRead0(mid, tc, apf0, ap0, node, cc, argAp, config) and flowCand(node, _, _, apf, unbind(config)) and flowCandConsCand(tc, apf, unbind(config)) ) @@ -1795,13 +1806,16 @@ private predicate flowFwdConsCand( pragma[nomagic] private predicate flowFwdIn( - DataFlowCall call, ParameterNode p, boolean fromArg, AccessPathOption argAp, AccessPathFront apf, - AccessPath ap, Configuration config + DataFlowCall call, ParameterNode p, CallContext outercc, CallContext innercc, + AccessPathOption argAp, AccessPathFront apf, AccessPath ap, Configuration config ) { - exists(ArgumentNode arg, boolean allowsFieldFlow | - flowFwd(arg, fromArg, argAp, apf, ap, config) and + exists(ArgumentNode arg, boolean allowsFieldFlow, DataFlowCallable c | + flowFwd(arg, outercc, argAp, apf, ap, config) and flowIntoCallNodeCand2(call, arg, p, allowsFieldFlow, config) and - flowCand(p, _, _, _, unbind(config)) + c = p.getEnclosingCallable() and + c = resolveCall(call, outercc) and + flowCand(p, _, _, _, unbind(config)) and + if recordDataFlowCallSite(call, c) then innercc = TSpecificCall(call) else innercc = TSomeCall() | ap instanceof AccessPathNil or allowsFieldFlow = true ) @@ -1809,13 +1823,19 @@ private predicate flowFwdIn( pragma[nomagic] private predicate flowFwdOut( - DataFlowCall call, Node node, boolean fromArg, AccessPathOption argAp, AccessPathFront apf, - AccessPath ap, Configuration config + DataFlowCall call, Node node, CallContext innercc, DataFlowCallable innerc, + AccessPathOption argAp, AccessPathFront apf, AccessPath ap, Configuration config ) { exists(ReturnNodeExt ret, boolean allowsFieldFlow | - flowFwd(ret, fromArg, argAp, apf, ap, config) and + flowFwd(ret, innercc, argAp, apf, ap, config) and flowOutOfCallNodeCand2(call, ret, node, allowsFieldFlow, config) and - flowCand(node, _, _, _, unbind(config)) + innerc = ret.getEnclosingCallable() and + flowCand(node, _, _, _, unbind(config)) and + ( + resolveReturn(innercc, innerc, call) + or + innercc.(CallContextCall).matchesCall(call) + ) | ap instanceof AccessPathNil or allowsFieldFlow = true ) @@ -1826,7 +1846,7 @@ private predicate flowFwdOutFromArg( DataFlowCall call, Node node, AccessPath argAp, AccessPathFront apf, AccessPath ap, Configuration config ) { - flowFwdOut(call, node, true, TAccessPathSome(argAp), apf, ap, config) + flowFwdOut(call, node, any(CallContextCall ccc), _, TAccessPathSome(argAp), apf, ap, config) } /** @@ -1834,10 +1854,10 @@ private predicate flowFwdOutFromArg( */ pragma[nomagic] private predicate flowFwdIsEntered( - DataFlowCall call, boolean fromArg, AccessPathOption argAp, AccessPath ap, Configuration config + DataFlowCall call, CallContext cc, AccessPathOption argAp, AccessPath ap, Configuration config ) { exists(ParameterNode p, AccessPathFront apf | - flowFwdIn(call, p, fromArg, argAp, apf, ap, config) and + flowFwdIn(call, p, cc, _, argAp, apf, ap, config) and flowCand(p, true, TAccessPathFrontSome(_), apf, config) ) } @@ -1920,7 +1940,7 @@ private predicate flow0( // flow out of a callable flowOut(_, node, _, _, ap, config) and toReturn = true and - if flowFwd(node, true, TAccessPathSome(_), _, ap, config) + if flowFwd(node, any(CallContextCall ccc), TAccessPathSome(_), _, ap, config) then returnAp = TAccessPathSome(ap) else returnAp = TAccessPathNone() } @@ -2006,9 +2026,10 @@ private predicate flowIsReturned( DataFlowCall call, boolean toReturn, AccessPathOption returnAp, AccessPath ap, Configuration config ) { - exists(ReturnNodeExt ret | + exists(ReturnNodeExt ret, CallContextCall ccc | flowOut(call, ret, toReturn, returnAp, ap, config) and - flowFwd(ret, true, TAccessPathSome(_), _, ap, config) + flowFwd(ret, ccc, TAccessPathSome(_), _, ap, config) and + ccc.matchesCall(call) ) } @@ -2031,7 +2052,7 @@ private newtype TSummaryCtx = exists(ReturnNodeExt ret, Configuration config, AccessPath ap0 | parameterFlow(p, ap, ret.getEnclosingCallable(), config) and flow(ret, true, TAccessPathSome(_), ap0, config) and - flowFwd(ret, true, TAccessPathSome(ap), _, ap0, config) + flowFwd(ret, any(CallContextCall ccc), TAccessPathSome(ap), _, ap0, config) ) } @@ -2352,7 +2373,7 @@ private predicate pathOutOfCallable0( ) { pos = getReturnPosition(mid.getNode()) and innercc = mid.getCallContext() and - not innercc instanceof CallContextCall and + innercc instanceof CallContextNoCall and ap = mid.getAp() and config = mid.getConfiguration() } @@ -2867,7 +2888,7 @@ private module FlowExploration { ) { pos = getReturnPosition(mid.getNode()) and innercc = mid.getCallContext() and - not innercc instanceof CallContextCall and + innercc instanceof CallContextNoCall and ap = mid.getAp() and config = mid.getConfiguration() } diff --git a/cpp/ql/src/semmle/code/cpp/ir/dataflow/internal/DataFlowImpl.qll b/cpp/ql/src/semmle/code/cpp/ir/dataflow/internal/DataFlowImpl.qll index 5042dce683f..8c210edbe5f 100644 --- a/cpp/ql/src/semmle/code/cpp/ir/dataflow/internal/DataFlowImpl.qll +++ b/cpp/ql/src/semmle/code/cpp/ir/dataflow/internal/DataFlowImpl.qll @@ -1066,7 +1066,7 @@ private module LocalFlowBigStep { * Holds if `node` can be the first node in a maximal subsequence of local * flow steps in a dataflow path. */ - private predicate localFlowEntry(Node node, Configuration config) { + predicate localFlowEntry(Node node, Configuration config) { nodeCand2(node, config) and ( config.isSource(node) or @@ -1650,53 +1650,53 @@ private class AccessPathOption extends TAccessPathOption { * Holds if `node` is reachable with access path `ap` from a source in * the configuration `config`. * - * The Boolean `fromArg` records whether the node is reached through an + * 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. */ private predicate flowFwd( - Node node, boolean fromArg, AccessPathOption argAp, AccessPathFront apf, AccessPath ap, + Node node, CallContext cc, AccessPathOption argAp, AccessPathFront apf, AccessPath ap, Configuration config ) { - flowFwd0(node, fromArg, argAp, apf, ap, config) and + flowFwd0(node, cc, argAp, apf, ap, config) and flowCand(node, _, _, apf, config) } private predicate flowFwd0( - Node node, boolean fromArg, AccessPathOption argAp, AccessPathFront apf, AccessPath ap, + Node node, CallContext cc, AccessPathOption argAp, AccessPathFront apf, AccessPath ap, Configuration config ) { flowCand(node, _, _, _, config) and config.isSource(node) and - fromArg = false and + cc instanceof CallContextAny and argAp = TAccessPathNone() and ap = TNil(getNodeType(node)) and apf = ap.(AccessPathNil).getFront() or flowCand(node, _, _, _, unbind(config)) and ( - exists(Node mid | - flowFwd(mid, fromArg, argAp, apf, ap, config) and - localFlowBigStep(mid, node, true, _, config, _) + exists(Node mid, LocalCallContext localCC | + flowFwdLocalEntry(mid, cc, argAp, apf, ap, localCC, config) and + localFlowBigStep(mid, node, true, _, config, localCC) ) or - exists(Node mid, AccessPathNil nil | - flowFwd(mid, fromArg, argAp, _, nil, config) and - localFlowBigStep(mid, node, false, apf, config, _) and + exists(Node mid, AccessPathNil nil, LocalCallContext localCC | + flowFwdLocalEntry(mid, cc, argAp, _, nil, localCC, config) and + localFlowBigStep(mid, node, false, apf, config, localCC) and apf = ap.(AccessPathNil).getFront() ) or exists(Node mid | flowFwd(mid, _, _, apf, ap, config) and jumpStep(mid, node, config) and - fromArg = false and + cc instanceof CallContextAny and argAp = TAccessPathNone() ) or exists(Node mid, AccessPathNil nil | flowFwd(mid, _, _, _, nil, config) and additionalJumpStep(mid, node, config) and - fromArg = false and + cc instanceof CallContextAny and argAp = TAccessPathNone() and ap = TNil(getNodeType(node)) and apf = ap.(AccessPathNil).getFront() @@ -1704,40 +1704,51 @@ private predicate flowFwd0( ) or // store - exists(TypedContent tc | flowFwdStore(node, tc, pop(tc, ap), apf, fromArg, argAp, config)) + exists(TypedContent tc | flowFwdStore(node, tc, pop(tc, ap), apf, cc, argAp, config)) or // read exists(TypedContent tc | - flowFwdRead(node, _, push(tc, ap), apf, fromArg, argAp, config) and + flowFwdRead(node, _, push(tc, ap), apf, cc, argAp, config) and flowFwdConsCand(tc, apf, ap, config) ) or // flow into a callable - flowFwdIn(_, node, _, _, apf, ap, config) and - fromArg = true and + flowFwdIn(_, node, _, cc, _, apf, ap, config) and if flowCand(node, true, _, apf, config) then argAp = TAccessPathSome(ap) else argAp = TAccessPathNone() or // flow out of a callable exists(DataFlowCall call | - flowFwdOut(call, node, fromArg, argAp, apf, ap, config) and - fromArg = false + exists(DataFlowCallable c | + flowFwdOut(call, node, any(CallContextNoCall innercc), c, argAp, apf, ap, config) and + if reducedViableImplInReturn(c, call) then cc = TReturn(c, call) else cc = TAnyCallContext() + ) or exists(AccessPath argAp0 | flowFwdOutFromArg(call, node, argAp0, apf, ap, config) and - flowFwdIsEntered(call, fromArg, argAp, argAp0, config) + flowFwdIsEntered(call, cc, argAp, argAp0, config) ) ) } +pragma[nomagic] +private predicate flowFwdLocalEntry( + Node node, CallContext cc, AccessPathOption argAp, AccessPathFront apf, AccessPath ap, + LocalCallContext localCC, Configuration config +) { + flowFwd(node, cc, argAp, apf, ap, config) and + localFlowEntry(node, config) and + localCC = getLocalCallContext(cc, node.getEnclosingCallable()) +} + pragma[nomagic] private predicate flowFwdStore( - Node node, TypedContent tc, AccessPath ap0, AccessPathFront apf, boolean fromArg, + Node node, TypedContent tc, AccessPath ap0, AccessPathFront apf, CallContext cc, AccessPathOption argAp, Configuration config ) { exists(Node mid, AccessPathFront apf0 | - flowFwd(mid, fromArg, argAp, apf0, ap0, config) and + flowFwd(mid, cc, argAp, apf0, ap0, config) and flowFwdStore0(mid, tc, node, apf0, apf, config) ) } @@ -1764,20 +1775,20 @@ private predicate flowFwdStore0( pragma[nomagic] private predicate flowFwdRead0( - Node node1, TypedContent tc, AccessPathFrontHead apf0, AccessPath ap0, Node node2, - boolean fromArg, AccessPathOption argAp, Configuration config + Node node1, TypedContent tc, AccessPathFrontHead apf0, AccessPath ap0, Node node2, CallContext cc, + AccessPathOption argAp, Configuration config ) { - flowFwd(node1, fromArg, argAp, apf0, ap0, config) and + flowFwd(node1, cc, argAp, apf0, ap0, config) and readCandFwd(node1, tc, apf0, node2, config) } pragma[nomagic] private predicate flowFwdRead( - Node node, AccessPathFrontHead apf0, AccessPath ap0, AccessPathFront apf, boolean fromArg, + Node node, AccessPathFrontHead apf0, AccessPath ap0, AccessPathFront apf, CallContext cc, AccessPathOption argAp, Configuration config ) { exists(Node mid, TypedContent tc | - flowFwdRead0(mid, tc, apf0, ap0, node, fromArg, argAp, config) and + flowFwdRead0(mid, tc, apf0, ap0, node, cc, argAp, config) and flowCand(node, _, _, apf, unbind(config)) and flowCandConsCand(tc, apf, unbind(config)) ) @@ -1795,13 +1806,16 @@ private predicate flowFwdConsCand( pragma[nomagic] private predicate flowFwdIn( - DataFlowCall call, ParameterNode p, boolean fromArg, AccessPathOption argAp, AccessPathFront apf, - AccessPath ap, Configuration config + DataFlowCall call, ParameterNode p, CallContext outercc, CallContext innercc, + AccessPathOption argAp, AccessPathFront apf, AccessPath ap, Configuration config ) { - exists(ArgumentNode arg, boolean allowsFieldFlow | - flowFwd(arg, fromArg, argAp, apf, ap, config) and + exists(ArgumentNode arg, boolean allowsFieldFlow, DataFlowCallable c | + flowFwd(arg, outercc, argAp, apf, ap, config) and flowIntoCallNodeCand2(call, arg, p, allowsFieldFlow, config) and - flowCand(p, _, _, _, unbind(config)) + c = p.getEnclosingCallable() and + c = resolveCall(call, outercc) and + flowCand(p, _, _, _, unbind(config)) and + if recordDataFlowCallSite(call, c) then innercc = TSpecificCall(call) else innercc = TSomeCall() | ap instanceof AccessPathNil or allowsFieldFlow = true ) @@ -1809,13 +1823,19 @@ private predicate flowFwdIn( pragma[nomagic] private predicate flowFwdOut( - DataFlowCall call, Node node, boolean fromArg, AccessPathOption argAp, AccessPathFront apf, - AccessPath ap, Configuration config + DataFlowCall call, Node node, CallContext innercc, DataFlowCallable innerc, + AccessPathOption argAp, AccessPathFront apf, AccessPath ap, Configuration config ) { exists(ReturnNodeExt ret, boolean allowsFieldFlow | - flowFwd(ret, fromArg, argAp, apf, ap, config) and + flowFwd(ret, innercc, argAp, apf, ap, config) and flowOutOfCallNodeCand2(call, ret, node, allowsFieldFlow, config) and - flowCand(node, _, _, _, unbind(config)) + innerc = ret.getEnclosingCallable() and + flowCand(node, _, _, _, unbind(config)) and + ( + resolveReturn(innercc, innerc, call) + or + innercc.(CallContextCall).matchesCall(call) + ) | ap instanceof AccessPathNil or allowsFieldFlow = true ) @@ -1826,7 +1846,7 @@ private predicate flowFwdOutFromArg( DataFlowCall call, Node node, AccessPath argAp, AccessPathFront apf, AccessPath ap, Configuration config ) { - flowFwdOut(call, node, true, TAccessPathSome(argAp), apf, ap, config) + flowFwdOut(call, node, any(CallContextCall ccc), _, TAccessPathSome(argAp), apf, ap, config) } /** @@ -1834,10 +1854,10 @@ private predicate flowFwdOutFromArg( */ pragma[nomagic] private predicate flowFwdIsEntered( - DataFlowCall call, boolean fromArg, AccessPathOption argAp, AccessPath ap, Configuration config + DataFlowCall call, CallContext cc, AccessPathOption argAp, AccessPath ap, Configuration config ) { exists(ParameterNode p, AccessPathFront apf | - flowFwdIn(call, p, fromArg, argAp, apf, ap, config) and + flowFwdIn(call, p, cc, _, argAp, apf, ap, config) and flowCand(p, true, TAccessPathFrontSome(_), apf, config) ) } @@ -1920,7 +1940,7 @@ private predicate flow0( // flow out of a callable flowOut(_, node, _, _, ap, config) and toReturn = true and - if flowFwd(node, true, TAccessPathSome(_), _, ap, config) + if flowFwd(node, any(CallContextCall ccc), TAccessPathSome(_), _, ap, config) then returnAp = TAccessPathSome(ap) else returnAp = TAccessPathNone() } @@ -2006,9 +2026,10 @@ private predicate flowIsReturned( DataFlowCall call, boolean toReturn, AccessPathOption returnAp, AccessPath ap, Configuration config ) { - exists(ReturnNodeExt ret | + exists(ReturnNodeExt ret, CallContextCall ccc | flowOut(call, ret, toReturn, returnAp, ap, config) and - flowFwd(ret, true, TAccessPathSome(_), _, ap, config) + flowFwd(ret, ccc, TAccessPathSome(_), _, ap, config) and + ccc.matchesCall(call) ) } @@ -2031,7 +2052,7 @@ private newtype TSummaryCtx = exists(ReturnNodeExt ret, Configuration config, AccessPath ap0 | parameterFlow(p, ap, ret.getEnclosingCallable(), config) and flow(ret, true, TAccessPathSome(_), ap0, config) and - flowFwd(ret, true, TAccessPathSome(ap), _, ap0, config) + flowFwd(ret, any(CallContextCall ccc), TAccessPathSome(ap), _, ap0, config) ) } @@ -2352,7 +2373,7 @@ private predicate pathOutOfCallable0( ) { pos = getReturnPosition(mid.getNode()) and innercc = mid.getCallContext() and - not innercc instanceof CallContextCall and + innercc instanceof CallContextNoCall and ap = mid.getAp() and config = mid.getConfiguration() } @@ -2867,7 +2888,7 @@ private module FlowExploration { ) { pos = getReturnPosition(mid.getNode()) and innercc = mid.getCallContext() and - not innercc instanceof CallContextCall and + innercc instanceof CallContextNoCall and ap = mid.getAp() and config = mid.getConfiguration() } diff --git a/cpp/ql/src/semmle/code/cpp/ir/dataflow/internal/DataFlowImpl2.qll b/cpp/ql/src/semmle/code/cpp/ir/dataflow/internal/DataFlowImpl2.qll index 5042dce683f..8c210edbe5f 100644 --- a/cpp/ql/src/semmle/code/cpp/ir/dataflow/internal/DataFlowImpl2.qll +++ b/cpp/ql/src/semmle/code/cpp/ir/dataflow/internal/DataFlowImpl2.qll @@ -1066,7 +1066,7 @@ private module LocalFlowBigStep { * Holds if `node` can be the first node in a maximal subsequence of local * flow steps in a dataflow path. */ - private predicate localFlowEntry(Node node, Configuration config) { + predicate localFlowEntry(Node node, Configuration config) { nodeCand2(node, config) and ( config.isSource(node) or @@ -1650,53 +1650,53 @@ private class AccessPathOption extends TAccessPathOption { * Holds if `node` is reachable with access path `ap` from a source in * the configuration `config`. * - * The Boolean `fromArg` records whether the node is reached through an + * 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. */ private predicate flowFwd( - Node node, boolean fromArg, AccessPathOption argAp, AccessPathFront apf, AccessPath ap, + Node node, CallContext cc, AccessPathOption argAp, AccessPathFront apf, AccessPath ap, Configuration config ) { - flowFwd0(node, fromArg, argAp, apf, ap, config) and + flowFwd0(node, cc, argAp, apf, ap, config) and flowCand(node, _, _, apf, config) } private predicate flowFwd0( - Node node, boolean fromArg, AccessPathOption argAp, AccessPathFront apf, AccessPath ap, + Node node, CallContext cc, AccessPathOption argAp, AccessPathFront apf, AccessPath ap, Configuration config ) { flowCand(node, _, _, _, config) and config.isSource(node) and - fromArg = false and + cc instanceof CallContextAny and argAp = TAccessPathNone() and ap = TNil(getNodeType(node)) and apf = ap.(AccessPathNil).getFront() or flowCand(node, _, _, _, unbind(config)) and ( - exists(Node mid | - flowFwd(mid, fromArg, argAp, apf, ap, config) and - localFlowBigStep(mid, node, true, _, config, _) + exists(Node mid, LocalCallContext localCC | + flowFwdLocalEntry(mid, cc, argAp, apf, ap, localCC, config) and + localFlowBigStep(mid, node, true, _, config, localCC) ) or - exists(Node mid, AccessPathNil nil | - flowFwd(mid, fromArg, argAp, _, nil, config) and - localFlowBigStep(mid, node, false, apf, config, _) and + exists(Node mid, AccessPathNil nil, LocalCallContext localCC | + flowFwdLocalEntry(mid, cc, argAp, _, nil, localCC, config) and + localFlowBigStep(mid, node, false, apf, config, localCC) and apf = ap.(AccessPathNil).getFront() ) or exists(Node mid | flowFwd(mid, _, _, apf, ap, config) and jumpStep(mid, node, config) and - fromArg = false and + cc instanceof CallContextAny and argAp = TAccessPathNone() ) or exists(Node mid, AccessPathNil nil | flowFwd(mid, _, _, _, nil, config) and additionalJumpStep(mid, node, config) and - fromArg = false and + cc instanceof CallContextAny and argAp = TAccessPathNone() and ap = TNil(getNodeType(node)) and apf = ap.(AccessPathNil).getFront() @@ -1704,40 +1704,51 @@ private predicate flowFwd0( ) or // store - exists(TypedContent tc | flowFwdStore(node, tc, pop(tc, ap), apf, fromArg, argAp, config)) + exists(TypedContent tc | flowFwdStore(node, tc, pop(tc, ap), apf, cc, argAp, config)) or // read exists(TypedContent tc | - flowFwdRead(node, _, push(tc, ap), apf, fromArg, argAp, config) and + flowFwdRead(node, _, push(tc, ap), apf, cc, argAp, config) and flowFwdConsCand(tc, apf, ap, config) ) or // flow into a callable - flowFwdIn(_, node, _, _, apf, ap, config) and - fromArg = true and + flowFwdIn(_, node, _, cc, _, apf, ap, config) and if flowCand(node, true, _, apf, config) then argAp = TAccessPathSome(ap) else argAp = TAccessPathNone() or // flow out of a callable exists(DataFlowCall call | - flowFwdOut(call, node, fromArg, argAp, apf, ap, config) and - fromArg = false + exists(DataFlowCallable c | + flowFwdOut(call, node, any(CallContextNoCall innercc), c, argAp, apf, ap, config) and + if reducedViableImplInReturn(c, call) then cc = TReturn(c, call) else cc = TAnyCallContext() + ) or exists(AccessPath argAp0 | flowFwdOutFromArg(call, node, argAp0, apf, ap, config) and - flowFwdIsEntered(call, fromArg, argAp, argAp0, config) + flowFwdIsEntered(call, cc, argAp, argAp0, config) ) ) } +pragma[nomagic] +private predicate flowFwdLocalEntry( + Node node, CallContext cc, AccessPathOption argAp, AccessPathFront apf, AccessPath ap, + LocalCallContext localCC, Configuration config +) { + flowFwd(node, cc, argAp, apf, ap, config) and + localFlowEntry(node, config) and + localCC = getLocalCallContext(cc, node.getEnclosingCallable()) +} + pragma[nomagic] private predicate flowFwdStore( - Node node, TypedContent tc, AccessPath ap0, AccessPathFront apf, boolean fromArg, + Node node, TypedContent tc, AccessPath ap0, AccessPathFront apf, CallContext cc, AccessPathOption argAp, Configuration config ) { exists(Node mid, AccessPathFront apf0 | - flowFwd(mid, fromArg, argAp, apf0, ap0, config) and + flowFwd(mid, cc, argAp, apf0, ap0, config) and flowFwdStore0(mid, tc, node, apf0, apf, config) ) } @@ -1764,20 +1775,20 @@ private predicate flowFwdStore0( pragma[nomagic] private predicate flowFwdRead0( - Node node1, TypedContent tc, AccessPathFrontHead apf0, AccessPath ap0, Node node2, - boolean fromArg, AccessPathOption argAp, Configuration config + Node node1, TypedContent tc, AccessPathFrontHead apf0, AccessPath ap0, Node node2, CallContext cc, + AccessPathOption argAp, Configuration config ) { - flowFwd(node1, fromArg, argAp, apf0, ap0, config) and + flowFwd(node1, cc, argAp, apf0, ap0, config) and readCandFwd(node1, tc, apf0, node2, config) } pragma[nomagic] private predicate flowFwdRead( - Node node, AccessPathFrontHead apf0, AccessPath ap0, AccessPathFront apf, boolean fromArg, + Node node, AccessPathFrontHead apf0, AccessPath ap0, AccessPathFront apf, CallContext cc, AccessPathOption argAp, Configuration config ) { exists(Node mid, TypedContent tc | - flowFwdRead0(mid, tc, apf0, ap0, node, fromArg, argAp, config) and + flowFwdRead0(mid, tc, apf0, ap0, node, cc, argAp, config) and flowCand(node, _, _, apf, unbind(config)) and flowCandConsCand(tc, apf, unbind(config)) ) @@ -1795,13 +1806,16 @@ private predicate flowFwdConsCand( pragma[nomagic] private predicate flowFwdIn( - DataFlowCall call, ParameterNode p, boolean fromArg, AccessPathOption argAp, AccessPathFront apf, - AccessPath ap, Configuration config + DataFlowCall call, ParameterNode p, CallContext outercc, CallContext innercc, + AccessPathOption argAp, AccessPathFront apf, AccessPath ap, Configuration config ) { - exists(ArgumentNode arg, boolean allowsFieldFlow | - flowFwd(arg, fromArg, argAp, apf, ap, config) and + exists(ArgumentNode arg, boolean allowsFieldFlow, DataFlowCallable c | + flowFwd(arg, outercc, argAp, apf, ap, config) and flowIntoCallNodeCand2(call, arg, p, allowsFieldFlow, config) and - flowCand(p, _, _, _, unbind(config)) + c = p.getEnclosingCallable() and + c = resolveCall(call, outercc) and + flowCand(p, _, _, _, unbind(config)) and + if recordDataFlowCallSite(call, c) then innercc = TSpecificCall(call) else innercc = TSomeCall() | ap instanceof AccessPathNil or allowsFieldFlow = true ) @@ -1809,13 +1823,19 @@ private predicate flowFwdIn( pragma[nomagic] private predicate flowFwdOut( - DataFlowCall call, Node node, boolean fromArg, AccessPathOption argAp, AccessPathFront apf, - AccessPath ap, Configuration config + DataFlowCall call, Node node, CallContext innercc, DataFlowCallable innerc, + AccessPathOption argAp, AccessPathFront apf, AccessPath ap, Configuration config ) { exists(ReturnNodeExt ret, boolean allowsFieldFlow | - flowFwd(ret, fromArg, argAp, apf, ap, config) and + flowFwd(ret, innercc, argAp, apf, ap, config) and flowOutOfCallNodeCand2(call, ret, node, allowsFieldFlow, config) and - flowCand(node, _, _, _, unbind(config)) + innerc = ret.getEnclosingCallable() and + flowCand(node, _, _, _, unbind(config)) and + ( + resolveReturn(innercc, innerc, call) + or + innercc.(CallContextCall).matchesCall(call) + ) | ap instanceof AccessPathNil or allowsFieldFlow = true ) @@ -1826,7 +1846,7 @@ private predicate flowFwdOutFromArg( DataFlowCall call, Node node, AccessPath argAp, AccessPathFront apf, AccessPath ap, Configuration config ) { - flowFwdOut(call, node, true, TAccessPathSome(argAp), apf, ap, config) + flowFwdOut(call, node, any(CallContextCall ccc), _, TAccessPathSome(argAp), apf, ap, config) } /** @@ -1834,10 +1854,10 @@ private predicate flowFwdOutFromArg( */ pragma[nomagic] private predicate flowFwdIsEntered( - DataFlowCall call, boolean fromArg, AccessPathOption argAp, AccessPath ap, Configuration config + DataFlowCall call, CallContext cc, AccessPathOption argAp, AccessPath ap, Configuration config ) { exists(ParameterNode p, AccessPathFront apf | - flowFwdIn(call, p, fromArg, argAp, apf, ap, config) and + flowFwdIn(call, p, cc, _, argAp, apf, ap, config) and flowCand(p, true, TAccessPathFrontSome(_), apf, config) ) } @@ -1920,7 +1940,7 @@ private predicate flow0( // flow out of a callable flowOut(_, node, _, _, ap, config) and toReturn = true and - if flowFwd(node, true, TAccessPathSome(_), _, ap, config) + if flowFwd(node, any(CallContextCall ccc), TAccessPathSome(_), _, ap, config) then returnAp = TAccessPathSome(ap) else returnAp = TAccessPathNone() } @@ -2006,9 +2026,10 @@ private predicate flowIsReturned( DataFlowCall call, boolean toReturn, AccessPathOption returnAp, AccessPath ap, Configuration config ) { - exists(ReturnNodeExt ret | + exists(ReturnNodeExt ret, CallContextCall ccc | flowOut(call, ret, toReturn, returnAp, ap, config) and - flowFwd(ret, true, TAccessPathSome(_), _, ap, config) + flowFwd(ret, ccc, TAccessPathSome(_), _, ap, config) and + ccc.matchesCall(call) ) } @@ -2031,7 +2052,7 @@ private newtype TSummaryCtx = exists(ReturnNodeExt ret, Configuration config, AccessPath ap0 | parameterFlow(p, ap, ret.getEnclosingCallable(), config) and flow(ret, true, TAccessPathSome(_), ap0, config) and - flowFwd(ret, true, TAccessPathSome(ap), _, ap0, config) + flowFwd(ret, any(CallContextCall ccc), TAccessPathSome(ap), _, ap0, config) ) } @@ -2352,7 +2373,7 @@ private predicate pathOutOfCallable0( ) { pos = getReturnPosition(mid.getNode()) and innercc = mid.getCallContext() and - not innercc instanceof CallContextCall and + innercc instanceof CallContextNoCall and ap = mid.getAp() and config = mid.getConfiguration() } @@ -2867,7 +2888,7 @@ private module FlowExploration { ) { pos = getReturnPosition(mid.getNode()) and innercc = mid.getCallContext() and - not innercc instanceof CallContextCall and + innercc instanceof CallContextNoCall and ap = mid.getAp() and config = mid.getConfiguration() } diff --git a/cpp/ql/src/semmle/code/cpp/ir/dataflow/internal/DataFlowImpl3.qll b/cpp/ql/src/semmle/code/cpp/ir/dataflow/internal/DataFlowImpl3.qll index 5042dce683f..8c210edbe5f 100644 --- a/cpp/ql/src/semmle/code/cpp/ir/dataflow/internal/DataFlowImpl3.qll +++ b/cpp/ql/src/semmle/code/cpp/ir/dataflow/internal/DataFlowImpl3.qll @@ -1066,7 +1066,7 @@ private module LocalFlowBigStep { * Holds if `node` can be the first node in a maximal subsequence of local * flow steps in a dataflow path. */ - private predicate localFlowEntry(Node node, Configuration config) { + predicate localFlowEntry(Node node, Configuration config) { nodeCand2(node, config) and ( config.isSource(node) or @@ -1650,53 +1650,53 @@ private class AccessPathOption extends TAccessPathOption { * Holds if `node` is reachable with access path `ap` from a source in * the configuration `config`. * - * The Boolean `fromArg` records whether the node is reached through an + * 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. */ private predicate flowFwd( - Node node, boolean fromArg, AccessPathOption argAp, AccessPathFront apf, AccessPath ap, + Node node, CallContext cc, AccessPathOption argAp, AccessPathFront apf, AccessPath ap, Configuration config ) { - flowFwd0(node, fromArg, argAp, apf, ap, config) and + flowFwd0(node, cc, argAp, apf, ap, config) and flowCand(node, _, _, apf, config) } private predicate flowFwd0( - Node node, boolean fromArg, AccessPathOption argAp, AccessPathFront apf, AccessPath ap, + Node node, CallContext cc, AccessPathOption argAp, AccessPathFront apf, AccessPath ap, Configuration config ) { flowCand(node, _, _, _, config) and config.isSource(node) and - fromArg = false and + cc instanceof CallContextAny and argAp = TAccessPathNone() and ap = TNil(getNodeType(node)) and apf = ap.(AccessPathNil).getFront() or flowCand(node, _, _, _, unbind(config)) and ( - exists(Node mid | - flowFwd(mid, fromArg, argAp, apf, ap, config) and - localFlowBigStep(mid, node, true, _, config, _) + exists(Node mid, LocalCallContext localCC | + flowFwdLocalEntry(mid, cc, argAp, apf, ap, localCC, config) and + localFlowBigStep(mid, node, true, _, config, localCC) ) or - exists(Node mid, AccessPathNil nil | - flowFwd(mid, fromArg, argAp, _, nil, config) and - localFlowBigStep(mid, node, false, apf, config, _) and + exists(Node mid, AccessPathNil nil, LocalCallContext localCC | + flowFwdLocalEntry(mid, cc, argAp, _, nil, localCC, config) and + localFlowBigStep(mid, node, false, apf, config, localCC) and apf = ap.(AccessPathNil).getFront() ) or exists(Node mid | flowFwd(mid, _, _, apf, ap, config) and jumpStep(mid, node, config) and - fromArg = false and + cc instanceof CallContextAny and argAp = TAccessPathNone() ) or exists(Node mid, AccessPathNil nil | flowFwd(mid, _, _, _, nil, config) and additionalJumpStep(mid, node, config) and - fromArg = false and + cc instanceof CallContextAny and argAp = TAccessPathNone() and ap = TNil(getNodeType(node)) and apf = ap.(AccessPathNil).getFront() @@ -1704,40 +1704,51 @@ private predicate flowFwd0( ) or // store - exists(TypedContent tc | flowFwdStore(node, tc, pop(tc, ap), apf, fromArg, argAp, config)) + exists(TypedContent tc | flowFwdStore(node, tc, pop(tc, ap), apf, cc, argAp, config)) or // read exists(TypedContent tc | - flowFwdRead(node, _, push(tc, ap), apf, fromArg, argAp, config) and + flowFwdRead(node, _, push(tc, ap), apf, cc, argAp, config) and flowFwdConsCand(tc, apf, ap, config) ) or // flow into a callable - flowFwdIn(_, node, _, _, apf, ap, config) and - fromArg = true and + flowFwdIn(_, node, _, cc, _, apf, ap, config) and if flowCand(node, true, _, apf, config) then argAp = TAccessPathSome(ap) else argAp = TAccessPathNone() or // flow out of a callable exists(DataFlowCall call | - flowFwdOut(call, node, fromArg, argAp, apf, ap, config) and - fromArg = false + exists(DataFlowCallable c | + flowFwdOut(call, node, any(CallContextNoCall innercc), c, argAp, apf, ap, config) and + if reducedViableImplInReturn(c, call) then cc = TReturn(c, call) else cc = TAnyCallContext() + ) or exists(AccessPath argAp0 | flowFwdOutFromArg(call, node, argAp0, apf, ap, config) and - flowFwdIsEntered(call, fromArg, argAp, argAp0, config) + flowFwdIsEntered(call, cc, argAp, argAp0, config) ) ) } +pragma[nomagic] +private predicate flowFwdLocalEntry( + Node node, CallContext cc, AccessPathOption argAp, AccessPathFront apf, AccessPath ap, + LocalCallContext localCC, Configuration config +) { + flowFwd(node, cc, argAp, apf, ap, config) and + localFlowEntry(node, config) and + localCC = getLocalCallContext(cc, node.getEnclosingCallable()) +} + pragma[nomagic] private predicate flowFwdStore( - Node node, TypedContent tc, AccessPath ap0, AccessPathFront apf, boolean fromArg, + Node node, TypedContent tc, AccessPath ap0, AccessPathFront apf, CallContext cc, AccessPathOption argAp, Configuration config ) { exists(Node mid, AccessPathFront apf0 | - flowFwd(mid, fromArg, argAp, apf0, ap0, config) and + flowFwd(mid, cc, argAp, apf0, ap0, config) and flowFwdStore0(mid, tc, node, apf0, apf, config) ) } @@ -1764,20 +1775,20 @@ private predicate flowFwdStore0( pragma[nomagic] private predicate flowFwdRead0( - Node node1, TypedContent tc, AccessPathFrontHead apf0, AccessPath ap0, Node node2, - boolean fromArg, AccessPathOption argAp, Configuration config + Node node1, TypedContent tc, AccessPathFrontHead apf0, AccessPath ap0, Node node2, CallContext cc, + AccessPathOption argAp, Configuration config ) { - flowFwd(node1, fromArg, argAp, apf0, ap0, config) and + flowFwd(node1, cc, argAp, apf0, ap0, config) and readCandFwd(node1, tc, apf0, node2, config) } pragma[nomagic] private predicate flowFwdRead( - Node node, AccessPathFrontHead apf0, AccessPath ap0, AccessPathFront apf, boolean fromArg, + Node node, AccessPathFrontHead apf0, AccessPath ap0, AccessPathFront apf, CallContext cc, AccessPathOption argAp, Configuration config ) { exists(Node mid, TypedContent tc | - flowFwdRead0(mid, tc, apf0, ap0, node, fromArg, argAp, config) and + flowFwdRead0(mid, tc, apf0, ap0, node, cc, argAp, config) and flowCand(node, _, _, apf, unbind(config)) and flowCandConsCand(tc, apf, unbind(config)) ) @@ -1795,13 +1806,16 @@ private predicate flowFwdConsCand( pragma[nomagic] private predicate flowFwdIn( - DataFlowCall call, ParameterNode p, boolean fromArg, AccessPathOption argAp, AccessPathFront apf, - AccessPath ap, Configuration config + DataFlowCall call, ParameterNode p, CallContext outercc, CallContext innercc, + AccessPathOption argAp, AccessPathFront apf, AccessPath ap, Configuration config ) { - exists(ArgumentNode arg, boolean allowsFieldFlow | - flowFwd(arg, fromArg, argAp, apf, ap, config) and + exists(ArgumentNode arg, boolean allowsFieldFlow, DataFlowCallable c | + flowFwd(arg, outercc, argAp, apf, ap, config) and flowIntoCallNodeCand2(call, arg, p, allowsFieldFlow, config) and - flowCand(p, _, _, _, unbind(config)) + c = p.getEnclosingCallable() and + c = resolveCall(call, outercc) and + flowCand(p, _, _, _, unbind(config)) and + if recordDataFlowCallSite(call, c) then innercc = TSpecificCall(call) else innercc = TSomeCall() | ap instanceof AccessPathNil or allowsFieldFlow = true ) @@ -1809,13 +1823,19 @@ private predicate flowFwdIn( pragma[nomagic] private predicate flowFwdOut( - DataFlowCall call, Node node, boolean fromArg, AccessPathOption argAp, AccessPathFront apf, - AccessPath ap, Configuration config + DataFlowCall call, Node node, CallContext innercc, DataFlowCallable innerc, + AccessPathOption argAp, AccessPathFront apf, AccessPath ap, Configuration config ) { exists(ReturnNodeExt ret, boolean allowsFieldFlow | - flowFwd(ret, fromArg, argAp, apf, ap, config) and + flowFwd(ret, innercc, argAp, apf, ap, config) and flowOutOfCallNodeCand2(call, ret, node, allowsFieldFlow, config) and - flowCand(node, _, _, _, unbind(config)) + innerc = ret.getEnclosingCallable() and + flowCand(node, _, _, _, unbind(config)) and + ( + resolveReturn(innercc, innerc, call) + or + innercc.(CallContextCall).matchesCall(call) + ) | ap instanceof AccessPathNil or allowsFieldFlow = true ) @@ -1826,7 +1846,7 @@ private predicate flowFwdOutFromArg( DataFlowCall call, Node node, AccessPath argAp, AccessPathFront apf, AccessPath ap, Configuration config ) { - flowFwdOut(call, node, true, TAccessPathSome(argAp), apf, ap, config) + flowFwdOut(call, node, any(CallContextCall ccc), _, TAccessPathSome(argAp), apf, ap, config) } /** @@ -1834,10 +1854,10 @@ private predicate flowFwdOutFromArg( */ pragma[nomagic] private predicate flowFwdIsEntered( - DataFlowCall call, boolean fromArg, AccessPathOption argAp, AccessPath ap, Configuration config + DataFlowCall call, CallContext cc, AccessPathOption argAp, AccessPath ap, Configuration config ) { exists(ParameterNode p, AccessPathFront apf | - flowFwdIn(call, p, fromArg, argAp, apf, ap, config) and + flowFwdIn(call, p, cc, _, argAp, apf, ap, config) and flowCand(p, true, TAccessPathFrontSome(_), apf, config) ) } @@ -1920,7 +1940,7 @@ private predicate flow0( // flow out of a callable flowOut(_, node, _, _, ap, config) and toReturn = true and - if flowFwd(node, true, TAccessPathSome(_), _, ap, config) + if flowFwd(node, any(CallContextCall ccc), TAccessPathSome(_), _, ap, config) then returnAp = TAccessPathSome(ap) else returnAp = TAccessPathNone() } @@ -2006,9 +2026,10 @@ private predicate flowIsReturned( DataFlowCall call, boolean toReturn, AccessPathOption returnAp, AccessPath ap, Configuration config ) { - exists(ReturnNodeExt ret | + exists(ReturnNodeExt ret, CallContextCall ccc | flowOut(call, ret, toReturn, returnAp, ap, config) and - flowFwd(ret, true, TAccessPathSome(_), _, ap, config) + flowFwd(ret, ccc, TAccessPathSome(_), _, ap, config) and + ccc.matchesCall(call) ) } @@ -2031,7 +2052,7 @@ private newtype TSummaryCtx = exists(ReturnNodeExt ret, Configuration config, AccessPath ap0 | parameterFlow(p, ap, ret.getEnclosingCallable(), config) and flow(ret, true, TAccessPathSome(_), ap0, config) and - flowFwd(ret, true, TAccessPathSome(ap), _, ap0, config) + flowFwd(ret, any(CallContextCall ccc), TAccessPathSome(ap), _, ap0, config) ) } @@ -2352,7 +2373,7 @@ private predicate pathOutOfCallable0( ) { pos = getReturnPosition(mid.getNode()) and innercc = mid.getCallContext() and - not innercc instanceof CallContextCall and + innercc instanceof CallContextNoCall and ap = mid.getAp() and config = mid.getConfiguration() } @@ -2867,7 +2888,7 @@ private module FlowExploration { ) { pos = getReturnPosition(mid.getNode()) and innercc = mid.getCallContext() and - not innercc instanceof CallContextCall and + innercc instanceof CallContextNoCall and ap = mid.getAp() and config = mid.getConfiguration() } diff --git a/cpp/ql/src/semmle/code/cpp/ir/dataflow/internal/DataFlowImpl4.qll b/cpp/ql/src/semmle/code/cpp/ir/dataflow/internal/DataFlowImpl4.qll index 5042dce683f..8c210edbe5f 100644 --- a/cpp/ql/src/semmle/code/cpp/ir/dataflow/internal/DataFlowImpl4.qll +++ b/cpp/ql/src/semmle/code/cpp/ir/dataflow/internal/DataFlowImpl4.qll @@ -1066,7 +1066,7 @@ private module LocalFlowBigStep { * Holds if `node` can be the first node in a maximal subsequence of local * flow steps in a dataflow path. */ - private predicate localFlowEntry(Node node, Configuration config) { + predicate localFlowEntry(Node node, Configuration config) { nodeCand2(node, config) and ( config.isSource(node) or @@ -1650,53 +1650,53 @@ private class AccessPathOption extends TAccessPathOption { * Holds if `node` is reachable with access path `ap` from a source in * the configuration `config`. * - * The Boolean `fromArg` records whether the node is reached through an + * 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. */ private predicate flowFwd( - Node node, boolean fromArg, AccessPathOption argAp, AccessPathFront apf, AccessPath ap, + Node node, CallContext cc, AccessPathOption argAp, AccessPathFront apf, AccessPath ap, Configuration config ) { - flowFwd0(node, fromArg, argAp, apf, ap, config) and + flowFwd0(node, cc, argAp, apf, ap, config) and flowCand(node, _, _, apf, config) } private predicate flowFwd0( - Node node, boolean fromArg, AccessPathOption argAp, AccessPathFront apf, AccessPath ap, + Node node, CallContext cc, AccessPathOption argAp, AccessPathFront apf, AccessPath ap, Configuration config ) { flowCand(node, _, _, _, config) and config.isSource(node) and - fromArg = false and + cc instanceof CallContextAny and argAp = TAccessPathNone() and ap = TNil(getNodeType(node)) and apf = ap.(AccessPathNil).getFront() or flowCand(node, _, _, _, unbind(config)) and ( - exists(Node mid | - flowFwd(mid, fromArg, argAp, apf, ap, config) and - localFlowBigStep(mid, node, true, _, config, _) + exists(Node mid, LocalCallContext localCC | + flowFwdLocalEntry(mid, cc, argAp, apf, ap, localCC, config) and + localFlowBigStep(mid, node, true, _, config, localCC) ) or - exists(Node mid, AccessPathNil nil | - flowFwd(mid, fromArg, argAp, _, nil, config) and - localFlowBigStep(mid, node, false, apf, config, _) and + exists(Node mid, AccessPathNil nil, LocalCallContext localCC | + flowFwdLocalEntry(mid, cc, argAp, _, nil, localCC, config) and + localFlowBigStep(mid, node, false, apf, config, localCC) and apf = ap.(AccessPathNil).getFront() ) or exists(Node mid | flowFwd(mid, _, _, apf, ap, config) and jumpStep(mid, node, config) and - fromArg = false and + cc instanceof CallContextAny and argAp = TAccessPathNone() ) or exists(Node mid, AccessPathNil nil | flowFwd(mid, _, _, _, nil, config) and additionalJumpStep(mid, node, config) and - fromArg = false and + cc instanceof CallContextAny and argAp = TAccessPathNone() and ap = TNil(getNodeType(node)) and apf = ap.(AccessPathNil).getFront() @@ -1704,40 +1704,51 @@ private predicate flowFwd0( ) or // store - exists(TypedContent tc | flowFwdStore(node, tc, pop(tc, ap), apf, fromArg, argAp, config)) + exists(TypedContent tc | flowFwdStore(node, tc, pop(tc, ap), apf, cc, argAp, config)) or // read exists(TypedContent tc | - flowFwdRead(node, _, push(tc, ap), apf, fromArg, argAp, config) and + flowFwdRead(node, _, push(tc, ap), apf, cc, argAp, config) and flowFwdConsCand(tc, apf, ap, config) ) or // flow into a callable - flowFwdIn(_, node, _, _, apf, ap, config) and - fromArg = true and + flowFwdIn(_, node, _, cc, _, apf, ap, config) and if flowCand(node, true, _, apf, config) then argAp = TAccessPathSome(ap) else argAp = TAccessPathNone() or // flow out of a callable exists(DataFlowCall call | - flowFwdOut(call, node, fromArg, argAp, apf, ap, config) and - fromArg = false + exists(DataFlowCallable c | + flowFwdOut(call, node, any(CallContextNoCall innercc), c, argAp, apf, ap, config) and + if reducedViableImplInReturn(c, call) then cc = TReturn(c, call) else cc = TAnyCallContext() + ) or exists(AccessPath argAp0 | flowFwdOutFromArg(call, node, argAp0, apf, ap, config) and - flowFwdIsEntered(call, fromArg, argAp, argAp0, config) + flowFwdIsEntered(call, cc, argAp, argAp0, config) ) ) } +pragma[nomagic] +private predicate flowFwdLocalEntry( + Node node, CallContext cc, AccessPathOption argAp, AccessPathFront apf, AccessPath ap, + LocalCallContext localCC, Configuration config +) { + flowFwd(node, cc, argAp, apf, ap, config) and + localFlowEntry(node, config) and + localCC = getLocalCallContext(cc, node.getEnclosingCallable()) +} + pragma[nomagic] private predicate flowFwdStore( - Node node, TypedContent tc, AccessPath ap0, AccessPathFront apf, boolean fromArg, + Node node, TypedContent tc, AccessPath ap0, AccessPathFront apf, CallContext cc, AccessPathOption argAp, Configuration config ) { exists(Node mid, AccessPathFront apf0 | - flowFwd(mid, fromArg, argAp, apf0, ap0, config) and + flowFwd(mid, cc, argAp, apf0, ap0, config) and flowFwdStore0(mid, tc, node, apf0, apf, config) ) } @@ -1764,20 +1775,20 @@ private predicate flowFwdStore0( pragma[nomagic] private predicate flowFwdRead0( - Node node1, TypedContent tc, AccessPathFrontHead apf0, AccessPath ap0, Node node2, - boolean fromArg, AccessPathOption argAp, Configuration config + Node node1, TypedContent tc, AccessPathFrontHead apf0, AccessPath ap0, Node node2, CallContext cc, + AccessPathOption argAp, Configuration config ) { - flowFwd(node1, fromArg, argAp, apf0, ap0, config) and + flowFwd(node1, cc, argAp, apf0, ap0, config) and readCandFwd(node1, tc, apf0, node2, config) } pragma[nomagic] private predicate flowFwdRead( - Node node, AccessPathFrontHead apf0, AccessPath ap0, AccessPathFront apf, boolean fromArg, + Node node, AccessPathFrontHead apf0, AccessPath ap0, AccessPathFront apf, CallContext cc, AccessPathOption argAp, Configuration config ) { exists(Node mid, TypedContent tc | - flowFwdRead0(mid, tc, apf0, ap0, node, fromArg, argAp, config) and + flowFwdRead0(mid, tc, apf0, ap0, node, cc, argAp, config) and flowCand(node, _, _, apf, unbind(config)) and flowCandConsCand(tc, apf, unbind(config)) ) @@ -1795,13 +1806,16 @@ private predicate flowFwdConsCand( pragma[nomagic] private predicate flowFwdIn( - DataFlowCall call, ParameterNode p, boolean fromArg, AccessPathOption argAp, AccessPathFront apf, - AccessPath ap, Configuration config + DataFlowCall call, ParameterNode p, CallContext outercc, CallContext innercc, + AccessPathOption argAp, AccessPathFront apf, AccessPath ap, Configuration config ) { - exists(ArgumentNode arg, boolean allowsFieldFlow | - flowFwd(arg, fromArg, argAp, apf, ap, config) and + exists(ArgumentNode arg, boolean allowsFieldFlow, DataFlowCallable c | + flowFwd(arg, outercc, argAp, apf, ap, config) and flowIntoCallNodeCand2(call, arg, p, allowsFieldFlow, config) and - flowCand(p, _, _, _, unbind(config)) + c = p.getEnclosingCallable() and + c = resolveCall(call, outercc) and + flowCand(p, _, _, _, unbind(config)) and + if recordDataFlowCallSite(call, c) then innercc = TSpecificCall(call) else innercc = TSomeCall() | ap instanceof AccessPathNil or allowsFieldFlow = true ) @@ -1809,13 +1823,19 @@ private predicate flowFwdIn( pragma[nomagic] private predicate flowFwdOut( - DataFlowCall call, Node node, boolean fromArg, AccessPathOption argAp, AccessPathFront apf, - AccessPath ap, Configuration config + DataFlowCall call, Node node, CallContext innercc, DataFlowCallable innerc, + AccessPathOption argAp, AccessPathFront apf, AccessPath ap, Configuration config ) { exists(ReturnNodeExt ret, boolean allowsFieldFlow | - flowFwd(ret, fromArg, argAp, apf, ap, config) and + flowFwd(ret, innercc, argAp, apf, ap, config) and flowOutOfCallNodeCand2(call, ret, node, allowsFieldFlow, config) and - flowCand(node, _, _, _, unbind(config)) + innerc = ret.getEnclosingCallable() and + flowCand(node, _, _, _, unbind(config)) and + ( + resolveReturn(innercc, innerc, call) + or + innercc.(CallContextCall).matchesCall(call) + ) | ap instanceof AccessPathNil or allowsFieldFlow = true ) @@ -1826,7 +1846,7 @@ private predicate flowFwdOutFromArg( DataFlowCall call, Node node, AccessPath argAp, AccessPathFront apf, AccessPath ap, Configuration config ) { - flowFwdOut(call, node, true, TAccessPathSome(argAp), apf, ap, config) + flowFwdOut(call, node, any(CallContextCall ccc), _, TAccessPathSome(argAp), apf, ap, config) } /** @@ -1834,10 +1854,10 @@ private predicate flowFwdOutFromArg( */ pragma[nomagic] private predicate flowFwdIsEntered( - DataFlowCall call, boolean fromArg, AccessPathOption argAp, AccessPath ap, Configuration config + DataFlowCall call, CallContext cc, AccessPathOption argAp, AccessPath ap, Configuration config ) { exists(ParameterNode p, AccessPathFront apf | - flowFwdIn(call, p, fromArg, argAp, apf, ap, config) and + flowFwdIn(call, p, cc, _, argAp, apf, ap, config) and flowCand(p, true, TAccessPathFrontSome(_), apf, config) ) } @@ -1920,7 +1940,7 @@ private predicate flow0( // flow out of a callable flowOut(_, node, _, _, ap, config) and toReturn = true and - if flowFwd(node, true, TAccessPathSome(_), _, ap, config) + if flowFwd(node, any(CallContextCall ccc), TAccessPathSome(_), _, ap, config) then returnAp = TAccessPathSome(ap) else returnAp = TAccessPathNone() } @@ -2006,9 +2026,10 @@ private predicate flowIsReturned( DataFlowCall call, boolean toReturn, AccessPathOption returnAp, AccessPath ap, Configuration config ) { - exists(ReturnNodeExt ret | + exists(ReturnNodeExt ret, CallContextCall ccc | flowOut(call, ret, toReturn, returnAp, ap, config) and - flowFwd(ret, true, TAccessPathSome(_), _, ap, config) + flowFwd(ret, ccc, TAccessPathSome(_), _, ap, config) and + ccc.matchesCall(call) ) } @@ -2031,7 +2052,7 @@ private newtype TSummaryCtx = exists(ReturnNodeExt ret, Configuration config, AccessPath ap0 | parameterFlow(p, ap, ret.getEnclosingCallable(), config) and flow(ret, true, TAccessPathSome(_), ap0, config) and - flowFwd(ret, true, TAccessPathSome(ap), _, ap0, config) + flowFwd(ret, any(CallContextCall ccc), TAccessPathSome(ap), _, ap0, config) ) } @@ -2352,7 +2373,7 @@ private predicate pathOutOfCallable0( ) { pos = getReturnPosition(mid.getNode()) and innercc = mid.getCallContext() and - not innercc instanceof CallContextCall and + innercc instanceof CallContextNoCall and ap = mid.getAp() and config = mid.getConfiguration() } @@ -2867,7 +2888,7 @@ private module FlowExploration { ) { pos = getReturnPosition(mid.getNode()) and innercc = mid.getCallContext() and - not innercc instanceof CallContextCall and + innercc instanceof CallContextNoCall and ap = mid.getAp() and config = mid.getConfiguration() } diff --git a/cpp/ql/src/semmle/code/cpp/ir/dataflow/internal/DataFlowImplCommon.qll b/cpp/ql/src/semmle/code/cpp/ir/dataflow/internal/DataFlowImplCommon.qll index 27ab1d01feb..892250f44bb 100644 --- a/cpp/ql/src/semmle/code/cpp/ir/dataflow/internal/DataFlowImplCommon.qll +++ b/cpp/ql/src/semmle/code/cpp/ir/dataflow/internal/DataFlowImplCommon.qll @@ -512,13 +512,19 @@ abstract class CallContext extends TCallContext { abstract predicate relevantFor(DataFlowCallable callable); } -class CallContextAny extends CallContext, TAnyCallContext { +abstract class CallContextNoCall extends CallContext { } + +class CallContextAny extends CallContextNoCall, TAnyCallContext { override string toString() { result = "CcAny" } override predicate relevantFor(DataFlowCallable callable) { any() } } -abstract class CallContextCall extends CallContext { } +abstract class CallContextCall extends CallContext { + /** Holds if this call context may be `call`. */ + bindingset[call] + abstract predicate matchesCall(DataFlowCall call); +} class CallContextSpecificCall extends CallContextCall, TSpecificCall { override string toString() { @@ -529,6 +535,8 @@ class CallContextSpecificCall extends CallContextCall, TSpecificCall { recordDataFlowCallSite(getCall(), callable) } + override predicate matchesCall(DataFlowCall call) { call = this.getCall() } + DataFlowCall getCall() { this = TSpecificCall(result) } } @@ -538,9 +546,11 @@ class CallContextSomeCall extends CallContextCall, TSomeCall { override predicate relevantFor(DataFlowCallable callable) { exists(ParameterNode p | p.getEnclosingCallable() = callable) } + + override predicate matchesCall(DataFlowCall call) { any() } } -class CallContextReturn extends CallContext, TReturn { +class CallContextReturn extends CallContextNoCall, TReturn { override string toString() { exists(DataFlowCall call | this = TReturn(_, call) | result = "CcReturn(" + call + ")") } diff --git a/cpp/ql/src/semmle/code/cpp/rangeanalysis/SimpleRangeAnalysis.qll b/cpp/ql/src/semmle/code/cpp/rangeanalysis/SimpleRangeAnalysis.qll index a6d97149adf..0ef1d1f7104 100644 --- a/cpp/ql/src/semmle/code/cpp/rangeanalysis/SimpleRangeAnalysis.qll +++ b/cpp/ql/src/semmle/code/cpp/rangeanalysis/SimpleRangeAnalysis.qll @@ -44,6 +44,7 @@ import cpp private import RangeAnalysisUtils +private import experimental.semmle.code.cpp.models.interfaces.SimpleRangeAnalysisExpr import RangeSSA import SimpleRangeAnalysisCached private import NanAnalysis @@ -295,6 +296,9 @@ private predicate analyzableExpr(Expr e) { or // `>>` by a constant exists(getValue(e.(RShiftExpr).getRightOperand())) + or + // A modeled expression for range analysis + e instanceof SimpleRangeAnalysisExpr ) } @@ -420,6 +424,16 @@ private predicate exprDependsOnDef(Expr e, RangeSsaDefinition srcDef, StackVaria ) or e = srcDef.getAUse(srcVar) + or + // A modeled expression for range analysis + exists(SimpleRangeAnalysisExpr rae | rae = e | + rae.dependsOnDef(srcDef, srcVar) + or + exists(Expr child | + rae.dependsOnChild(child) and + exprDependsOnDef(child, srcDef, srcVar) + ) + ) } /** @@ -545,13 +559,6 @@ private float addRoundingDownSmall(float x, float small) { if (x + small) - x > small then result = (x + small).nextDown() else result = (x + small) } -/** - * Gets the truncated lower bounds of the fully converted expression. - */ -private float getFullyConvertedLowerBounds(Expr expr) { - result = getTruncatedLowerBounds(expr.getFullyConverted()) -} - /** * Gets the lower bounds of the expression. * @@ -598,13 +605,6 @@ private float getTruncatedLowerBounds(Expr expr) { result = exprMinVal(expr) } -/** - * Gets the truncated upper bounds of the fully converted expression. - */ -private float getFullyConvertedUpperBounds(Expr expr) { - result = getTruncatedUpperBounds(expr.getFullyConverted()) -} - /** * Gets the upper bounds of the expression. * @@ -838,7 +838,9 @@ private float getLowerBoundsImpl(Expr expr) { or // Use SSA to get the lower bounds for a variable use. exists(RangeSsaDefinition def, StackVariable v | expr = def.getAUse(v) | - result = getDefLowerBounds(def, v) + result = getDefLowerBounds(def, v) and + // Not explicitly modeled by a SimpleRangeAnalysisExpr + not expr instanceof SimpleRangeAnalysisExpr ) or // unsigned `&` (tighter bounds may exist) @@ -854,6 +856,12 @@ private float getLowerBoundsImpl(Expr expr) { right = getValue(rsExpr.getRightOperand().getFullyConverted()).toInt() and result = safeFloor(left / 2.pow(right)) ) + or + // A modeled expression for range analysis + exists(SimpleRangeAnalysisExpr rangeAnalysisExpr | + rangeAnalysisExpr = expr and + result = rangeAnalysisExpr.getLowerBounds() + ) } /** Only to be called by `getTruncatedUpperBounds`. */ @@ -1022,7 +1030,9 @@ private float getUpperBoundsImpl(Expr expr) { or // Use SSA to get the upper bounds for a variable use. exists(RangeSsaDefinition def, StackVariable v | expr = def.getAUse(v) | - result = getDefUpperBounds(def, v) + result = getDefUpperBounds(def, v) and + // Not explicitly modeled by a SimpleRangeAnalysisExpr + not expr instanceof SimpleRangeAnalysisExpr ) or // unsigned `&` (tighter bounds may exist) @@ -1040,6 +1050,12 @@ private float getUpperBoundsImpl(Expr expr) { right = getValue(rsExpr.getRightOperand().getFullyConverted()).toInt() and result = safeFloor(left / 2.pow(right)) ) + or + // A modeled expression for range analysis + exists(SimpleRangeAnalysisExpr rangeAnalysisExpr | + rangeAnalysisExpr = expr and + result = rangeAnalysisExpr.getUpperBounds() + ) } /** @@ -1668,3 +1684,25 @@ private module SimpleRangeAnalysisCached { convertedExprMightOverflowPositively(expr) } } + +/** + * INTERNAL: do not use. This module contains utilities for use in the + * experimental `SimpleRangeAnalysisExpr` module. + */ +module SimpleRangeAnalysisInternal { + /** + * Gets the truncated lower bounds of the fully converted expression. + */ + float getFullyConvertedLowerBounds(Expr expr) { + result = getTruncatedLowerBounds(expr.getFullyConverted()) + } + + /** + * Gets the truncated upper bounds of the fully converted expression. + */ + float getFullyConvertedUpperBounds(Expr expr) { + result = getTruncatedUpperBounds(expr.getFullyConverted()) + } +} + +private import SimpleRangeAnalysisInternal diff --git a/cpp/ql/test/experimental/library-tests/rangeanalysis/extensibility/extensibility.c b/cpp/ql/test/experimental/library-tests/rangeanalysis/extensibility/extensibility.c new file mode 100644 index 00000000000..42f7574996c --- /dev/null +++ b/cpp/ql/test/experimental/library-tests/rangeanalysis/extensibility/extensibility.c @@ -0,0 +1,9 @@ +/// Adds its arguments (has custom modeling in QL) +int custom_add_function(int a, int b); + +int test_extensibility_add(int x) { + if (x >= -10 && x <= 10) { + int result = custom_add_function(x, 100); + return result; // 90 .. 110 + } +} diff --git a/cpp/ql/test/experimental/library-tests/rangeanalysis/extensibility/extensibility.expected b/cpp/ql/test/experimental/library-tests/rangeanalysis/extensibility/extensibility.expected new file mode 100644 index 00000000000..463ac9ea06c --- /dev/null +++ b/cpp/ql/test/experimental/library-tests/rangeanalysis/extensibility/extensibility.expected @@ -0,0 +1,4 @@ +| extensibility.c:5:7:5:7 | x | -2.147483648E9 | 2.147483647E9 | +| extensibility.c:5:19:5:19 | x | -10.0 | 2.147483647E9 | +| extensibility.c:6:38:6:38 | x | -10.0 | 10.0 | +| extensibility.c:7:12:7:17 | result | 90.0 | 110.0 | diff --git a/cpp/ql/test/experimental/library-tests/rangeanalysis/extensibility/extensibility.ql b/cpp/ql/test/experimental/library-tests/rangeanalysis/extensibility/extensibility.ql new file mode 100644 index 00000000000..710a89ad7fa --- /dev/null +++ b/cpp/ql/test/experimental/library-tests/rangeanalysis/extensibility/extensibility.ql @@ -0,0 +1,32 @@ +import semmle.code.cpp.rangeanalysis.SimpleRangeAnalysis +import experimental.semmle.code.cpp.models.interfaces.SimpleRangeAnalysisExpr + +class CustomAddFunctionCall extends SimpleRangeAnalysisExpr, FunctionCall { + CustomAddFunctionCall() { this.getTarget().hasGlobalName("custom_add_function") } + + override float getLowerBounds() { + exists(float lower0, float lower1 | + lower0 = getFullyConvertedLowerBounds(this.getArgument(0)) and + lower1 = getFullyConvertedLowerBounds(this.getArgument(1)) and + // Note: this rounds toward 0, not -Inf as it should + result = lower0 + lower1 + ) + } + + override float getUpperBounds() { + exists(float upper0, float upper1 | + upper0 = getFullyConvertedUpperBounds(this.getArgument(0)) and + upper1 = getFullyConvertedUpperBounds(this.getArgument(1)) and + // Note: this rounds toward 0, not Inf as it should + result = upper0 + upper1 + ) + } + + override predicate dependsOnChild(Expr child) { child = this.getAnArgument() } +} + +from VariableAccess expr, float lower, float upper +where + lower = lowerBound(expr) and + upper = upperBound(expr) +select expr, lower, upper diff --git a/cpp/ql/test/library-tests/dataflow/taint-tests/localTaint.expected b/cpp/ql/test/library-tests/dataflow/taint-tests/localTaint.expected index cbaa02ae640..2fc40532357 100644 --- a/cpp/ql/test/library-tests/dataflow/taint-tests/localTaint.expected +++ b/cpp/ql/test/library-tests/dataflow/taint-tests/localTaint.expected @@ -308,284 +308,230 @@ | movableclass.cpp:65:13:65:18 | call to source | movableclass.cpp:65:13:65:20 | call to MyMovableClass | TAINT | | movableclass.cpp:65:13:65:20 | call to MyMovableClass | movableclass.cpp:65:8:65:9 | ref arg s3 | TAINT | | movableclass.cpp:65:13:65:20 | call to MyMovableClass | movableclass.cpp:65:11:65:11 | call to operator= | TAINT | -| stl.cpp:109:12:109:17 | call to source | stl.cpp:113:7:113:7 | a | | -| stl.cpp:110:16:110:20 | 123 | stl.cpp:110:16:110:21 | call to basic_string | TAINT | -| stl.cpp:110:16:110:21 | call to basic_string | stl.cpp:114:7:114:7 | b | | -| stl.cpp:110:16:110:21 | call to basic_string | stl.cpp:116:7:116:7 | b | | -| stl.cpp:111:16:111:21 | call to source | stl.cpp:111:16:111:24 | call to basic_string | TAINT | -| stl.cpp:111:16:111:24 | call to basic_string | stl.cpp:115:7:115:7 | c | | -| stl.cpp:111:16:111:24 | call to basic_string | stl.cpp:117:7:117:7 | c | | -| stl.cpp:116:7:116:7 | b | stl.cpp:116:9:116:13 | call to c_str | TAINT | -| stl.cpp:117:7:117:7 | c | stl.cpp:117:9:117:13 | call to c_str | TAINT | -| stl.cpp:122:20:122:22 | call to basic_stringstream | stl.cpp:125:2:125:4 | ss1 | | -| stl.cpp:122:20:122:22 | call to basic_stringstream | stl.cpp:131:7:131:9 | ss1 | | -| stl.cpp:122:20:122:22 | call to basic_stringstream | stl.cpp:136:7:136:9 | ss1 | | -| stl.cpp:122:25:122:27 | call to basic_stringstream | stl.cpp:126:2:126:4 | ss2 | | -| stl.cpp:122:25:122:27 | call to basic_stringstream | stl.cpp:132:7:132:9 | ss2 | | -| stl.cpp:122:25:122:27 | call to basic_stringstream | stl.cpp:137:7:137:9 | ss2 | | -| stl.cpp:122:30:122:32 | call to basic_stringstream | stl.cpp:127:2:127:4 | ss3 | | -| stl.cpp:122:30:122:32 | call to basic_stringstream | stl.cpp:133:7:133:9 | ss3 | | -| stl.cpp:122:30:122:32 | call to basic_stringstream | stl.cpp:138:7:138:9 | ss3 | | -| stl.cpp:122:35:122:37 | call to basic_stringstream | stl.cpp:128:2:128:4 | ss4 | | -| stl.cpp:122:35:122:37 | call to basic_stringstream | stl.cpp:134:7:134:9 | ss4 | | -| stl.cpp:122:35:122:37 | call to basic_stringstream | stl.cpp:139:7:139:9 | ss4 | | -| stl.cpp:122:40:122:42 | call to basic_stringstream | stl.cpp:129:2:129:4 | ss5 | | -| stl.cpp:122:40:122:42 | call to basic_stringstream | stl.cpp:135:7:135:9 | ss5 | | -| stl.cpp:122:40:122:42 | call to basic_stringstream | stl.cpp:140:7:140:9 | ss5 | | -| stl.cpp:123:16:123:21 | call to source | stl.cpp:123:16:123:24 | call to basic_string | TAINT | -| stl.cpp:123:16:123:24 | call to basic_string | stl.cpp:129:9:129:9 | t | | -| stl.cpp:125:2:125:4 | ref arg ss1 | stl.cpp:131:7:131:9 | ss1 | | -| stl.cpp:125:2:125:4 | ref arg ss1 | stl.cpp:136:7:136:9 | ss1 | | -| stl.cpp:126:2:126:4 | ref arg ss2 | stl.cpp:132:7:132:9 | ss2 | | -| stl.cpp:126:2:126:4 | ref arg ss2 | stl.cpp:137:7:137:9 | ss2 | | -| stl.cpp:127:2:127:4 | ref arg ss3 | stl.cpp:133:7:133:9 | ss3 | | -| stl.cpp:127:2:127:4 | ref arg ss3 | stl.cpp:138:7:138:9 | ss3 | | -| stl.cpp:128:2:128:4 | ref arg ss4 | stl.cpp:134:7:134:9 | ss4 | | -| stl.cpp:128:2:128:4 | ref arg ss4 | stl.cpp:139:7:139:9 | ss4 | | -| stl.cpp:129:2:129:4 | ref arg ss5 | stl.cpp:135:7:135:9 | ss5 | | -| stl.cpp:129:2:129:4 | ref arg ss5 | stl.cpp:140:7:140:9 | ss5 | | -| stl.cpp:143:32:143:37 | source | stl.cpp:148:9:148:14 | source | | -| stl.cpp:145:20:145:22 | call to basic_stringstream | stl.cpp:147:2:147:4 | ss1 | | -| stl.cpp:145:20:145:22 | call to basic_stringstream | stl.cpp:150:7:150:9 | ss1 | | -| stl.cpp:145:20:145:22 | call to basic_stringstream | stl.cpp:152:7:152:9 | ss1 | | -| stl.cpp:145:25:145:27 | call to basic_stringstream | stl.cpp:148:2:148:4 | ss2 | | -| stl.cpp:145:25:145:27 | call to basic_stringstream | stl.cpp:151:7:151:9 | ss2 | | -| stl.cpp:145:25:145:27 | call to basic_stringstream | stl.cpp:153:7:153:9 | ss2 | | -| stl.cpp:147:2:147:4 | ref arg ss1 | stl.cpp:150:7:150:9 | ss1 | | -| stl.cpp:147:2:147:4 | ref arg ss1 | stl.cpp:152:7:152:9 | ss1 | | -| stl.cpp:148:2:148:4 | ref arg ss2 | stl.cpp:151:7:151:9 | ss2 | | -| stl.cpp:148:2:148:4 | ref arg ss2 | stl.cpp:153:7:153:9 | ss2 | | -| stl.cpp:166:16:166:28 | call to basic_string | stl.cpp:167:7:167:11 | path1 | | -| stl.cpp:166:17:166:26 | call to user_input | stl.cpp:166:16:166:28 | call to basic_string | TAINT | -| stl.cpp:167:7:167:11 | path1 | stl.cpp:167:13:167:17 | call to c_str | TAINT | -| stl.cpp:170:10:170:19 | call to user_input | stl.cpp:170:10:170:21 | call to basic_string | TAINT | -| stl.cpp:170:10:170:21 | call to basic_string | stl.cpp:170:2:170:21 | ... = ... | | -| stl.cpp:170:10:170:21 | call to basic_string | stl.cpp:171:7:171:11 | path2 | | -| stl.cpp:171:7:171:11 | path2 | stl.cpp:171:13:171:17 | call to c_str | TAINT | -| stl.cpp:173:15:173:24 | call to user_input | stl.cpp:173:15:173:27 | call to basic_string | TAINT | -| stl.cpp:173:15:173:27 | call to basic_string | stl.cpp:174:7:174:11 | path3 | | -| stl.cpp:174:7:174:11 | path3 | stl.cpp:174:13:174:17 | call to c_str | TAINT | -| stl.cpp:179:19:179:24 | call to source | stl.cpp:182:17:182:18 | cs | | -| stl.cpp:179:19:179:24 | call to source | stl.cpp:184:7:184:8 | cs | | -| stl.cpp:182:17:182:18 | cs | stl.cpp:182:17:182:19 | call to basic_string | TAINT | -| stl.cpp:182:17:182:19 | call to basic_string | stl.cpp:185:7:185:8 | ss | | -| stl.cpp:190:19:190:24 | call to source | stl.cpp:193:17:193:18 | cs | | -| stl.cpp:193:17:193:18 | cs | stl.cpp:193:17:193:19 | call to basic_string | TAINT | -| stl.cpp:193:17:193:19 | call to basic_string | stl.cpp:196:7:196:8 | ss | | -| stl.cpp:193:17:193:19 | call to basic_string | stl.cpp:199:7:199:8 | ss | | -| stl.cpp:196:7:196:8 | ss | stl.cpp:196:10:196:14 | call to c_str | TAINT | -| stl.cpp:196:10:196:14 | call to c_str | stl.cpp:196:2:196:16 | ... = ... | | -| stl.cpp:196:10:196:14 | call to c_str | stl.cpp:198:7:198:8 | cs | | -| stl.cpp:205:18:205:24 | hello | stl.cpp:205:18:205:25 | call to basic_string | TAINT | -| stl.cpp:205:18:205:25 | call to basic_string | stl.cpp:210:8:210:9 | s1 | | -| stl.cpp:206:19:206:26 | call to basic_string | stl.cpp:211:8:211:9 | s2 | | -| stl.cpp:206:20:206:26 | hello | stl.cpp:206:19:206:26 | call to basic_string | TAINT | -| stl.cpp:208:8:208:14 | call to basic_string | stl.cpp:208:3:208:14 | ... = ... | | -| stl.cpp:208:8:208:14 | call to basic_string | stl.cpp:212:8:212:9 | s3 | | -| stl.cpp:208:8:208:14 | hello | stl.cpp:208:8:208:14 | call to basic_string | TAINT | -| stl.cpp:216:18:216:23 | call to source | stl.cpp:216:18:216:26 | call to basic_string | TAINT | -| stl.cpp:216:18:216:26 | call to basic_string | stl.cpp:221:8:221:9 | s1 | | -| stl.cpp:217:19:217:27 | call to basic_string | stl.cpp:222:8:222:9 | s2 | | -| stl.cpp:217:20:217:25 | call to source | stl.cpp:217:19:217:27 | call to basic_string | TAINT | -| stl.cpp:219:8:219:13 | call to source | stl.cpp:219:8:219:15 | call to basic_string | TAINT | -| stl.cpp:219:8:219:15 | call to basic_string | stl.cpp:219:3:219:15 | ... = ... | | -| stl.cpp:219:8:219:15 | call to basic_string | stl.cpp:223:8:223:9 | s3 | | -| stl.cpp:227:15:227:16 | call to basic_string | stl.cpp:228:20:228:21 | s1 | | -| stl.cpp:227:15:227:16 | call to basic_string | stl.cpp:230:8:230:9 | s1 | | -| stl.cpp:227:15:227:16 | call to basic_string | stl.cpp:232:8:232:9 | s1 | | -| stl.cpp:228:20:228:21 | s1 | stl.cpp:233:8:233:9 | s2 | | -| stl.cpp:230:8:230:9 | s1 | stl.cpp:230:3:230:9 | ... = ... | | -| stl.cpp:230:8:230:9 | s1 | stl.cpp:234:8:234:9 | s3 | | -| stl.cpp:238:19:238:40 | call to basic_string | stl.cpp:242:8:242:9 | s1 | | -| stl.cpp:238:32:238:37 | call to source | stl.cpp:238:19:238:40 | call to basic_string | TAINT | -| stl.cpp:240:8:240:28 | call to basic_string | stl.cpp:240:3:240:28 | ... = ... | | -| stl.cpp:240:8:240:28 | call to basic_string | stl.cpp:243:8:243:9 | s2 | | -| stl.cpp:240:20:240:25 | call to source | stl.cpp:240:8:240:28 | call to basic_string | TAINT | -| stl.cpp:250:16:250:21 | call to source | stl.cpp:250:16:250:24 | call to basic_string | TAINT | -| stl.cpp:250:16:250:24 | call to basic_string | stl.cpp:251:15:251:15 | s | | -| stl.cpp:250:16:250:24 | call to basic_string | stl.cpp:255:33:255:33 | s | | -| stl.cpp:250:16:250:24 | call to basic_string | stl.cpp:255:50:255:50 | s | | -| stl.cpp:250:16:250:24 | call to basic_string | stl.cpp:259:16:259:16 | s | | -| stl.cpp:251:15:251:15 | call to begin | stl.cpp:251:15:251:15 | (__begin) | | -| stl.cpp:251:15:251:15 | call to begin | stl.cpp:251:15:251:15 | (__begin) | | -| stl.cpp:251:15:251:15 | call to begin | stl.cpp:251:15:251:15 | (__begin) | | -| stl.cpp:251:15:251:15 | call to end | stl.cpp:251:15:251:15 | (__end) | | -| stl.cpp:251:15:251:15 | call to operator* | stl.cpp:252:8:252:8 | c | | -| stl.cpp:251:15:251:15 | ref arg (__begin) | stl.cpp:251:15:251:15 | (__begin) | | -| stl.cpp:251:15:251:15 | ref arg (__begin) | stl.cpp:251:15:251:15 | (__begin) | | -| stl.cpp:251:15:251:15 | ref arg (__begin) | stl.cpp:251:15:251:15 | (__begin) | | -| stl.cpp:251:15:251:15 | ref arg (__range) | stl.cpp:251:15:251:15 | (__range) | | -| stl.cpp:251:15:251:15 | s | stl.cpp:251:15:251:15 | (__range) | | -| stl.cpp:251:15:251:15 | s | stl.cpp:251:15:251:15 | (__range) | | -| stl.cpp:251:15:251:15 | s | stl.cpp:251:15:251:15 | call to operator* | TAINT | -| stl.cpp:255:33:255:33 | ref arg s | stl.cpp:255:50:255:50 | s | | -| stl.cpp:255:33:255:33 | ref arg s | stl.cpp:259:16:259:16 | s | | -| stl.cpp:255:35:255:39 | call to begin | stl.cpp:255:44:255:45 | it | | -| stl.cpp:255:35:255:39 | call to begin | stl.cpp:255:61:255:62 | it | | -| stl.cpp:255:35:255:39 | call to begin | stl.cpp:256:9:256:10 | it | | -| stl.cpp:255:50:255:50 | ref arg s | stl.cpp:255:50:255:50 | s | | -| stl.cpp:255:50:255:50 | ref arg s | stl.cpp:259:16:259:16 | s | | -| stl.cpp:255:61:255:62 | ref arg it | stl.cpp:255:44:255:45 | it | | -| stl.cpp:255:61:255:62 | ref arg it | stl.cpp:255:61:255:62 | it | | -| stl.cpp:255:61:255:62 | ref arg it | stl.cpp:256:9:256:10 | it | | -| stl.cpp:259:16:259:16 | call to begin | stl.cpp:259:16:259:16 | (__begin) | | -| stl.cpp:259:16:259:16 | call to begin | stl.cpp:259:16:259:16 | (__begin) | | -| stl.cpp:259:16:259:16 | call to begin | stl.cpp:259:16:259:16 | (__begin) | | -| stl.cpp:259:16:259:16 | call to end | stl.cpp:259:16:259:16 | (__end) | | -| stl.cpp:259:16:259:16 | call to operator* | stl.cpp:260:8:260:8 | c | | -| stl.cpp:259:16:259:16 | ref arg (__begin) | stl.cpp:259:16:259:16 | (__begin) | | -| stl.cpp:259:16:259:16 | ref arg (__begin) | stl.cpp:259:16:259:16 | (__begin) | | -| stl.cpp:259:16:259:16 | ref arg (__begin) | stl.cpp:259:16:259:16 | (__begin) | | -| stl.cpp:259:16:259:16 | ref arg (__range) | stl.cpp:259:16:259:16 | (__range) | | -| stl.cpp:259:16:259:16 | s | stl.cpp:259:16:259:16 | (__range) | | -| stl.cpp:259:16:259:16 | s | stl.cpp:259:16:259:16 | (__range) | | -| stl.cpp:259:16:259:16 | s | stl.cpp:259:16:259:16 | call to operator* | TAINT | -| stl.cpp:263:28:263:33 | call to source | stl.cpp:263:28:263:36 | call to basic_string | TAINT | -| stl.cpp:263:28:263:36 | call to basic_string | stl.cpp:264:22:264:28 | const_s | | -| stl.cpp:264:22:264:22 | call to begin | stl.cpp:264:22:264:22 | (__begin) | | -| stl.cpp:264:22:264:22 | call to begin | stl.cpp:264:22:264:22 | (__begin) | | -| stl.cpp:264:22:264:22 | call to begin | stl.cpp:264:22:264:22 | (__begin) | | -| stl.cpp:264:22:264:22 | call to end | stl.cpp:264:22:264:22 | (__end) | | -| stl.cpp:264:22:264:22 | call to operator* | stl.cpp:265:8:265:8 | c | | -| stl.cpp:264:22:264:22 | ref arg (__begin) | stl.cpp:264:22:264:22 | (__begin) | | -| stl.cpp:264:22:264:22 | ref arg (__begin) | stl.cpp:264:22:264:22 | (__begin) | | -| stl.cpp:264:22:264:22 | ref arg (__begin) | stl.cpp:264:22:264:22 | (__begin) | | -| stl.cpp:264:22:264:28 | const_s | stl.cpp:264:22:264:22 | (__range) | | -| stl.cpp:264:22:264:28 | const_s | stl.cpp:264:22:264:22 | (__range) | | -| stl.cpp:264:22:264:28 | const_s | stl.cpp:264:22:264:22 | call to operator* | TAINT | -| stl.cpp:300:43:300:49 | source1 | stl.cpp:304:21:304:27 | source1 | | -| stl.cpp:300:43:300:49 | source1 | stl.cpp:318:33:318:39 | source1 | | -| stl.cpp:304:21:304:27 | source1 | stl.cpp:304:21:304:28 | call to vector | TAINT | -| stl.cpp:304:21:304:28 | call to vector | stl.cpp:306:14:306:14 | v | | -| stl.cpp:304:21:304:28 | call to vector | stl.cpp:310:38:310:38 | v | | -| stl.cpp:304:21:304:28 | call to vector | stl.cpp:310:55:310:55 | v | | -| stl.cpp:304:21:304:28 | call to vector | stl.cpp:314:15:314:15 | v | | -| stl.cpp:306:14:306:14 | call to begin | stl.cpp:306:14:306:14 | (__begin) | | -| stl.cpp:306:14:306:14 | call to begin | stl.cpp:306:14:306:14 | (__begin) | | -| stl.cpp:306:14:306:14 | call to begin | stl.cpp:306:14:306:14 | (__begin) | | -| stl.cpp:306:14:306:14 | call to end | stl.cpp:306:14:306:14 | (__end) | | -| stl.cpp:306:14:306:14 | call to operator* | stl.cpp:307:8:307:8 | x | | -| stl.cpp:306:14:306:14 | ref arg (__begin) | stl.cpp:306:14:306:14 | (__begin) | | -| stl.cpp:306:14:306:14 | ref arg (__begin) | stl.cpp:306:14:306:14 | (__begin) | | -| stl.cpp:306:14:306:14 | ref arg (__begin) | stl.cpp:306:14:306:14 | (__begin) | | -| stl.cpp:306:14:306:14 | ref arg (__range) | stl.cpp:306:14:306:14 | (__range) | | -| stl.cpp:306:14:306:14 | v | stl.cpp:306:14:306:14 | (__range) | | -| stl.cpp:306:14:306:14 | v | stl.cpp:306:14:306:14 | (__range) | | -| stl.cpp:306:14:306:14 | v | stl.cpp:306:14:306:14 | call to operator* | TAINT | -| stl.cpp:310:38:310:38 | ref arg v | stl.cpp:310:55:310:55 | v | | -| stl.cpp:310:38:310:38 | ref arg v | stl.cpp:314:15:314:15 | v | | -| stl.cpp:310:40:310:44 | call to begin | stl.cpp:310:49:310:50 | it | | -| stl.cpp:310:40:310:44 | call to begin | stl.cpp:310:66:310:67 | it | | -| stl.cpp:310:40:310:44 | call to begin | stl.cpp:311:9:311:10 | it | | -| stl.cpp:310:55:310:55 | ref arg v | stl.cpp:310:55:310:55 | v | | -| stl.cpp:310:55:310:55 | ref arg v | stl.cpp:314:15:314:15 | v | | -| stl.cpp:310:66:310:67 | ref arg it | stl.cpp:310:49:310:50 | it | | -| stl.cpp:310:66:310:67 | ref arg it | stl.cpp:310:66:310:67 | it | | -| stl.cpp:310:66:310:67 | ref arg it | stl.cpp:311:9:311:10 | it | | -| stl.cpp:314:15:314:15 | call to begin | stl.cpp:314:15:314:15 | (__begin) | | -| stl.cpp:314:15:314:15 | call to begin | stl.cpp:314:15:314:15 | (__begin) | | -| stl.cpp:314:15:314:15 | call to begin | stl.cpp:314:15:314:15 | (__begin) | | -| stl.cpp:314:15:314:15 | call to end | stl.cpp:314:15:314:15 | (__end) | | -| stl.cpp:314:15:314:15 | call to operator* | stl.cpp:315:8:315:8 | x | | -| stl.cpp:314:15:314:15 | ref arg (__begin) | stl.cpp:314:15:314:15 | (__begin) | | -| stl.cpp:314:15:314:15 | ref arg (__begin) | stl.cpp:314:15:314:15 | (__begin) | | -| stl.cpp:314:15:314:15 | ref arg (__begin) | stl.cpp:314:15:314:15 | (__begin) | | -| stl.cpp:314:15:314:15 | ref arg (__range) | stl.cpp:314:15:314:15 | (__range) | | -| stl.cpp:314:15:314:15 | v | stl.cpp:314:15:314:15 | (__range) | | -| stl.cpp:314:15:314:15 | v | stl.cpp:314:15:314:15 | (__range) | | -| stl.cpp:314:15:314:15 | v | stl.cpp:314:15:314:15 | call to operator* | TAINT | -| stl.cpp:318:33:318:39 | source1 | stl.cpp:318:33:318:40 | call to vector | TAINT | -| stl.cpp:318:33:318:40 | call to vector | stl.cpp:319:21:319:27 | const_v | | -| stl.cpp:319:21:319:21 | call to begin | stl.cpp:319:21:319:21 | (__begin) | | -| stl.cpp:319:21:319:21 | call to begin | stl.cpp:319:21:319:21 | (__begin) | | -| stl.cpp:319:21:319:21 | call to begin | stl.cpp:319:21:319:21 | (__begin) | | -| stl.cpp:319:21:319:21 | call to end | stl.cpp:319:21:319:21 | (__end) | | -| stl.cpp:319:21:319:21 | call to operator* | stl.cpp:320:8:320:8 | x | | -| stl.cpp:319:21:319:21 | ref arg (__begin) | stl.cpp:319:21:319:21 | (__begin) | | -| stl.cpp:319:21:319:21 | ref arg (__begin) | stl.cpp:319:21:319:21 | (__begin) | | -| stl.cpp:319:21:319:21 | ref arg (__begin) | stl.cpp:319:21:319:21 | (__begin) | | -| stl.cpp:319:21:319:27 | const_v | stl.cpp:319:21:319:21 | (__range) | | -| stl.cpp:319:21:319:27 | const_v | stl.cpp:319:21:319:21 | (__range) | | -| stl.cpp:319:21:319:27 | const_v | stl.cpp:319:21:319:21 | call to operator* | TAINT | -| stl.cpp:331:18:331:24 | hello | stl.cpp:331:18:331:25 | call to basic_string | TAINT | -| stl.cpp:331:18:331:25 | call to basic_string | stl.cpp:334:8:334:9 | s1 | | -| stl.cpp:331:18:331:25 | call to basic_string | stl.cpp:334:13:334:14 | s1 | | -| stl.cpp:331:18:331:25 | call to basic_string | stl.cpp:335:8:335:9 | s1 | | -| stl.cpp:331:18:331:25 | call to basic_string | stl.cpp:336:13:336:14 | s1 | | -| stl.cpp:331:18:331:25 | call to basic_string | stl.cpp:339:8:339:9 | s1 | | -| stl.cpp:331:18:331:25 | call to basic_string | stl.cpp:340:8:340:9 | s1 | | -| stl.cpp:332:18:332:23 | call to source | stl.cpp:332:18:332:26 | call to basic_string | TAINT | -| stl.cpp:332:18:332:26 | call to basic_string | stl.cpp:335:13:335:14 | s2 | | -| stl.cpp:332:18:332:26 | call to basic_string | stl.cpp:336:8:336:9 | s2 | | -| stl.cpp:332:18:332:26 | call to basic_string | stl.cpp:337:8:337:9 | s2 | | -| stl.cpp:332:18:332:26 | call to basic_string | stl.cpp:337:13:337:14 | s2 | | -| stl.cpp:334:8:334:9 | s1 | stl.cpp:334:11:334:11 | call to operator+ | TAINT | -| stl.cpp:334:13:334:14 | s1 | stl.cpp:334:11:334:11 | call to operator+ | TAINT | -| stl.cpp:335:8:335:9 | s1 | stl.cpp:335:11:335:11 | call to operator+ | TAINT | -| stl.cpp:335:13:335:14 | s2 | stl.cpp:335:11:335:11 | call to operator+ | TAINT | -| stl.cpp:336:8:336:9 | s2 | stl.cpp:336:11:336:11 | call to operator+ | TAINT | -| stl.cpp:336:13:336:14 | s1 | stl.cpp:336:11:336:11 | call to operator+ | TAINT | -| stl.cpp:337:8:337:9 | s2 | stl.cpp:337:11:337:11 | call to operator+ | TAINT | -| stl.cpp:337:13:337:14 | s2 | stl.cpp:337:11:337:11 | call to operator+ | TAINT | -| stl.cpp:339:8:339:9 | s1 | stl.cpp:339:11:339:11 | call to operator+ | TAINT | -| stl.cpp:339:13:339:20 | world | stl.cpp:339:11:339:11 | call to operator+ | TAINT | -| stl.cpp:340:8:340:9 | s1 | stl.cpp:340:11:340:11 | call to operator+ | TAINT | -| stl.cpp:340:13:340:18 | call to source | stl.cpp:340:11:340:11 | call to operator+ | TAINT | -| stl.cpp:344:18:344:22 | abc | stl.cpp:344:18:344:23 | call to basic_string | TAINT | -| stl.cpp:344:18:344:23 | call to basic_string | stl.cpp:348:8:348:9 | s3 | | -| stl.cpp:344:18:344:23 | call to basic_string | stl.cpp:351:8:351:9 | s3 | | -| stl.cpp:344:18:344:23 | call to basic_string | stl.cpp:355:8:355:9 | s3 | | -| stl.cpp:344:18:344:23 | call to basic_string | stl.cpp:360:8:360:9 | s3 | | -| stl.cpp:344:18:344:23 | call to basic_string | stl.cpp:364:8:364:9 | s3 | | -| stl.cpp:345:18:345:23 | call to source | stl.cpp:345:18:345:26 | call to basic_string | TAINT | -| stl.cpp:345:18:345:26 | call to basic_string | stl.cpp:348:13:348:14 | s4 | | -| stl.cpp:345:18:345:26 | call to basic_string | stl.cpp:352:9:352:10 | s4 | | -| stl.cpp:345:18:345:26 | call to basic_string | stl.cpp:361:13:361:14 | s4 | | -| stl.cpp:348:8:348:9 | s3 | stl.cpp:348:11:348:11 | call to operator+ | TAINT | -| stl.cpp:348:11:348:11 | call to operator+ | stl.cpp:348:3:348:14 | ... = ... | | -| stl.cpp:348:11:348:11 | call to operator+ | stl.cpp:349:8:349:9 | s5 | | -| stl.cpp:348:13:348:14 | s4 | stl.cpp:348:11:348:11 | call to operator+ | TAINT | -| stl.cpp:351:8:351:9 | s3 | stl.cpp:351:3:351:9 | ... = ... | | -| stl.cpp:351:8:351:9 | s3 | stl.cpp:352:3:352:4 | s6 | | -| stl.cpp:351:8:351:9 | s3 | stl.cpp:353:8:353:9 | s6 | | -| stl.cpp:352:3:352:4 | ref arg s6 | stl.cpp:353:8:353:9 | s6 | | -| stl.cpp:352:9:352:10 | s4 | stl.cpp:352:3:352:4 | ref arg s6 | TAINT | -| stl.cpp:352:9:352:10 | s4 | stl.cpp:352:6:352:6 | call to operator+= | TAINT | -| stl.cpp:355:8:355:9 | s3 | stl.cpp:355:3:355:9 | ... = ... | | -| stl.cpp:355:8:355:9 | s3 | stl.cpp:356:3:356:4 | s7 | | -| stl.cpp:355:8:355:9 | s3 | stl.cpp:357:3:357:4 | s7 | | -| stl.cpp:355:8:355:9 | s3 | stl.cpp:358:8:358:9 | s7 | | -| stl.cpp:356:3:356:4 | ref arg s7 | stl.cpp:357:3:357:4 | s7 | | -| stl.cpp:356:3:356:4 | ref arg s7 | stl.cpp:358:8:358:9 | s7 | | -| stl.cpp:356:9:356:14 | call to source | stl.cpp:356:3:356:4 | ref arg s7 | TAINT | -| stl.cpp:356:9:356:14 | call to source | stl.cpp:356:6:356:6 | call to operator+= | TAINT | -| stl.cpp:357:3:357:4 | ref arg s7 | stl.cpp:358:8:358:9 | s7 | | -| stl.cpp:357:9:357:11 | | stl.cpp:357:3:357:4 | ref arg s7 | TAINT | -| stl.cpp:357:9:357:11 | | stl.cpp:357:6:357:6 | call to operator+= | TAINT | -| stl.cpp:360:8:360:9 | s3 | stl.cpp:360:3:360:9 | ... = ... | | -| stl.cpp:360:8:360:9 | s3 | stl.cpp:361:3:361:4 | s8 | | -| stl.cpp:360:8:360:9 | s3 | stl.cpp:362:8:362:9 | s8 | | -| stl.cpp:361:3:361:4 | ref arg s8 | stl.cpp:362:8:362:9 | s8 | | -| stl.cpp:361:13:361:14 | s4 | stl.cpp:361:3:361:4 | ref arg s8 | TAINT | -| stl.cpp:361:13:361:14 | s4 | stl.cpp:361:6:361:11 | call to append | TAINT | -| stl.cpp:364:8:364:9 | s3 | stl.cpp:364:3:364:9 | ... = ... | | -| stl.cpp:364:8:364:9 | s3 | stl.cpp:365:3:365:4 | s9 | | -| stl.cpp:364:8:364:9 | s3 | stl.cpp:366:3:366:4 | s9 | | -| stl.cpp:364:8:364:9 | s3 | stl.cpp:367:8:367:9 | s9 | | -| stl.cpp:365:3:365:4 | ref arg s9 | stl.cpp:366:3:366:4 | s9 | | -| stl.cpp:365:3:365:4 | ref arg s9 | stl.cpp:367:8:367:9 | s9 | | -| stl.cpp:365:13:365:18 | call to source | stl.cpp:365:3:365:4 | ref arg s9 | TAINT | -| stl.cpp:365:13:365:18 | call to source | stl.cpp:365:6:365:11 | call to append | TAINT | -| stl.cpp:366:3:366:4 | ref arg s9 | stl.cpp:367:8:367:9 | s9 | | -| stl.cpp:366:13:366:15 | | stl.cpp:366:3:366:4 | ref arg s9 | TAINT | -| stl.cpp:366:13:366:15 | | stl.cpp:366:6:366:11 | call to append | TAINT | -| stl.cpp:371:19:371:23 | abc | stl.cpp:371:19:371:24 | call to basic_string | TAINT | -| stl.cpp:371:19:371:24 | call to basic_string | stl.cpp:374:3:374:5 | s10 | | -| stl.cpp:371:19:371:24 | call to basic_string | stl.cpp:375:8:375:10 | s10 | | -| stl.cpp:372:12:372:26 | call to source | stl.cpp:374:17:374:17 | c | | -| stl.cpp:374:3:374:5 | ref arg s10 | stl.cpp:375:8:375:10 | s10 | | -| stl.cpp:374:17:374:17 | c | stl.cpp:374:3:374:5 | ref arg s10 | TAINT | -| stl.cpp:374:17:374:17 | c | stl.cpp:374:7:374:12 | call to append | TAINT | +| string.cpp:24:12:24:17 | call to source | string.cpp:28:7:28:7 | a | | +| string.cpp:25:16:25:20 | 123 | string.cpp:25:16:25:21 | call to basic_string | TAINT | +| string.cpp:25:16:25:21 | call to basic_string | string.cpp:29:7:29:7 | b | | +| string.cpp:25:16:25:21 | call to basic_string | string.cpp:31:7:31:7 | b | | +| string.cpp:26:16:26:21 | call to source | string.cpp:26:16:26:24 | call to basic_string | TAINT | +| string.cpp:26:16:26:24 | call to basic_string | string.cpp:30:7:30:7 | c | | +| string.cpp:26:16:26:24 | call to basic_string | string.cpp:32:7:32:7 | c | | +| string.cpp:31:7:31:7 | b | string.cpp:31:9:31:13 | call to c_str | TAINT | +| string.cpp:32:7:32:7 | c | string.cpp:32:9:32:13 | call to c_str | TAINT | +| string.cpp:37:16:37:28 | call to basic_string | string.cpp:38:7:38:11 | path1 | | +| string.cpp:37:17:37:26 | call to user_input | string.cpp:37:16:37:28 | call to basic_string | TAINT | +| string.cpp:38:7:38:11 | path1 | string.cpp:38:13:38:17 | call to c_str | TAINT | +| string.cpp:41:10:41:19 | call to user_input | string.cpp:41:10:41:21 | call to basic_string | TAINT | +| string.cpp:41:10:41:21 | call to basic_string | string.cpp:41:2:41:21 | ... = ... | | +| string.cpp:41:10:41:21 | call to basic_string | string.cpp:42:7:42:11 | path2 | | +| string.cpp:42:7:42:11 | path2 | string.cpp:42:13:42:17 | call to c_str | TAINT | +| string.cpp:44:15:44:24 | call to user_input | string.cpp:44:15:44:27 | call to basic_string | TAINT | +| string.cpp:44:15:44:27 | call to basic_string | string.cpp:45:7:45:11 | path3 | | +| string.cpp:45:7:45:11 | path3 | string.cpp:45:13:45:17 | call to c_str | TAINT | +| string.cpp:50:19:50:24 | call to source | string.cpp:53:17:53:18 | cs | | +| string.cpp:50:19:50:24 | call to source | string.cpp:55:7:55:8 | cs | | +| string.cpp:53:17:53:18 | cs | string.cpp:53:17:53:19 | call to basic_string | TAINT | +| string.cpp:53:17:53:19 | call to basic_string | string.cpp:56:7:56:8 | ss | | +| string.cpp:61:19:61:24 | call to source | string.cpp:64:17:64:18 | cs | | +| string.cpp:64:17:64:18 | cs | string.cpp:64:17:64:19 | call to basic_string | TAINT | +| string.cpp:64:17:64:19 | call to basic_string | string.cpp:67:7:67:8 | ss | | +| string.cpp:64:17:64:19 | call to basic_string | string.cpp:70:7:70:8 | ss | | +| string.cpp:67:7:67:8 | ss | string.cpp:67:10:67:14 | call to c_str | TAINT | +| string.cpp:67:10:67:14 | call to c_str | string.cpp:67:2:67:16 | ... = ... | | +| string.cpp:67:10:67:14 | call to c_str | string.cpp:69:7:69:8 | cs | | +| string.cpp:76:18:76:24 | hello | string.cpp:76:18:76:25 | call to basic_string | TAINT | +| string.cpp:76:18:76:25 | call to basic_string | string.cpp:81:8:81:9 | s1 | | +| string.cpp:77:19:77:26 | call to basic_string | string.cpp:82:8:82:9 | s2 | | +| string.cpp:77:20:77:26 | hello | string.cpp:77:19:77:26 | call to basic_string | TAINT | +| string.cpp:79:8:79:14 | call to basic_string | string.cpp:79:3:79:14 | ... = ... | | +| string.cpp:79:8:79:14 | call to basic_string | string.cpp:83:8:83:9 | s3 | | +| string.cpp:79:8:79:14 | hello | string.cpp:79:8:79:14 | call to basic_string | TAINT | +| string.cpp:87:18:87:23 | call to source | string.cpp:87:18:87:26 | call to basic_string | TAINT | +| string.cpp:87:18:87:26 | call to basic_string | string.cpp:92:8:92:9 | s1 | | +| string.cpp:88:19:88:27 | call to basic_string | string.cpp:93:8:93:9 | s2 | | +| string.cpp:88:20:88:25 | call to source | string.cpp:88:19:88:27 | call to basic_string | TAINT | +| string.cpp:90:8:90:13 | call to source | string.cpp:90:8:90:15 | call to basic_string | TAINT | +| string.cpp:90:8:90:15 | call to basic_string | string.cpp:90:3:90:15 | ... = ... | | +| string.cpp:90:8:90:15 | call to basic_string | string.cpp:94:8:94:9 | s3 | | +| string.cpp:98:15:98:16 | call to basic_string | string.cpp:99:20:99:21 | s1 | | +| string.cpp:98:15:98:16 | call to basic_string | string.cpp:101:8:101:9 | s1 | | +| string.cpp:98:15:98:16 | call to basic_string | string.cpp:103:8:103:9 | s1 | | +| string.cpp:99:20:99:21 | s1 | string.cpp:104:8:104:9 | s2 | | +| string.cpp:101:8:101:9 | s1 | string.cpp:101:3:101:9 | ... = ... | | +| string.cpp:101:8:101:9 | s1 | string.cpp:105:8:105:9 | s3 | | +| string.cpp:109:19:109:40 | call to basic_string | string.cpp:113:8:113:9 | s1 | | +| string.cpp:109:32:109:37 | call to source | string.cpp:109:19:109:40 | call to basic_string | TAINT | +| string.cpp:111:8:111:28 | call to basic_string | string.cpp:111:3:111:28 | ... = ... | | +| string.cpp:111:8:111:28 | call to basic_string | string.cpp:114:8:114:9 | s2 | | +| string.cpp:111:20:111:25 | call to source | string.cpp:111:8:111:28 | call to basic_string | TAINT | +| string.cpp:119:16:119:21 | call to source | string.cpp:119:16:119:24 | call to basic_string | TAINT | +| string.cpp:119:16:119:24 | call to basic_string | string.cpp:120:15:120:15 | s | | +| string.cpp:119:16:119:24 | call to basic_string | string.cpp:124:33:124:33 | s | | +| string.cpp:119:16:119:24 | call to basic_string | string.cpp:124:50:124:50 | s | | +| string.cpp:119:16:119:24 | call to basic_string | string.cpp:128:16:128:16 | s | | +| string.cpp:120:15:120:15 | call to begin | string.cpp:120:15:120:15 | (__begin) | | +| string.cpp:120:15:120:15 | call to begin | string.cpp:120:15:120:15 | (__begin) | | +| string.cpp:120:15:120:15 | call to begin | string.cpp:120:15:120:15 | (__begin) | | +| string.cpp:120:15:120:15 | call to end | string.cpp:120:15:120:15 | (__end) | | +| string.cpp:120:15:120:15 | call to operator* | string.cpp:121:8:121:8 | c | | +| string.cpp:120:15:120:15 | ref arg (__begin) | string.cpp:120:15:120:15 | (__begin) | | +| string.cpp:120:15:120:15 | ref arg (__begin) | string.cpp:120:15:120:15 | (__begin) | | +| string.cpp:120:15:120:15 | ref arg (__begin) | string.cpp:120:15:120:15 | (__begin) | | +| string.cpp:120:15:120:15 | ref arg (__range) | string.cpp:120:15:120:15 | (__range) | | +| string.cpp:120:15:120:15 | s | string.cpp:120:15:120:15 | (__range) | | +| string.cpp:120:15:120:15 | s | string.cpp:120:15:120:15 | (__range) | | +| string.cpp:120:15:120:15 | s | string.cpp:120:15:120:15 | call to operator* | TAINT | +| string.cpp:124:33:124:33 | ref arg s | string.cpp:124:50:124:50 | s | | +| string.cpp:124:33:124:33 | ref arg s | string.cpp:128:16:128:16 | s | | +| string.cpp:124:35:124:39 | call to begin | string.cpp:124:44:124:45 | it | | +| string.cpp:124:35:124:39 | call to begin | string.cpp:124:61:124:62 | it | | +| string.cpp:124:35:124:39 | call to begin | string.cpp:125:9:125:10 | it | | +| string.cpp:124:50:124:50 | ref arg s | string.cpp:124:50:124:50 | s | | +| string.cpp:124:50:124:50 | ref arg s | string.cpp:128:16:128:16 | s | | +| string.cpp:124:61:124:62 | ref arg it | string.cpp:124:44:124:45 | it | | +| string.cpp:124:61:124:62 | ref arg it | string.cpp:124:61:124:62 | it | | +| string.cpp:124:61:124:62 | ref arg it | string.cpp:125:9:125:10 | it | | +| string.cpp:128:16:128:16 | call to begin | string.cpp:128:16:128:16 | (__begin) | | +| string.cpp:128:16:128:16 | call to begin | string.cpp:128:16:128:16 | (__begin) | | +| string.cpp:128:16:128:16 | call to begin | string.cpp:128:16:128:16 | (__begin) | | +| string.cpp:128:16:128:16 | call to end | string.cpp:128:16:128:16 | (__end) | | +| string.cpp:128:16:128:16 | call to operator* | string.cpp:129:8:129:8 | c | | +| string.cpp:128:16:128:16 | ref arg (__begin) | string.cpp:128:16:128:16 | (__begin) | | +| string.cpp:128:16:128:16 | ref arg (__begin) | string.cpp:128:16:128:16 | (__begin) | | +| string.cpp:128:16:128:16 | ref arg (__begin) | string.cpp:128:16:128:16 | (__begin) | | +| string.cpp:128:16:128:16 | ref arg (__range) | string.cpp:128:16:128:16 | (__range) | | +| string.cpp:128:16:128:16 | s | string.cpp:128:16:128:16 | (__range) | | +| string.cpp:128:16:128:16 | s | string.cpp:128:16:128:16 | (__range) | | +| string.cpp:128:16:128:16 | s | string.cpp:128:16:128:16 | call to operator* | TAINT | +| string.cpp:132:28:132:33 | call to source | string.cpp:132:28:132:36 | call to basic_string | TAINT | +| string.cpp:132:28:132:36 | call to basic_string | string.cpp:133:22:133:28 | const_s | | +| string.cpp:133:22:133:22 | call to begin | string.cpp:133:22:133:22 | (__begin) | | +| string.cpp:133:22:133:22 | call to begin | string.cpp:133:22:133:22 | (__begin) | | +| string.cpp:133:22:133:22 | call to begin | string.cpp:133:22:133:22 | (__begin) | | +| string.cpp:133:22:133:22 | call to end | string.cpp:133:22:133:22 | (__end) | | +| string.cpp:133:22:133:22 | call to operator* | string.cpp:134:8:134:8 | c | | +| string.cpp:133:22:133:22 | ref arg (__begin) | string.cpp:133:22:133:22 | (__begin) | | +| string.cpp:133:22:133:22 | ref arg (__begin) | string.cpp:133:22:133:22 | (__begin) | | +| string.cpp:133:22:133:22 | ref arg (__begin) | string.cpp:133:22:133:22 | (__begin) | | +| string.cpp:133:22:133:28 | const_s | string.cpp:133:22:133:22 | (__range) | | +| string.cpp:133:22:133:28 | const_s | string.cpp:133:22:133:22 | (__range) | | +| string.cpp:133:22:133:28 | const_s | string.cpp:133:22:133:22 | call to operator* | TAINT | +| string.cpp:140:18:140:24 | hello | string.cpp:140:18:140:25 | call to basic_string | TAINT | +| string.cpp:140:18:140:25 | call to basic_string | string.cpp:143:8:143:9 | s1 | | +| string.cpp:140:18:140:25 | call to basic_string | string.cpp:143:13:143:14 | s1 | | +| string.cpp:140:18:140:25 | call to basic_string | string.cpp:144:8:144:9 | s1 | | +| string.cpp:140:18:140:25 | call to basic_string | string.cpp:145:13:145:14 | s1 | | +| string.cpp:140:18:140:25 | call to basic_string | string.cpp:148:8:148:9 | s1 | | +| string.cpp:140:18:140:25 | call to basic_string | string.cpp:149:8:149:9 | s1 | | +| string.cpp:141:18:141:23 | call to source | string.cpp:141:18:141:26 | call to basic_string | TAINT | +| string.cpp:141:18:141:26 | call to basic_string | string.cpp:144:13:144:14 | s2 | | +| string.cpp:141:18:141:26 | call to basic_string | string.cpp:145:8:145:9 | s2 | | +| string.cpp:141:18:141:26 | call to basic_string | string.cpp:146:8:146:9 | s2 | | +| string.cpp:141:18:141:26 | call to basic_string | string.cpp:146:13:146:14 | s2 | | +| string.cpp:143:8:143:9 | s1 | string.cpp:143:11:143:11 | call to operator+ | TAINT | +| string.cpp:143:13:143:14 | s1 | string.cpp:143:11:143:11 | call to operator+ | TAINT | +| string.cpp:144:8:144:9 | s1 | string.cpp:144:11:144:11 | call to operator+ | TAINT | +| string.cpp:144:13:144:14 | s2 | string.cpp:144:11:144:11 | call to operator+ | TAINT | +| string.cpp:145:8:145:9 | s2 | string.cpp:145:11:145:11 | call to operator+ | TAINT | +| string.cpp:145:13:145:14 | s1 | string.cpp:145:11:145:11 | call to operator+ | TAINT | +| string.cpp:146:8:146:9 | s2 | string.cpp:146:11:146:11 | call to operator+ | TAINT | +| string.cpp:146:13:146:14 | s2 | string.cpp:146:11:146:11 | call to operator+ | TAINT | +| string.cpp:148:8:148:9 | s1 | string.cpp:148:11:148:11 | call to operator+ | TAINT | +| string.cpp:148:13:148:20 | world | string.cpp:148:11:148:11 | call to operator+ | TAINT | +| string.cpp:149:8:149:9 | s1 | string.cpp:149:11:149:11 | call to operator+ | TAINT | +| string.cpp:149:13:149:18 | call to source | string.cpp:149:11:149:11 | call to operator+ | TAINT | +| string.cpp:153:18:153:22 | abc | string.cpp:153:18:153:23 | call to basic_string | TAINT | +| string.cpp:153:18:153:23 | call to basic_string | string.cpp:157:8:157:9 | s3 | | +| string.cpp:153:18:153:23 | call to basic_string | string.cpp:160:8:160:9 | s3 | | +| string.cpp:153:18:153:23 | call to basic_string | string.cpp:164:8:164:9 | s3 | | +| string.cpp:153:18:153:23 | call to basic_string | string.cpp:169:8:169:9 | s3 | | +| string.cpp:153:18:153:23 | call to basic_string | string.cpp:173:8:173:9 | s3 | | +| string.cpp:154:18:154:23 | call to source | string.cpp:154:18:154:26 | call to basic_string | TAINT | +| string.cpp:154:18:154:26 | call to basic_string | string.cpp:157:13:157:14 | s4 | | +| string.cpp:154:18:154:26 | call to basic_string | string.cpp:161:9:161:10 | s4 | | +| string.cpp:154:18:154:26 | call to basic_string | string.cpp:170:13:170:14 | s4 | | +| string.cpp:157:8:157:9 | s3 | string.cpp:157:11:157:11 | call to operator+ | TAINT | +| string.cpp:157:11:157:11 | call to operator+ | string.cpp:157:3:157:14 | ... = ... | | +| string.cpp:157:11:157:11 | call to operator+ | string.cpp:158:8:158:9 | s5 | | +| string.cpp:157:13:157:14 | s4 | string.cpp:157:11:157:11 | call to operator+ | TAINT | +| string.cpp:160:8:160:9 | s3 | string.cpp:160:3:160:9 | ... = ... | | +| string.cpp:160:8:160:9 | s3 | string.cpp:161:3:161:4 | s6 | | +| string.cpp:160:8:160:9 | s3 | string.cpp:162:8:162:9 | s6 | | +| string.cpp:161:3:161:4 | ref arg s6 | string.cpp:162:8:162:9 | s6 | | +| string.cpp:161:9:161:10 | s4 | string.cpp:161:3:161:4 | ref arg s6 | TAINT | +| string.cpp:161:9:161:10 | s4 | string.cpp:161:6:161:6 | call to operator+= | TAINT | +| string.cpp:164:8:164:9 | s3 | string.cpp:164:3:164:9 | ... = ... | | +| string.cpp:164:8:164:9 | s3 | string.cpp:165:3:165:4 | s7 | | +| string.cpp:164:8:164:9 | s3 | string.cpp:166:3:166:4 | s7 | | +| string.cpp:164:8:164:9 | s3 | string.cpp:167:8:167:9 | s7 | | +| string.cpp:165:3:165:4 | ref arg s7 | string.cpp:166:3:166:4 | s7 | | +| string.cpp:165:3:165:4 | ref arg s7 | string.cpp:167:8:167:9 | s7 | | +| string.cpp:165:9:165:14 | call to source | string.cpp:165:3:165:4 | ref arg s7 | TAINT | +| string.cpp:165:9:165:14 | call to source | string.cpp:165:6:165:6 | call to operator+= | TAINT | +| string.cpp:166:3:166:4 | ref arg s7 | string.cpp:167:8:167:9 | s7 | | +| string.cpp:166:9:166:11 | | string.cpp:166:3:166:4 | ref arg s7 | TAINT | +| string.cpp:166:9:166:11 | | string.cpp:166:6:166:6 | call to operator+= | TAINT | +| string.cpp:169:8:169:9 | s3 | string.cpp:169:3:169:9 | ... = ... | | +| string.cpp:169:8:169:9 | s3 | string.cpp:170:3:170:4 | s8 | | +| string.cpp:169:8:169:9 | s3 | string.cpp:171:8:171:9 | s8 | | +| string.cpp:170:3:170:4 | ref arg s8 | string.cpp:171:8:171:9 | s8 | | +| string.cpp:170:13:170:14 | s4 | string.cpp:170:3:170:4 | ref arg s8 | TAINT | +| string.cpp:170:13:170:14 | s4 | string.cpp:170:6:170:11 | call to append | TAINT | +| string.cpp:173:8:173:9 | s3 | string.cpp:173:3:173:9 | ... = ... | | +| string.cpp:173:8:173:9 | s3 | string.cpp:174:3:174:4 | s9 | | +| string.cpp:173:8:173:9 | s3 | string.cpp:175:3:175:4 | s9 | | +| string.cpp:173:8:173:9 | s3 | string.cpp:176:8:176:9 | s9 | | +| string.cpp:174:3:174:4 | ref arg s9 | string.cpp:175:3:175:4 | s9 | | +| string.cpp:174:3:174:4 | ref arg s9 | string.cpp:176:8:176:9 | s9 | | +| string.cpp:174:13:174:18 | call to source | string.cpp:174:3:174:4 | ref arg s9 | TAINT | +| string.cpp:174:13:174:18 | call to source | string.cpp:174:6:174:11 | call to append | TAINT | +| string.cpp:175:3:175:4 | ref arg s9 | string.cpp:176:8:176:9 | s9 | | +| string.cpp:175:13:175:15 | | string.cpp:175:3:175:4 | ref arg s9 | TAINT | +| string.cpp:175:13:175:15 | | string.cpp:175:6:175:11 | call to append | TAINT | +| string.cpp:180:19:180:23 | abc | string.cpp:180:19:180:24 | call to basic_string | TAINT | +| string.cpp:180:19:180:24 | call to basic_string | string.cpp:183:3:183:5 | s10 | | +| string.cpp:180:19:180:24 | call to basic_string | string.cpp:184:8:184:10 | s10 | | +| string.cpp:181:12:181:26 | call to source | string.cpp:183:17:183:17 | c | | +| string.cpp:183:3:183:5 | ref arg s10 | string.cpp:184:8:184:10 | s10 | | +| string.cpp:183:17:183:17 | c | string.cpp:183:3:183:5 | ref arg s10 | TAINT | +| string.cpp:183:17:183:17 | c | string.cpp:183:7:183:12 | call to append | TAINT | +| stringstream.cpp:13:20:13:22 | call to basic_stringstream | stringstream.cpp:16:2:16:4 | ss1 | | +| stringstream.cpp:13:20:13:22 | call to basic_stringstream | stringstream.cpp:22:7:22:9 | ss1 | | +| stringstream.cpp:13:20:13:22 | call to basic_stringstream | stringstream.cpp:27:7:27:9 | ss1 | | +| stringstream.cpp:13:25:13:27 | call to basic_stringstream | stringstream.cpp:17:2:17:4 | ss2 | | +| stringstream.cpp:13:25:13:27 | call to basic_stringstream | stringstream.cpp:23:7:23:9 | ss2 | | +| stringstream.cpp:13:25:13:27 | call to basic_stringstream | stringstream.cpp:28:7:28:9 | ss2 | | +| stringstream.cpp:13:30:13:32 | call to basic_stringstream | stringstream.cpp:18:2:18:4 | ss3 | | +| stringstream.cpp:13:30:13:32 | call to basic_stringstream | stringstream.cpp:24:7:24:9 | ss3 | | +| stringstream.cpp:13:30:13:32 | call to basic_stringstream | stringstream.cpp:29:7:29:9 | ss3 | | +| stringstream.cpp:13:35:13:37 | call to basic_stringstream | stringstream.cpp:19:2:19:4 | ss4 | | +| stringstream.cpp:13:35:13:37 | call to basic_stringstream | stringstream.cpp:25:7:25:9 | ss4 | | +| stringstream.cpp:13:35:13:37 | call to basic_stringstream | stringstream.cpp:30:7:30:9 | ss4 | | +| stringstream.cpp:13:40:13:42 | call to basic_stringstream | stringstream.cpp:20:2:20:4 | ss5 | | +| stringstream.cpp:13:40:13:42 | call to basic_stringstream | stringstream.cpp:26:7:26:9 | ss5 | | +| stringstream.cpp:13:40:13:42 | call to basic_stringstream | stringstream.cpp:31:7:31:9 | ss5 | | +| stringstream.cpp:14:16:14:21 | call to source | stringstream.cpp:14:16:14:24 | call to basic_string | TAINT | +| stringstream.cpp:14:16:14:24 | call to basic_string | stringstream.cpp:20:9:20:9 | t | | +| stringstream.cpp:16:2:16:4 | ref arg ss1 | stringstream.cpp:22:7:22:9 | ss1 | | +| stringstream.cpp:16:2:16:4 | ref arg ss1 | stringstream.cpp:27:7:27:9 | ss1 | | +| stringstream.cpp:17:2:17:4 | ref arg ss2 | stringstream.cpp:23:7:23:9 | ss2 | | +| stringstream.cpp:17:2:17:4 | ref arg ss2 | stringstream.cpp:28:7:28:9 | ss2 | | +| stringstream.cpp:18:2:18:4 | ref arg ss3 | stringstream.cpp:24:7:24:9 | ss3 | | +| stringstream.cpp:18:2:18:4 | ref arg ss3 | stringstream.cpp:29:7:29:9 | ss3 | | +| stringstream.cpp:19:2:19:4 | ref arg ss4 | stringstream.cpp:25:7:25:9 | ss4 | | +| stringstream.cpp:19:2:19:4 | ref arg ss4 | stringstream.cpp:30:7:30:9 | ss4 | | +| stringstream.cpp:20:2:20:4 | ref arg ss5 | stringstream.cpp:26:7:26:9 | ss5 | | +| stringstream.cpp:20:2:20:4 | ref arg ss5 | stringstream.cpp:31:7:31:9 | ss5 | | +| stringstream.cpp:34:32:34:37 | source | stringstream.cpp:39:9:39:14 | source | | +| stringstream.cpp:36:20:36:22 | call to basic_stringstream | stringstream.cpp:38:2:38:4 | ss1 | | +| stringstream.cpp:36:20:36:22 | call to basic_stringstream | stringstream.cpp:41:7:41:9 | ss1 | | +| stringstream.cpp:36:20:36:22 | call to basic_stringstream | stringstream.cpp:43:7:43:9 | ss1 | | +| stringstream.cpp:36:25:36:27 | call to basic_stringstream | stringstream.cpp:39:2:39:4 | ss2 | | +| stringstream.cpp:36:25:36:27 | call to basic_stringstream | stringstream.cpp:42:7:42:9 | ss2 | | +| stringstream.cpp:36:25:36:27 | call to basic_stringstream | stringstream.cpp:44:7:44:9 | ss2 | | +| stringstream.cpp:38:2:38:4 | ref arg ss1 | stringstream.cpp:41:7:41:9 | ss1 | | +| stringstream.cpp:38:2:38:4 | ref arg ss1 | stringstream.cpp:43:7:43:9 | ss1 | | +| stringstream.cpp:39:2:39:4 | ref arg ss2 | stringstream.cpp:42:7:42:9 | ss2 | | +| stringstream.cpp:39:2:39:4 | ref arg ss2 | stringstream.cpp:44:7:44:9 | ss2 | | | structlikeclass.cpp:5:7:5:7 | Unknown literal | structlikeclass.cpp:5:7:5:7 | constructor init of field v | TAINT | | structlikeclass.cpp:5:7:5:7 | Unknown literal | structlikeclass.cpp:5:7:5:7 | constructor init of field v | TAINT | | structlikeclass.cpp:5:7:5:7 | this | structlikeclass.cpp:5:7:5:7 | constructor init of field v [pre-this] | | @@ -1384,3 +1330,57 @@ | taint.cpp:483:18:483:19 | ref arg & ... | taint.cpp:483:19:483:19 | n [inner post update] | | | taint.cpp:483:19:483:19 | n | taint.cpp:483:18:483:19 | & ... | | | taint.cpp:483:28:483:34 | source1 | taint.cpp:483:11:483:15 | ref arg & ... | TAINT | +| vector.cpp:8:43:8:49 | source1 | vector.cpp:12:21:12:27 | source1 | | +| vector.cpp:8:43:8:49 | source1 | vector.cpp:26:33:26:39 | source1 | | +| vector.cpp:12:21:12:27 | source1 | vector.cpp:12:21:12:28 | call to vector | TAINT | +| vector.cpp:12:21:12:28 | call to vector | vector.cpp:14:14:14:14 | v | | +| vector.cpp:12:21:12:28 | call to vector | vector.cpp:18:38:18:38 | v | | +| vector.cpp:12:21:12:28 | call to vector | vector.cpp:18:55:18:55 | v | | +| vector.cpp:12:21:12:28 | call to vector | vector.cpp:22:15:22:15 | v | | +| vector.cpp:14:14:14:14 | call to begin | vector.cpp:14:14:14:14 | (__begin) | | +| vector.cpp:14:14:14:14 | call to begin | vector.cpp:14:14:14:14 | (__begin) | | +| vector.cpp:14:14:14:14 | call to begin | vector.cpp:14:14:14:14 | (__begin) | | +| vector.cpp:14:14:14:14 | call to end | vector.cpp:14:14:14:14 | (__end) | | +| vector.cpp:14:14:14:14 | call to operator* | vector.cpp:15:8:15:8 | x | | +| vector.cpp:14:14:14:14 | ref arg (__begin) | vector.cpp:14:14:14:14 | (__begin) | | +| vector.cpp:14:14:14:14 | ref arg (__begin) | vector.cpp:14:14:14:14 | (__begin) | | +| vector.cpp:14:14:14:14 | ref arg (__begin) | vector.cpp:14:14:14:14 | (__begin) | | +| vector.cpp:14:14:14:14 | ref arg (__range) | vector.cpp:14:14:14:14 | (__range) | | +| vector.cpp:14:14:14:14 | v | vector.cpp:14:14:14:14 | (__range) | | +| vector.cpp:14:14:14:14 | v | vector.cpp:14:14:14:14 | (__range) | | +| vector.cpp:14:14:14:14 | v | vector.cpp:14:14:14:14 | call to operator* | TAINT | +| vector.cpp:18:38:18:38 | ref arg v | vector.cpp:18:55:18:55 | v | | +| vector.cpp:18:38:18:38 | ref arg v | vector.cpp:22:15:22:15 | v | | +| vector.cpp:18:40:18:44 | call to begin | vector.cpp:18:49:18:50 | it | | +| vector.cpp:18:40:18:44 | call to begin | vector.cpp:18:66:18:67 | it | | +| vector.cpp:18:40:18:44 | call to begin | vector.cpp:19:9:19:10 | it | | +| vector.cpp:18:55:18:55 | ref arg v | vector.cpp:18:55:18:55 | v | | +| vector.cpp:18:55:18:55 | ref arg v | vector.cpp:22:15:22:15 | v | | +| vector.cpp:18:66:18:67 | ref arg it | vector.cpp:18:49:18:50 | it | | +| vector.cpp:18:66:18:67 | ref arg it | vector.cpp:18:66:18:67 | it | | +| vector.cpp:18:66:18:67 | ref arg it | vector.cpp:19:9:19:10 | it | | +| vector.cpp:22:15:22:15 | call to begin | vector.cpp:22:15:22:15 | (__begin) | | +| vector.cpp:22:15:22:15 | call to begin | vector.cpp:22:15:22:15 | (__begin) | | +| vector.cpp:22:15:22:15 | call to begin | vector.cpp:22:15:22:15 | (__begin) | | +| vector.cpp:22:15:22:15 | call to end | vector.cpp:22:15:22:15 | (__end) | | +| vector.cpp:22:15:22:15 | call to operator* | vector.cpp:23:8:23:8 | x | | +| vector.cpp:22:15:22:15 | ref arg (__begin) | vector.cpp:22:15:22:15 | (__begin) | | +| vector.cpp:22:15:22:15 | ref arg (__begin) | vector.cpp:22:15:22:15 | (__begin) | | +| vector.cpp:22:15:22:15 | ref arg (__begin) | vector.cpp:22:15:22:15 | (__begin) | | +| vector.cpp:22:15:22:15 | ref arg (__range) | vector.cpp:22:15:22:15 | (__range) | | +| vector.cpp:22:15:22:15 | v | vector.cpp:22:15:22:15 | (__range) | | +| vector.cpp:22:15:22:15 | v | vector.cpp:22:15:22:15 | (__range) | | +| vector.cpp:22:15:22:15 | v | vector.cpp:22:15:22:15 | call to operator* | TAINT | +| vector.cpp:26:33:26:39 | source1 | vector.cpp:26:33:26:40 | call to vector | TAINT | +| vector.cpp:26:33:26:40 | call to vector | vector.cpp:27:21:27:27 | const_v | | +| vector.cpp:27:21:27:21 | call to begin | vector.cpp:27:21:27:21 | (__begin) | | +| vector.cpp:27:21:27:21 | call to begin | vector.cpp:27:21:27:21 | (__begin) | | +| vector.cpp:27:21:27:21 | call to begin | vector.cpp:27:21:27:21 | (__begin) | | +| vector.cpp:27:21:27:21 | call to end | vector.cpp:27:21:27:21 | (__end) | | +| vector.cpp:27:21:27:21 | call to operator* | vector.cpp:28:8:28:8 | x | | +| vector.cpp:27:21:27:21 | ref arg (__begin) | vector.cpp:27:21:27:21 | (__begin) | | +| vector.cpp:27:21:27:21 | ref arg (__begin) | vector.cpp:27:21:27:21 | (__begin) | | +| vector.cpp:27:21:27:21 | ref arg (__begin) | vector.cpp:27:21:27:21 | (__begin) | | +| vector.cpp:27:21:27:27 | const_v | vector.cpp:27:21:27:21 | (__range) | | +| vector.cpp:27:21:27:27 | const_v | vector.cpp:27:21:27:21 | (__range) | | +| vector.cpp:27:21:27:27 | const_v | vector.cpp:27:21:27:21 | call to operator* | TAINT | diff --git a/cpp/ql/test/library-tests/dataflow/taint-tests/stl.cpp b/cpp/ql/test/library-tests/dataflow/taint-tests/stl.cpp deleted file mode 100644 index d0253b9b025..00000000000 --- a/cpp/ql/test/library-tests/dataflow/taint-tests/stl.cpp +++ /dev/null @@ -1,377 +0,0 @@ - -typedef unsigned long size_t; - -namespace std -{ - template struct char_traits; - - typedef size_t streamsize; - - struct ptrdiff_t; - - template - struct iterator { - iterator &operator++(); - iterator operator++(int); - bool operator==(iterator other) const; - bool operator!=(iterator other) const; - reference_type operator*() const; - }; - - struct input_iterator_tag {}; - struct forward_iterator_tag : public input_iterator_tag {}; - struct bidirectional_iterator_tag : public forward_iterator_tag {}; - struct random_access_iterator_tag : public bidirectional_iterator_tag {}; - - template class allocator { - public: - allocator() throw(); - typedef size_t size_type; - }; - - template, class Allocator = allocator > - class basic_string { - public: - typedef typename Allocator::size_type size_type; - - explicit basic_string(const Allocator& a = Allocator()); - basic_string(const charT* s, const Allocator& a = Allocator()); - - const charT* c_str() const; - - typedef std::iterator iterator; - typedef std::iterator const_iterator; - - iterator begin(); - iterator end(); - const_iterator begin() const; - const_iterator end() const; - const_iterator cbegin() const; - const_iterator cend() const; - - template basic_string& operator+=(const T& t); - basic_string& operator+=(const charT* s); - basic_string& append(const basic_string& str); - basic_string& append(const charT* s); - basic_string& append(size_type n, charT c); - }; - - template basic_string operator+(const basic_string& lhs, const basic_string& rhs); - template basic_string operator+(const basic_string& lhs, const charT* rhs); - - typedef basic_string string; - - template > - class basic_istream /*: virtual public basic_ios - not needed for this test */ { - public: - basic_istream& operator>>(int& n); - }; - - template > - class basic_ostream /*: virtual public basic_ios - not needed for this test */ { - public: - typedef charT char_type; - basic_ostream& write(const char_type* s, streamsize n); - - basic_ostream& operator<<(int n); - }; - - template basic_ostream& operator<<(basic_ostream&, const charT*); - template basic_ostream& operator<<(basic_ostream& os, const basic_string& str); - - template> - class basic_iostream : public basic_istream, public basic_ostream { - public: - }; - - template, class Allocator = allocator> - class basic_stringstream : public basic_iostream { - public: - explicit basic_stringstream(/*ios_base::openmode which = ios_base::out|ios_base::in - not needed for this test*/); - - basic_string str() const; - }; - - using stringstream = basic_stringstream; -} - -char *source(); -void sink(const char *s) {}; -void sink(const std::string &s) {}; -void sink(const std::stringstream &s) {}; - -void test_string() -{ - char *a = source(); - std::string b("123"); - std::string c(source()); - - sink(a); // tainted - sink(b); - sink(c); // tainted - sink(b.c_str()); - sink(c.c_str()); // tainted -} - -void test_stringstream() -{ - std::stringstream ss1, ss2, ss3, ss4, ss5; - std::string t(source()); - - ss1 << "1234"; - ss2 << source(); - ss3 << "123" << source(); - ss4 << source() << "456"; - ss5 << t; - - sink(ss1); - sink(ss2); // tainted [NOT DETECTED] - sink(ss3); // tainted [NOT DETECTED] - sink(ss4); // tainted [NOT DETECTED] - sink(ss5); // tainted [NOT DETECTED] - sink(ss1.str()); - sink(ss2.str()); // tainted [NOT DETECTED] - sink(ss3.str()); // tainted [NOT DETECTED] - sink(ss4.str()); // tainted [NOT DETECTED] - sink(ss5.str()); // tainted [NOT DETECTED] -} - -void test_stringstream_int(int source) -{ - std::stringstream ss1, ss2; - - ss1 << 1234; - ss2 << source; - - sink(ss1); - sink(ss2); // tainted [NOT DETECTED] - sink(ss1.str()); - sink(ss2.str()); // tainted [NOT DETECTED] -} - -using namespace std; - -char *user_input() { - return source(); -} - -void sink(const char *filename, const char *mode); - -void test_strings2() -{ - string path1 = user_input(); - sink(path1.c_str(), "r"); // tainted - - string path2; - path2 = user_input(); - sink(path2.c_str(), "r"); // tainted - - string path3(user_input()); - sink(path3.c_str(), "r"); // tainted -} - -void test_string3() -{ - const char *cs = source(); - - // convert char * -> std::string - std::string ss(cs); - - sink(cs); // tainted - sink(ss); // tainted -} - -void test_string4() -{ - const char *cs = source(); - - // convert char * -> std::string - std::string ss(cs); - - // convert back std::string -> char * - cs = ss.c_str(); - - sink(cs); // tainted - sink(ss); // tainted -} - -void test_string_constructors_assignments() -{ - { - std::string s1("hello"); - std::string s2 = "hello"; - std::string s3; - s3 = "hello"; - - sink(s1); - sink(s2); - sink(s3); - } - - { - std::string s1(source()); - std::string s2 = source(); - std::string s3; - s3 = source(); - - sink(s1); // tainted - sink(s2); // tainted - sink(s3); // tainted - } - - { - std::string s1; - std::string s2 = s1; - std::string s3; - s3 = s1; - - sink(s1); - sink(s2); - sink(s3); - } - - { - std::string s1 = std::string(source()); - std::string s2; - s2 = std::string(source()); - - sink(s1); // tainted - sink(s2); // tainted - } -} - -void sink(char) {} - -void test_range_based_for_loop_string() { - std::string s(source()); - for(char c : s) { - sink(c); // tainted [NOT DETECTED by IR] - } - - for(std::string::iterator it = s.begin(); it != s.end(); ++it) { - sink(*it); // tainted [NOT DETECTED] - } - - for(char& c : s) { - sink(c); // tainted [NOT DETECTED by IR] - } - - const std::string const_s(source()); - for(const char& c : const_s) { - sink(c); // tainted [NOT DETECTED by IR] - } -} - - - - - - - - -namespace std { - template - class vector { - private: - void *data_; - public: - vector(int size); - - T& operator[](int idx); - const T& operator[](int idx) const; - - typedef std::iterator iterator; - typedef std::iterator const_iterator; - - iterator begin() noexcept; - iterator end() noexcept; - - const_iterator begin() const noexcept; - const_iterator end() const noexcept; - }; -} - -void sink(int); - -void test_range_based_for_loop_vector(int source1) { - // Tainting the vector by allocating a tainted length. This doesn't represent - // how a vector would typically get tainted, but it allows this test to avoid - // being concerned with std::vector modeling. - std::vector v(source1); - - for(int x : v) { - sink(x); // tainted [NOT DETECTED by IR] - } - - for(std::vector::iterator it = v.begin(); it != v.end(); ++it) { - sink(*it); // tainted [NOT DETECTED] - } - - for(int& x : v) { - sink(x); // tainted [NOT DETECTED by IR] - } - - const std::vector const_v(source1); - for(const int& x : const_v) { - sink(x); // tainted [NOT DETECTED by IR] - } -} - -namespace ns_char -{ - char source(); -} - -void test_string_append() { - { - std::string s1("hello"); - std::string s2(source()); - - sink(s1 + s1); - sink(s1 + s2); // tainted - sink(s2 + s1); // tainted - sink(s2 + s2); // tainted - - sink(s1 + " world"); - sink(s1 + source()); // tainted - } - - { - std::string s3("abc"); - std::string s4(source()); - std::string s5, s6, s7, s8, s9; - - s5 = s3 + s4; - sink(s5); // tainted - - s6 = s3; - s6 += s4; - sink(s6); // tainted - - s7 = s3; - s7 += source(); - s7 += " "; - sink(s7); // tainted - - s8 = s3; - s8.append(s4); - sink(s8); // tainted - - s9 = s3; - s9.append(source()); - s9.append(" "); - sink(s9); // tainted - } - - { - std::string s10("abc"); - char c = ns_char::source(); - - s10.append(1, c); - sink(s10); // tainted - } -} diff --git a/cpp/ql/test/library-tests/dataflow/taint-tests/stl.h b/cpp/ql/test/library-tests/dataflow/taint-tests/stl.h new file mode 100644 index 00000000000..4ebd5e9283a --- /dev/null +++ b/cpp/ql/test/library-tests/dataflow/taint-tests/stl.h @@ -0,0 +1,126 @@ + +typedef unsigned long size_t; + +// --- string --- + +namespace std +{ + template struct char_traits; + + typedef size_t streamsize; + + struct ptrdiff_t; + + template + struct iterator { + iterator &operator++(); + iterator operator++(int); + bool operator==(iterator other) const; + bool operator!=(iterator other) const; + reference_type operator*() const; + }; + + struct input_iterator_tag {}; + struct forward_iterator_tag : public input_iterator_tag {}; + struct bidirectional_iterator_tag : public forward_iterator_tag {}; + struct random_access_iterator_tag : public bidirectional_iterator_tag {}; + + template class allocator { + public: + allocator() throw(); + typedef size_t size_type; + }; + + template, class Allocator = allocator > + class basic_string { + public: + typedef typename Allocator::size_type size_type; + + explicit basic_string(const Allocator& a = Allocator()); + basic_string(const charT* s, const Allocator& a = Allocator()); + + const charT* c_str() const; + + typedef std::iterator iterator; + typedef std::iterator const_iterator; + + iterator begin(); + iterator end(); + const_iterator begin() const; + const_iterator end() const; + const_iterator cbegin() const; + const_iterator cend() const; + + template basic_string& operator+=(const T& t); + basic_string& operator+=(const charT* s); + basic_string& append(const basic_string& str); + basic_string& append(const charT* s); + basic_string& append(size_type n, charT c); + }; + + template basic_string operator+(const basic_string& lhs, const basic_string& rhs); + template basic_string operator+(const basic_string& lhs, const charT* rhs); + + typedef basic_string string; + + template > + class basic_istream /*: virtual public basic_ios - not needed for this test */ { + public: + basic_istream& operator>>(int& n); + }; + + template > + class basic_ostream /*: virtual public basic_ios - not needed for this test */ { + public: + typedef charT char_type; + basic_ostream& write(const char_type* s, streamsize n); + + basic_ostream& operator<<(int n); + }; + + template basic_ostream& operator<<(basic_ostream&, const charT*); + template basic_ostream& operator<<(basic_ostream& os, const basic_string& str); + + template> + class basic_iostream : public basic_istream, public basic_ostream { + public: + }; + + template, class Allocator = allocator> + class basic_stringstream : public basic_iostream { + public: + explicit basic_stringstream(/*ios_base::openmode which = ios_base::out|ios_base::in - not needed for this test*/); + + basic_string str() const; + }; + + using stringstream = basic_stringstream; +} + +// --- vector --- + +namespace std { + template + class vector { + private: + void *data_; + public: + vector(int size); + + T& operator[](int idx); + const T& operator[](int idx) const; + + typedef std::iterator iterator; + typedef std::iterator const_iterator; + + iterator begin() noexcept; + iterator end() noexcept; + + const_iterator begin() const noexcept; + const_iterator end() const noexcept; + }; +} diff --git a/cpp/ql/test/library-tests/dataflow/taint-tests/string.cpp b/cpp/ql/test/library-tests/dataflow/taint-tests/string.cpp new file mode 100644 index 00000000000..1b8f9c095ab --- /dev/null +++ b/cpp/ql/test/library-tests/dataflow/taint-tests/string.cpp @@ -0,0 +1,186 @@ + +#include "stl.h" + +using namespace std; + +char *source(); + +namespace ns_char +{ + char source(); +} + +char *user_input() { + return source(); +} + +void sink(const char *s) {}; +void sink(const std::string &s) {}; +void sink(const char *filename, const char *mode); +void sink(char) {} + +void test_string() +{ + char *a = source(); + std::string b("123"); + std::string c(source()); + + sink(a); // tainted + sink(b); + sink(c); // tainted + sink(b.c_str()); + sink(c.c_str()); // tainted +} + +void test_strings2() +{ + string path1 = user_input(); + sink(path1.c_str(), "r"); // tainted + + string path2; + path2 = user_input(); + sink(path2.c_str(), "r"); // tainted + + string path3(user_input()); + sink(path3.c_str(), "r"); // tainted +} + +void test_string3() +{ + const char *cs = source(); + + // convert char * -> std::string + std::string ss(cs); + + sink(cs); // tainted + sink(ss); // tainted +} + +void test_string4() +{ + const char *cs = source(); + + // convert char * -> std::string + std::string ss(cs); + + // convert back std::string -> char * + cs = ss.c_str(); + + sink(cs); // tainted + sink(ss); // tainted +} + +void test_string_constructors_assignments() +{ + { + std::string s1("hello"); + std::string s2 = "hello"; + std::string s3; + s3 = "hello"; + + sink(s1); + sink(s2); + sink(s3); + } + + { + std::string s1(source()); + std::string s2 = source(); + std::string s3; + s3 = source(); + + sink(s1); // tainted + sink(s2); // tainted + sink(s3); // tainted + } + + { + std::string s1; + std::string s2 = s1; + std::string s3; + s3 = s1; + + sink(s1); + sink(s2); + sink(s3); + } + + { + std::string s1 = std::string(source()); + std::string s2; + s2 = std::string(source()); + + sink(s1); // tainted + sink(s2); // tainted + } +} + +void test_range_based_for_loop_string() { + std::string s(source()); + for(char c : s) { + sink(c); // tainted [NOT DETECTED by IR] + } + + for(std::string::iterator it = s.begin(); it != s.end(); ++it) { + sink(*it); // tainted [NOT DETECTED] + } + + for(char& c : s) { + sink(c); // tainted [NOT DETECTED by IR] + } + + const std::string const_s(source()); + for(const char& c : const_s) { + sink(c); // tainted [NOT DETECTED by IR] + } +} + +void test_string_append() { + { + std::string s1("hello"); + std::string s2(source()); + + sink(s1 + s1); + sink(s1 + s2); // tainted + sink(s2 + s1); // tainted + sink(s2 + s2); // tainted + + sink(s1 + " world"); + sink(s1 + source()); // tainted + } + + { + std::string s3("abc"); + std::string s4(source()); + std::string s5, s6, s7, s8, s9; + + s5 = s3 + s4; + sink(s5); // tainted + + s6 = s3; + s6 += s4; + sink(s6); // tainted + + s7 = s3; + s7 += source(); + s7 += " "; + sink(s7); // tainted + + s8 = s3; + s8.append(s4); + sink(s8); // tainted + + s9 = s3; + s9.append(source()); + s9.append(" "); + sink(s9); // tainted + } + + { + std::string s10("abc"); + char c = ns_char::source(); + + s10.append(1, c); + sink(s10); // tainted + } +} diff --git a/cpp/ql/test/library-tests/dataflow/taint-tests/stringstream.cpp b/cpp/ql/test/library-tests/dataflow/taint-tests/stringstream.cpp new file mode 100644 index 00000000000..0656da01277 --- /dev/null +++ b/cpp/ql/test/library-tests/dataflow/taint-tests/stringstream.cpp @@ -0,0 +1,45 @@ + +#include "stl.h" + +using namespace std; + +char *source(); + +void sink(const std::string &s) {}; +void sink(const std::stringstream &s) {}; + +void test_stringstream() +{ + std::stringstream ss1, ss2, ss3, ss4, ss5; + std::string t(source()); + + ss1 << "1234"; + ss2 << source(); + ss3 << "123" << source(); + ss4 << source() << "456"; + ss5 << t; + + sink(ss1); + sink(ss2); // tainted [NOT DETECTED] + sink(ss3); // tainted [NOT DETECTED] + sink(ss4); // tainted [NOT DETECTED] + sink(ss5); // tainted [NOT DETECTED] + sink(ss1.str()); + sink(ss2.str()); // tainted [NOT DETECTED] + sink(ss3.str()); // tainted [NOT DETECTED] + sink(ss4.str()); // tainted [NOT DETECTED] + sink(ss5.str()); // tainted [NOT DETECTED] +} + +void test_stringstream_int(int source) +{ + std::stringstream ss1, ss2; + + ss1 << 1234; + ss2 << source; + + sink(ss1); + sink(ss2); // tainted [NOT DETECTED] + sink(ss1.str()); + sink(ss2.str()); // tainted [NOT DETECTED] +} diff --git a/cpp/ql/test/library-tests/dataflow/taint-tests/taint.expected b/cpp/ql/test/library-tests/dataflow/taint-tests/taint.expected index 3c3ed47e351..62fd58a3790 100644 --- a/cpp/ql/test/library-tests/dataflow/taint-tests/taint.expected +++ b/cpp/ql/test/library-tests/dataflow/taint-tests/taint.expected @@ -32,37 +32,34 @@ | movableclass.cpp:55:8:55:9 | s2 | movableclass.cpp:52:23:52:28 | call to source | | movableclass.cpp:64:8:64:9 | s2 | movableclass.cpp:23:55:23:60 | call to source | | movableclass.cpp:65:11:65:11 | call to operator= | movableclass.cpp:65:13:65:18 | call to source | -| stl.cpp:113:7:113:7 | a | stl.cpp:109:12:109:17 | call to source | -| stl.cpp:115:7:115:7 | c | stl.cpp:111:16:111:21 | call to source | -| stl.cpp:117:9:117:13 | call to c_str | stl.cpp:111:16:111:21 | call to source | -| stl.cpp:167:13:167:17 | call to c_str | stl.cpp:159:10:159:15 | call to source | -| stl.cpp:171:13:171:17 | call to c_str | stl.cpp:159:10:159:15 | call to source | -| stl.cpp:174:13:174:17 | call to c_str | stl.cpp:159:10:159:15 | call to source | -| stl.cpp:184:7:184:8 | cs | stl.cpp:179:19:179:24 | call to source | -| stl.cpp:185:7:185:8 | ss | stl.cpp:179:19:179:24 | call to source | -| stl.cpp:198:7:198:8 | cs | stl.cpp:190:19:190:24 | call to source | -| stl.cpp:199:7:199:8 | ss | stl.cpp:190:19:190:24 | call to source | -| stl.cpp:221:8:221:9 | s1 | stl.cpp:216:18:216:23 | call to source | -| stl.cpp:222:8:222:9 | s2 | stl.cpp:217:20:217:25 | call to source | -| stl.cpp:223:8:223:9 | s3 | stl.cpp:219:8:219:13 | call to source | -| stl.cpp:242:8:242:9 | s1 | stl.cpp:238:32:238:37 | call to source | -| stl.cpp:243:8:243:9 | s2 | stl.cpp:240:20:240:25 | call to source | -| stl.cpp:252:8:252:8 | c | stl.cpp:250:16:250:21 | call to source | -| stl.cpp:260:8:260:8 | c | stl.cpp:250:16:250:21 | call to source | -| stl.cpp:265:8:265:8 | c | stl.cpp:263:28:263:33 | call to source | -| stl.cpp:307:8:307:8 | x | stl.cpp:300:43:300:49 | source1 | -| stl.cpp:315:8:315:8 | x | stl.cpp:300:43:300:49 | source1 | -| stl.cpp:320:8:320:8 | x | stl.cpp:300:43:300:49 | source1 | -| stl.cpp:335:11:335:11 | call to operator+ | stl.cpp:332:18:332:23 | call to source | -| stl.cpp:336:11:336:11 | call to operator+ | stl.cpp:332:18:332:23 | call to source | -| stl.cpp:337:11:337:11 | call to operator+ | stl.cpp:332:18:332:23 | call to source | -| stl.cpp:340:11:340:11 | call to operator+ | stl.cpp:340:13:340:18 | call to source | -| stl.cpp:349:8:349:9 | s5 | stl.cpp:345:18:345:23 | call to source | -| stl.cpp:353:8:353:9 | s6 | stl.cpp:345:18:345:23 | call to source | -| stl.cpp:358:8:358:9 | s7 | stl.cpp:356:9:356:14 | call to source | -| stl.cpp:362:8:362:9 | s8 | stl.cpp:345:18:345:23 | call to source | -| stl.cpp:367:8:367:9 | s9 | stl.cpp:365:13:365:18 | call to source | -| stl.cpp:375:8:375:10 | s10 | stl.cpp:372:12:372:26 | call to source | +| string.cpp:28:7:28:7 | a | string.cpp:24:12:24:17 | call to source | +| string.cpp:30:7:30:7 | c | string.cpp:26:16:26:21 | call to source | +| string.cpp:32:9:32:13 | call to c_str | string.cpp:26:16:26:21 | call to source | +| string.cpp:38:13:38:17 | call to c_str | string.cpp:14:10:14:15 | call to source | +| string.cpp:42:13:42:17 | call to c_str | string.cpp:14:10:14:15 | call to source | +| string.cpp:45:13:45:17 | call to c_str | string.cpp:14:10:14:15 | call to source | +| string.cpp:55:7:55:8 | cs | string.cpp:50:19:50:24 | call to source | +| string.cpp:56:7:56:8 | ss | string.cpp:50:19:50:24 | call to source | +| string.cpp:69:7:69:8 | cs | string.cpp:61:19:61:24 | call to source | +| string.cpp:70:7:70:8 | ss | string.cpp:61:19:61:24 | call to source | +| string.cpp:92:8:92:9 | s1 | string.cpp:87:18:87:23 | call to source | +| string.cpp:93:8:93:9 | s2 | string.cpp:88:20:88:25 | call to source | +| string.cpp:94:8:94:9 | s3 | string.cpp:90:8:90:13 | call to source | +| string.cpp:113:8:113:9 | s1 | string.cpp:109:32:109:37 | call to source | +| string.cpp:114:8:114:9 | s2 | string.cpp:111:20:111:25 | call to source | +| string.cpp:121:8:121:8 | c | string.cpp:119:16:119:21 | call to source | +| string.cpp:129:8:129:8 | c | string.cpp:119:16:119:21 | call to source | +| string.cpp:134:8:134:8 | c | string.cpp:132:28:132:33 | call to source | +| string.cpp:144:11:144:11 | call to operator+ | string.cpp:141:18:141:23 | call to source | +| string.cpp:145:11:145:11 | call to operator+ | string.cpp:141:18:141:23 | call to source | +| string.cpp:146:11:146:11 | call to operator+ | string.cpp:141:18:141:23 | call to source | +| string.cpp:149:11:149:11 | call to operator+ | string.cpp:149:13:149:18 | call to source | +| string.cpp:158:8:158:9 | s5 | string.cpp:154:18:154:23 | call to source | +| string.cpp:162:8:162:9 | s6 | string.cpp:154:18:154:23 | call to source | +| string.cpp:167:8:167:9 | s7 | string.cpp:165:9:165:14 | call to source | +| string.cpp:171:8:171:9 | s8 | string.cpp:154:18:154:23 | call to source | +| string.cpp:176:8:176:9 | s9 | string.cpp:174:13:174:18 | call to source | +| string.cpp:184:8:184:10 | s10 | string.cpp:181:12:181:26 | call to source | | structlikeclass.cpp:35:8:35:9 | s1 | structlikeclass.cpp:29:22:29:27 | call to source | | structlikeclass.cpp:36:8:36:9 | s2 | structlikeclass.cpp:30:24:30:29 | call to source | | structlikeclass.cpp:37:8:37:9 | s3 | structlikeclass.cpp:29:22:29:27 | call to source | @@ -169,3 +166,6 @@ | taint.cpp:470:7:470:7 | x | taint.cpp:462:6:462:11 | call to source | | taint.cpp:471:7:471:7 | y | taint.cpp:462:6:462:11 | call to source | | taint.cpp:485:7:485:10 | line | taint.cpp:480:26:480:32 | source1 | +| vector.cpp:15:8:15:8 | x | vector.cpp:8:43:8:49 | source1 | +| vector.cpp:23:8:23:8 | x | vector.cpp:8:43:8:49 | source1 | +| vector.cpp:28:8:28:8 | x | vector.cpp:8:43:8:49 | source1 | diff --git a/cpp/ql/test/library-tests/dataflow/taint-tests/test_diff.expected b/cpp/ql/test/library-tests/dataflow/taint-tests/test_diff.expected index 7704bbf9bf0..ef4b311d954 100644 --- a/cpp/ql/test/library-tests/dataflow/taint-tests/test_diff.expected +++ b/cpp/ql/test/library-tests/dataflow/taint-tests/test_diff.expected @@ -30,36 +30,33 @@ | movableclass.cpp:55:8:55:9 | movableclass.cpp:52:23:52:28 | AST only | | movableclass.cpp:64:8:64:9 | movableclass.cpp:23:55:23:60 | AST only | | movableclass.cpp:65:11:65:11 | movableclass.cpp:65:13:65:18 | AST only | -| stl.cpp:115:7:115:7 | stl.cpp:111:16:111:21 | AST only | -| stl.cpp:117:9:117:13 | stl.cpp:111:16:111:21 | AST only | -| stl.cpp:167:13:167:17 | stl.cpp:159:10:159:15 | AST only | -| stl.cpp:171:13:171:17 | stl.cpp:159:10:159:15 | AST only | -| stl.cpp:174:13:174:17 | stl.cpp:159:10:159:15 | AST only | -| stl.cpp:184:7:184:8 | stl.cpp:179:19:179:26 | IR only | -| stl.cpp:185:7:185:8 | stl.cpp:179:19:179:24 | AST only | -| stl.cpp:198:7:198:8 | stl.cpp:190:19:190:24 | AST only | -| stl.cpp:199:7:199:8 | stl.cpp:190:19:190:24 | AST only | -| stl.cpp:221:8:221:9 | stl.cpp:216:18:216:23 | AST only | -| stl.cpp:222:8:222:9 | stl.cpp:217:20:217:25 | AST only | -| stl.cpp:223:8:223:9 | stl.cpp:219:8:219:13 | AST only | -| stl.cpp:242:8:242:9 | stl.cpp:238:32:238:37 | AST only | -| stl.cpp:243:8:243:9 | stl.cpp:240:20:240:25 | AST only | -| stl.cpp:252:8:252:8 | stl.cpp:250:16:250:21 | AST only | -| stl.cpp:260:8:260:8 | stl.cpp:250:16:250:21 | AST only | -| stl.cpp:265:8:265:8 | stl.cpp:263:28:263:33 | AST only | -| stl.cpp:307:8:307:8 | stl.cpp:300:43:300:49 | AST only | -| stl.cpp:315:8:315:8 | stl.cpp:300:43:300:49 | AST only | -| stl.cpp:320:8:320:8 | stl.cpp:300:43:300:49 | AST only | -| stl.cpp:335:11:335:11 | stl.cpp:332:18:332:23 | AST only | -| stl.cpp:336:11:336:11 | stl.cpp:332:18:332:23 | AST only | -| stl.cpp:337:11:337:11 | stl.cpp:332:18:332:23 | AST only | -| stl.cpp:340:11:340:11 | stl.cpp:340:13:340:18 | AST only | -| stl.cpp:349:8:349:9 | stl.cpp:345:18:345:23 | AST only | -| stl.cpp:353:8:353:9 | stl.cpp:345:18:345:23 | AST only | -| stl.cpp:358:8:358:9 | stl.cpp:356:9:356:14 | AST only | -| stl.cpp:362:8:362:9 | stl.cpp:345:18:345:23 | AST only | -| stl.cpp:367:8:367:9 | stl.cpp:365:13:365:18 | AST only | -| stl.cpp:375:8:375:10 | stl.cpp:372:12:372:26 | AST only | +| string.cpp:30:7:30:7 | string.cpp:26:16:26:21 | AST only | +| string.cpp:32:9:32:13 | string.cpp:26:16:26:21 | AST only | +| string.cpp:38:13:38:17 | string.cpp:14:10:14:15 | AST only | +| string.cpp:42:13:42:17 | string.cpp:14:10:14:15 | AST only | +| string.cpp:45:13:45:17 | string.cpp:14:10:14:15 | AST only | +| string.cpp:55:7:55:8 | string.cpp:50:19:50:26 | IR only | +| string.cpp:56:7:56:8 | string.cpp:50:19:50:24 | AST only | +| string.cpp:69:7:69:8 | string.cpp:61:19:61:24 | AST only | +| string.cpp:70:7:70:8 | string.cpp:61:19:61:24 | AST only | +| string.cpp:92:8:92:9 | string.cpp:87:18:87:23 | AST only | +| string.cpp:93:8:93:9 | string.cpp:88:20:88:25 | AST only | +| string.cpp:94:8:94:9 | string.cpp:90:8:90:13 | AST only | +| string.cpp:113:8:113:9 | string.cpp:109:32:109:37 | AST only | +| string.cpp:114:8:114:9 | string.cpp:111:20:111:25 | AST only | +| string.cpp:121:8:121:8 | string.cpp:119:16:119:21 | AST only | +| string.cpp:129:8:129:8 | string.cpp:119:16:119:21 | AST only | +| string.cpp:134:8:134:8 | string.cpp:132:28:132:33 | AST only | +| string.cpp:144:11:144:11 | string.cpp:141:18:141:23 | AST only | +| string.cpp:145:11:145:11 | string.cpp:141:18:141:23 | AST only | +| string.cpp:146:11:146:11 | string.cpp:141:18:141:23 | AST only | +| string.cpp:149:11:149:11 | string.cpp:149:13:149:18 | AST only | +| string.cpp:158:8:158:9 | string.cpp:154:18:154:23 | AST only | +| string.cpp:162:8:162:9 | string.cpp:154:18:154:23 | AST only | +| string.cpp:167:8:167:9 | string.cpp:165:9:165:14 | AST only | +| string.cpp:171:8:171:9 | string.cpp:154:18:154:23 | AST only | +| string.cpp:176:8:176:9 | string.cpp:174:13:174:18 | AST only | +| string.cpp:184:8:184:10 | string.cpp:181:12:181:26 | AST only | | structlikeclass.cpp:35:8:35:9 | structlikeclass.cpp:29:22:29:27 | AST only | | structlikeclass.cpp:36:8:36:9 | structlikeclass.cpp:30:24:30:29 | AST only | | structlikeclass.cpp:37:8:37:9 | structlikeclass.cpp:29:22:29:27 | AST only | @@ -104,3 +101,6 @@ | taint.cpp:446:7:446:7 | taint.cpp:445:14:445:28 | AST only | | taint.cpp:447:9:447:17 | taint.cpp:445:14:445:28 | AST only | | taint.cpp:471:7:471:7 | taint.cpp:462:6:462:11 | AST only | +| vector.cpp:15:8:15:8 | vector.cpp:8:43:8:49 | AST only | +| vector.cpp:23:8:23:8 | vector.cpp:8:43:8:49 | AST only | +| vector.cpp:28:8:28:8 | vector.cpp:8:43:8:49 | AST only | diff --git a/cpp/ql/test/library-tests/dataflow/taint-tests/test_ir.expected b/cpp/ql/test/library-tests/dataflow/taint-tests/test_ir.expected index 463637d73e0..ac79007f6a7 100644 --- a/cpp/ql/test/library-tests/dataflow/taint-tests/test_ir.expected +++ b/cpp/ql/test/library-tests/dataflow/taint-tests/test_ir.expected @@ -1,10 +1,10 @@ | format.cpp:157:7:157:22 | (int)... | format.cpp:147:12:147:25 | call to source | | format.cpp:157:7:157:22 | access to array | format.cpp:147:12:147:25 | call to source | | format.cpp:158:7:158:27 | ... + ... | format.cpp:148:16:148:30 | call to source | -| stl.cpp:113:7:113:7 | (const char *)... | stl.cpp:109:12:109:17 | call to source | -| stl.cpp:113:7:113:7 | a | stl.cpp:109:12:109:17 | call to source | -| stl.cpp:184:7:184:8 | cs | stl.cpp:179:19:179:24 | call to source | -| stl.cpp:184:7:184:8 | cs | stl.cpp:179:19:179:26 | (const char *)... | +| string.cpp:28:7:28:7 | (const char *)... | string.cpp:24:12:24:17 | call to source | +| string.cpp:28:7:28:7 | a | string.cpp:24:12:24:17 | call to source | +| string.cpp:55:7:55:8 | cs | string.cpp:50:19:50:24 | call to source | +| string.cpp:55:7:55:8 | cs | string.cpp:50:19:50:26 | (const char *)... | | structlikeclass.cpp:38:8:38:9 | s4 | structlikeclass.cpp:33:8:33:13 | call to source | | structlikeclass.cpp:61:8:61:9 | s2 | structlikeclass.cpp:58:24:58:29 | call to source | | structlikeclass.cpp:62:8:62:20 | ... = ... | structlikeclass.cpp:62:13:62:18 | call to source | diff --git a/cpp/ql/test/library-tests/dataflow/taint-tests/vector.cpp b/cpp/ql/test/library-tests/dataflow/taint-tests/vector.cpp new file mode 100644 index 00000000000..337e4b40a1f --- /dev/null +++ b/cpp/ql/test/library-tests/dataflow/taint-tests/vector.cpp @@ -0,0 +1,30 @@ + +#include "stl.h" + +using namespace std; + +void sink(int); + +void test_range_based_for_loop_vector(int source1) { + // Tainting the vector by allocating a tainted length. This doesn't represent + // how a vector would typically get tainted, but it allows this test to avoid + // being concerned with std::vector modeling. + std::vector v(source1); + + for(int x : v) { + sink(x); // tainted [NOT DETECTED by IR] + } + + for(std::vector::iterator it = v.begin(); it != v.end(); ++it) { + sink(*it); // tainted [NOT DETECTED] + } + + for(int& x : v) { + sink(x); // tainted [NOT DETECTED by IR] + } + + const std::vector const_v(source1); + for(const int& x : const_v) { + sink(x); // tainted [NOT DETECTED by IR] + } +} diff --git a/csharp/extractor/Semmle.Extraction.CSharp/Entities/Expressions/ArrayCreation.cs b/csharp/extractor/Semmle.Extraction.CSharp/Entities/Expressions/ArrayCreation.cs index a4b1a4beb09..068cf2368fd 100644 --- a/csharp/extractor/Semmle.Extraction.CSharp/Entities/Expressions/ArrayCreation.cs +++ b/csharp/extractor/Semmle.Extraction.CSharp/Entities/Expressions/ArrayCreation.cs @@ -17,7 +17,7 @@ namespace Semmle.Extraction.CSharp.Entities.Expressions protected abstract ArrayTypeSyntax TypeSyntax { get; } - public abstract InitializerExpressionSyntax Initializer { get; } + public abstract InitializerExpressionSyntax Initializer { get; } protected override void PopulateExpression(TextWriter trapFile) { @@ -48,7 +48,7 @@ namespace Semmle.Extraction.CSharp.Entities.Expressions ExprKind.INT_LITERAL, this, child, - false, + true, size.ToString()); new Expression(info); diff --git a/csharp/ql/src/semmle/code/csharp/dataflow/internal/DataFlowImpl.qll b/csharp/ql/src/semmle/code/csharp/dataflow/internal/DataFlowImpl.qll index 5042dce683f..8c210edbe5f 100644 --- a/csharp/ql/src/semmle/code/csharp/dataflow/internal/DataFlowImpl.qll +++ b/csharp/ql/src/semmle/code/csharp/dataflow/internal/DataFlowImpl.qll @@ -1066,7 +1066,7 @@ private module LocalFlowBigStep { * Holds if `node` can be the first node in a maximal subsequence of local * flow steps in a dataflow path. */ - private predicate localFlowEntry(Node node, Configuration config) { + predicate localFlowEntry(Node node, Configuration config) { nodeCand2(node, config) and ( config.isSource(node) or @@ -1650,53 +1650,53 @@ private class AccessPathOption extends TAccessPathOption { * Holds if `node` is reachable with access path `ap` from a source in * the configuration `config`. * - * The Boolean `fromArg` records whether the node is reached through an + * 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. */ private predicate flowFwd( - Node node, boolean fromArg, AccessPathOption argAp, AccessPathFront apf, AccessPath ap, + Node node, CallContext cc, AccessPathOption argAp, AccessPathFront apf, AccessPath ap, Configuration config ) { - flowFwd0(node, fromArg, argAp, apf, ap, config) and + flowFwd0(node, cc, argAp, apf, ap, config) and flowCand(node, _, _, apf, config) } private predicate flowFwd0( - Node node, boolean fromArg, AccessPathOption argAp, AccessPathFront apf, AccessPath ap, + Node node, CallContext cc, AccessPathOption argAp, AccessPathFront apf, AccessPath ap, Configuration config ) { flowCand(node, _, _, _, config) and config.isSource(node) and - fromArg = false and + cc instanceof CallContextAny and argAp = TAccessPathNone() and ap = TNil(getNodeType(node)) and apf = ap.(AccessPathNil).getFront() or flowCand(node, _, _, _, unbind(config)) and ( - exists(Node mid | - flowFwd(mid, fromArg, argAp, apf, ap, config) and - localFlowBigStep(mid, node, true, _, config, _) + exists(Node mid, LocalCallContext localCC | + flowFwdLocalEntry(mid, cc, argAp, apf, ap, localCC, config) and + localFlowBigStep(mid, node, true, _, config, localCC) ) or - exists(Node mid, AccessPathNil nil | - flowFwd(mid, fromArg, argAp, _, nil, config) and - localFlowBigStep(mid, node, false, apf, config, _) and + exists(Node mid, AccessPathNil nil, LocalCallContext localCC | + flowFwdLocalEntry(mid, cc, argAp, _, nil, localCC, config) and + localFlowBigStep(mid, node, false, apf, config, localCC) and apf = ap.(AccessPathNil).getFront() ) or exists(Node mid | flowFwd(mid, _, _, apf, ap, config) and jumpStep(mid, node, config) and - fromArg = false and + cc instanceof CallContextAny and argAp = TAccessPathNone() ) or exists(Node mid, AccessPathNil nil | flowFwd(mid, _, _, _, nil, config) and additionalJumpStep(mid, node, config) and - fromArg = false and + cc instanceof CallContextAny and argAp = TAccessPathNone() and ap = TNil(getNodeType(node)) and apf = ap.(AccessPathNil).getFront() @@ -1704,40 +1704,51 @@ private predicate flowFwd0( ) or // store - exists(TypedContent tc | flowFwdStore(node, tc, pop(tc, ap), apf, fromArg, argAp, config)) + exists(TypedContent tc | flowFwdStore(node, tc, pop(tc, ap), apf, cc, argAp, config)) or // read exists(TypedContent tc | - flowFwdRead(node, _, push(tc, ap), apf, fromArg, argAp, config) and + flowFwdRead(node, _, push(tc, ap), apf, cc, argAp, config) and flowFwdConsCand(tc, apf, ap, config) ) or // flow into a callable - flowFwdIn(_, node, _, _, apf, ap, config) and - fromArg = true and + flowFwdIn(_, node, _, cc, _, apf, ap, config) and if flowCand(node, true, _, apf, config) then argAp = TAccessPathSome(ap) else argAp = TAccessPathNone() or // flow out of a callable exists(DataFlowCall call | - flowFwdOut(call, node, fromArg, argAp, apf, ap, config) and - fromArg = false + exists(DataFlowCallable c | + flowFwdOut(call, node, any(CallContextNoCall innercc), c, argAp, apf, ap, config) and + if reducedViableImplInReturn(c, call) then cc = TReturn(c, call) else cc = TAnyCallContext() + ) or exists(AccessPath argAp0 | flowFwdOutFromArg(call, node, argAp0, apf, ap, config) and - flowFwdIsEntered(call, fromArg, argAp, argAp0, config) + flowFwdIsEntered(call, cc, argAp, argAp0, config) ) ) } +pragma[nomagic] +private predicate flowFwdLocalEntry( + Node node, CallContext cc, AccessPathOption argAp, AccessPathFront apf, AccessPath ap, + LocalCallContext localCC, Configuration config +) { + flowFwd(node, cc, argAp, apf, ap, config) and + localFlowEntry(node, config) and + localCC = getLocalCallContext(cc, node.getEnclosingCallable()) +} + pragma[nomagic] private predicate flowFwdStore( - Node node, TypedContent tc, AccessPath ap0, AccessPathFront apf, boolean fromArg, + Node node, TypedContent tc, AccessPath ap0, AccessPathFront apf, CallContext cc, AccessPathOption argAp, Configuration config ) { exists(Node mid, AccessPathFront apf0 | - flowFwd(mid, fromArg, argAp, apf0, ap0, config) and + flowFwd(mid, cc, argAp, apf0, ap0, config) and flowFwdStore0(mid, tc, node, apf0, apf, config) ) } @@ -1764,20 +1775,20 @@ private predicate flowFwdStore0( pragma[nomagic] private predicate flowFwdRead0( - Node node1, TypedContent tc, AccessPathFrontHead apf0, AccessPath ap0, Node node2, - boolean fromArg, AccessPathOption argAp, Configuration config + Node node1, TypedContent tc, AccessPathFrontHead apf0, AccessPath ap0, Node node2, CallContext cc, + AccessPathOption argAp, Configuration config ) { - flowFwd(node1, fromArg, argAp, apf0, ap0, config) and + flowFwd(node1, cc, argAp, apf0, ap0, config) and readCandFwd(node1, tc, apf0, node2, config) } pragma[nomagic] private predicate flowFwdRead( - Node node, AccessPathFrontHead apf0, AccessPath ap0, AccessPathFront apf, boolean fromArg, + Node node, AccessPathFrontHead apf0, AccessPath ap0, AccessPathFront apf, CallContext cc, AccessPathOption argAp, Configuration config ) { exists(Node mid, TypedContent tc | - flowFwdRead0(mid, tc, apf0, ap0, node, fromArg, argAp, config) and + flowFwdRead0(mid, tc, apf0, ap0, node, cc, argAp, config) and flowCand(node, _, _, apf, unbind(config)) and flowCandConsCand(tc, apf, unbind(config)) ) @@ -1795,13 +1806,16 @@ private predicate flowFwdConsCand( pragma[nomagic] private predicate flowFwdIn( - DataFlowCall call, ParameterNode p, boolean fromArg, AccessPathOption argAp, AccessPathFront apf, - AccessPath ap, Configuration config + DataFlowCall call, ParameterNode p, CallContext outercc, CallContext innercc, + AccessPathOption argAp, AccessPathFront apf, AccessPath ap, Configuration config ) { - exists(ArgumentNode arg, boolean allowsFieldFlow | - flowFwd(arg, fromArg, argAp, apf, ap, config) and + exists(ArgumentNode arg, boolean allowsFieldFlow, DataFlowCallable c | + flowFwd(arg, outercc, argAp, apf, ap, config) and flowIntoCallNodeCand2(call, arg, p, allowsFieldFlow, config) and - flowCand(p, _, _, _, unbind(config)) + c = p.getEnclosingCallable() and + c = resolveCall(call, outercc) and + flowCand(p, _, _, _, unbind(config)) and + if recordDataFlowCallSite(call, c) then innercc = TSpecificCall(call) else innercc = TSomeCall() | ap instanceof AccessPathNil or allowsFieldFlow = true ) @@ -1809,13 +1823,19 @@ private predicate flowFwdIn( pragma[nomagic] private predicate flowFwdOut( - DataFlowCall call, Node node, boolean fromArg, AccessPathOption argAp, AccessPathFront apf, - AccessPath ap, Configuration config + DataFlowCall call, Node node, CallContext innercc, DataFlowCallable innerc, + AccessPathOption argAp, AccessPathFront apf, AccessPath ap, Configuration config ) { exists(ReturnNodeExt ret, boolean allowsFieldFlow | - flowFwd(ret, fromArg, argAp, apf, ap, config) and + flowFwd(ret, innercc, argAp, apf, ap, config) and flowOutOfCallNodeCand2(call, ret, node, allowsFieldFlow, config) and - flowCand(node, _, _, _, unbind(config)) + innerc = ret.getEnclosingCallable() and + flowCand(node, _, _, _, unbind(config)) and + ( + resolveReturn(innercc, innerc, call) + or + innercc.(CallContextCall).matchesCall(call) + ) | ap instanceof AccessPathNil or allowsFieldFlow = true ) @@ -1826,7 +1846,7 @@ private predicate flowFwdOutFromArg( DataFlowCall call, Node node, AccessPath argAp, AccessPathFront apf, AccessPath ap, Configuration config ) { - flowFwdOut(call, node, true, TAccessPathSome(argAp), apf, ap, config) + flowFwdOut(call, node, any(CallContextCall ccc), _, TAccessPathSome(argAp), apf, ap, config) } /** @@ -1834,10 +1854,10 @@ private predicate flowFwdOutFromArg( */ pragma[nomagic] private predicate flowFwdIsEntered( - DataFlowCall call, boolean fromArg, AccessPathOption argAp, AccessPath ap, Configuration config + DataFlowCall call, CallContext cc, AccessPathOption argAp, AccessPath ap, Configuration config ) { exists(ParameterNode p, AccessPathFront apf | - flowFwdIn(call, p, fromArg, argAp, apf, ap, config) and + flowFwdIn(call, p, cc, _, argAp, apf, ap, config) and flowCand(p, true, TAccessPathFrontSome(_), apf, config) ) } @@ -1920,7 +1940,7 @@ private predicate flow0( // flow out of a callable flowOut(_, node, _, _, ap, config) and toReturn = true and - if flowFwd(node, true, TAccessPathSome(_), _, ap, config) + if flowFwd(node, any(CallContextCall ccc), TAccessPathSome(_), _, ap, config) then returnAp = TAccessPathSome(ap) else returnAp = TAccessPathNone() } @@ -2006,9 +2026,10 @@ private predicate flowIsReturned( DataFlowCall call, boolean toReturn, AccessPathOption returnAp, AccessPath ap, Configuration config ) { - exists(ReturnNodeExt ret | + exists(ReturnNodeExt ret, CallContextCall ccc | flowOut(call, ret, toReturn, returnAp, ap, config) and - flowFwd(ret, true, TAccessPathSome(_), _, ap, config) + flowFwd(ret, ccc, TAccessPathSome(_), _, ap, config) and + ccc.matchesCall(call) ) } @@ -2031,7 +2052,7 @@ private newtype TSummaryCtx = exists(ReturnNodeExt ret, Configuration config, AccessPath ap0 | parameterFlow(p, ap, ret.getEnclosingCallable(), config) and flow(ret, true, TAccessPathSome(_), ap0, config) and - flowFwd(ret, true, TAccessPathSome(ap), _, ap0, config) + flowFwd(ret, any(CallContextCall ccc), TAccessPathSome(ap), _, ap0, config) ) } @@ -2352,7 +2373,7 @@ private predicate pathOutOfCallable0( ) { pos = getReturnPosition(mid.getNode()) and innercc = mid.getCallContext() and - not innercc instanceof CallContextCall and + innercc instanceof CallContextNoCall and ap = mid.getAp() and config = mid.getConfiguration() } @@ -2867,7 +2888,7 @@ private module FlowExploration { ) { pos = getReturnPosition(mid.getNode()) and innercc = mid.getCallContext() and - not innercc instanceof CallContextCall and + innercc instanceof CallContextNoCall and ap = mid.getAp() and config = mid.getConfiguration() } diff --git a/csharp/ql/src/semmle/code/csharp/dataflow/internal/DataFlowImpl2.qll b/csharp/ql/src/semmle/code/csharp/dataflow/internal/DataFlowImpl2.qll index 5042dce683f..8c210edbe5f 100644 --- a/csharp/ql/src/semmle/code/csharp/dataflow/internal/DataFlowImpl2.qll +++ b/csharp/ql/src/semmle/code/csharp/dataflow/internal/DataFlowImpl2.qll @@ -1066,7 +1066,7 @@ private module LocalFlowBigStep { * Holds if `node` can be the first node in a maximal subsequence of local * flow steps in a dataflow path. */ - private predicate localFlowEntry(Node node, Configuration config) { + predicate localFlowEntry(Node node, Configuration config) { nodeCand2(node, config) and ( config.isSource(node) or @@ -1650,53 +1650,53 @@ private class AccessPathOption extends TAccessPathOption { * Holds if `node` is reachable with access path `ap` from a source in * the configuration `config`. * - * The Boolean `fromArg` records whether the node is reached through an + * 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. */ private predicate flowFwd( - Node node, boolean fromArg, AccessPathOption argAp, AccessPathFront apf, AccessPath ap, + Node node, CallContext cc, AccessPathOption argAp, AccessPathFront apf, AccessPath ap, Configuration config ) { - flowFwd0(node, fromArg, argAp, apf, ap, config) and + flowFwd0(node, cc, argAp, apf, ap, config) and flowCand(node, _, _, apf, config) } private predicate flowFwd0( - Node node, boolean fromArg, AccessPathOption argAp, AccessPathFront apf, AccessPath ap, + Node node, CallContext cc, AccessPathOption argAp, AccessPathFront apf, AccessPath ap, Configuration config ) { flowCand(node, _, _, _, config) and config.isSource(node) and - fromArg = false and + cc instanceof CallContextAny and argAp = TAccessPathNone() and ap = TNil(getNodeType(node)) and apf = ap.(AccessPathNil).getFront() or flowCand(node, _, _, _, unbind(config)) and ( - exists(Node mid | - flowFwd(mid, fromArg, argAp, apf, ap, config) and - localFlowBigStep(mid, node, true, _, config, _) + exists(Node mid, LocalCallContext localCC | + flowFwdLocalEntry(mid, cc, argAp, apf, ap, localCC, config) and + localFlowBigStep(mid, node, true, _, config, localCC) ) or - exists(Node mid, AccessPathNil nil | - flowFwd(mid, fromArg, argAp, _, nil, config) and - localFlowBigStep(mid, node, false, apf, config, _) and + exists(Node mid, AccessPathNil nil, LocalCallContext localCC | + flowFwdLocalEntry(mid, cc, argAp, _, nil, localCC, config) and + localFlowBigStep(mid, node, false, apf, config, localCC) and apf = ap.(AccessPathNil).getFront() ) or exists(Node mid | flowFwd(mid, _, _, apf, ap, config) and jumpStep(mid, node, config) and - fromArg = false and + cc instanceof CallContextAny and argAp = TAccessPathNone() ) or exists(Node mid, AccessPathNil nil | flowFwd(mid, _, _, _, nil, config) and additionalJumpStep(mid, node, config) and - fromArg = false and + cc instanceof CallContextAny and argAp = TAccessPathNone() and ap = TNil(getNodeType(node)) and apf = ap.(AccessPathNil).getFront() @@ -1704,40 +1704,51 @@ private predicate flowFwd0( ) or // store - exists(TypedContent tc | flowFwdStore(node, tc, pop(tc, ap), apf, fromArg, argAp, config)) + exists(TypedContent tc | flowFwdStore(node, tc, pop(tc, ap), apf, cc, argAp, config)) or // read exists(TypedContent tc | - flowFwdRead(node, _, push(tc, ap), apf, fromArg, argAp, config) and + flowFwdRead(node, _, push(tc, ap), apf, cc, argAp, config) and flowFwdConsCand(tc, apf, ap, config) ) or // flow into a callable - flowFwdIn(_, node, _, _, apf, ap, config) and - fromArg = true and + flowFwdIn(_, node, _, cc, _, apf, ap, config) and if flowCand(node, true, _, apf, config) then argAp = TAccessPathSome(ap) else argAp = TAccessPathNone() or // flow out of a callable exists(DataFlowCall call | - flowFwdOut(call, node, fromArg, argAp, apf, ap, config) and - fromArg = false + exists(DataFlowCallable c | + flowFwdOut(call, node, any(CallContextNoCall innercc), c, argAp, apf, ap, config) and + if reducedViableImplInReturn(c, call) then cc = TReturn(c, call) else cc = TAnyCallContext() + ) or exists(AccessPath argAp0 | flowFwdOutFromArg(call, node, argAp0, apf, ap, config) and - flowFwdIsEntered(call, fromArg, argAp, argAp0, config) + flowFwdIsEntered(call, cc, argAp, argAp0, config) ) ) } +pragma[nomagic] +private predicate flowFwdLocalEntry( + Node node, CallContext cc, AccessPathOption argAp, AccessPathFront apf, AccessPath ap, + LocalCallContext localCC, Configuration config +) { + flowFwd(node, cc, argAp, apf, ap, config) and + localFlowEntry(node, config) and + localCC = getLocalCallContext(cc, node.getEnclosingCallable()) +} + pragma[nomagic] private predicate flowFwdStore( - Node node, TypedContent tc, AccessPath ap0, AccessPathFront apf, boolean fromArg, + Node node, TypedContent tc, AccessPath ap0, AccessPathFront apf, CallContext cc, AccessPathOption argAp, Configuration config ) { exists(Node mid, AccessPathFront apf0 | - flowFwd(mid, fromArg, argAp, apf0, ap0, config) and + flowFwd(mid, cc, argAp, apf0, ap0, config) and flowFwdStore0(mid, tc, node, apf0, apf, config) ) } @@ -1764,20 +1775,20 @@ private predicate flowFwdStore0( pragma[nomagic] private predicate flowFwdRead0( - Node node1, TypedContent tc, AccessPathFrontHead apf0, AccessPath ap0, Node node2, - boolean fromArg, AccessPathOption argAp, Configuration config + Node node1, TypedContent tc, AccessPathFrontHead apf0, AccessPath ap0, Node node2, CallContext cc, + AccessPathOption argAp, Configuration config ) { - flowFwd(node1, fromArg, argAp, apf0, ap0, config) and + flowFwd(node1, cc, argAp, apf0, ap0, config) and readCandFwd(node1, tc, apf0, node2, config) } pragma[nomagic] private predicate flowFwdRead( - Node node, AccessPathFrontHead apf0, AccessPath ap0, AccessPathFront apf, boolean fromArg, + Node node, AccessPathFrontHead apf0, AccessPath ap0, AccessPathFront apf, CallContext cc, AccessPathOption argAp, Configuration config ) { exists(Node mid, TypedContent tc | - flowFwdRead0(mid, tc, apf0, ap0, node, fromArg, argAp, config) and + flowFwdRead0(mid, tc, apf0, ap0, node, cc, argAp, config) and flowCand(node, _, _, apf, unbind(config)) and flowCandConsCand(tc, apf, unbind(config)) ) @@ -1795,13 +1806,16 @@ private predicate flowFwdConsCand( pragma[nomagic] private predicate flowFwdIn( - DataFlowCall call, ParameterNode p, boolean fromArg, AccessPathOption argAp, AccessPathFront apf, - AccessPath ap, Configuration config + DataFlowCall call, ParameterNode p, CallContext outercc, CallContext innercc, + AccessPathOption argAp, AccessPathFront apf, AccessPath ap, Configuration config ) { - exists(ArgumentNode arg, boolean allowsFieldFlow | - flowFwd(arg, fromArg, argAp, apf, ap, config) and + exists(ArgumentNode arg, boolean allowsFieldFlow, DataFlowCallable c | + flowFwd(arg, outercc, argAp, apf, ap, config) and flowIntoCallNodeCand2(call, arg, p, allowsFieldFlow, config) and - flowCand(p, _, _, _, unbind(config)) + c = p.getEnclosingCallable() and + c = resolveCall(call, outercc) and + flowCand(p, _, _, _, unbind(config)) and + if recordDataFlowCallSite(call, c) then innercc = TSpecificCall(call) else innercc = TSomeCall() | ap instanceof AccessPathNil or allowsFieldFlow = true ) @@ -1809,13 +1823,19 @@ private predicate flowFwdIn( pragma[nomagic] private predicate flowFwdOut( - DataFlowCall call, Node node, boolean fromArg, AccessPathOption argAp, AccessPathFront apf, - AccessPath ap, Configuration config + DataFlowCall call, Node node, CallContext innercc, DataFlowCallable innerc, + AccessPathOption argAp, AccessPathFront apf, AccessPath ap, Configuration config ) { exists(ReturnNodeExt ret, boolean allowsFieldFlow | - flowFwd(ret, fromArg, argAp, apf, ap, config) and + flowFwd(ret, innercc, argAp, apf, ap, config) and flowOutOfCallNodeCand2(call, ret, node, allowsFieldFlow, config) and - flowCand(node, _, _, _, unbind(config)) + innerc = ret.getEnclosingCallable() and + flowCand(node, _, _, _, unbind(config)) and + ( + resolveReturn(innercc, innerc, call) + or + innercc.(CallContextCall).matchesCall(call) + ) | ap instanceof AccessPathNil or allowsFieldFlow = true ) @@ -1826,7 +1846,7 @@ private predicate flowFwdOutFromArg( DataFlowCall call, Node node, AccessPath argAp, AccessPathFront apf, AccessPath ap, Configuration config ) { - flowFwdOut(call, node, true, TAccessPathSome(argAp), apf, ap, config) + flowFwdOut(call, node, any(CallContextCall ccc), _, TAccessPathSome(argAp), apf, ap, config) } /** @@ -1834,10 +1854,10 @@ private predicate flowFwdOutFromArg( */ pragma[nomagic] private predicate flowFwdIsEntered( - DataFlowCall call, boolean fromArg, AccessPathOption argAp, AccessPath ap, Configuration config + DataFlowCall call, CallContext cc, AccessPathOption argAp, AccessPath ap, Configuration config ) { exists(ParameterNode p, AccessPathFront apf | - flowFwdIn(call, p, fromArg, argAp, apf, ap, config) and + flowFwdIn(call, p, cc, _, argAp, apf, ap, config) and flowCand(p, true, TAccessPathFrontSome(_), apf, config) ) } @@ -1920,7 +1940,7 @@ private predicate flow0( // flow out of a callable flowOut(_, node, _, _, ap, config) and toReturn = true and - if flowFwd(node, true, TAccessPathSome(_), _, ap, config) + if flowFwd(node, any(CallContextCall ccc), TAccessPathSome(_), _, ap, config) then returnAp = TAccessPathSome(ap) else returnAp = TAccessPathNone() } @@ -2006,9 +2026,10 @@ private predicate flowIsReturned( DataFlowCall call, boolean toReturn, AccessPathOption returnAp, AccessPath ap, Configuration config ) { - exists(ReturnNodeExt ret | + exists(ReturnNodeExt ret, CallContextCall ccc | flowOut(call, ret, toReturn, returnAp, ap, config) and - flowFwd(ret, true, TAccessPathSome(_), _, ap, config) + flowFwd(ret, ccc, TAccessPathSome(_), _, ap, config) and + ccc.matchesCall(call) ) } @@ -2031,7 +2052,7 @@ private newtype TSummaryCtx = exists(ReturnNodeExt ret, Configuration config, AccessPath ap0 | parameterFlow(p, ap, ret.getEnclosingCallable(), config) and flow(ret, true, TAccessPathSome(_), ap0, config) and - flowFwd(ret, true, TAccessPathSome(ap), _, ap0, config) + flowFwd(ret, any(CallContextCall ccc), TAccessPathSome(ap), _, ap0, config) ) } @@ -2352,7 +2373,7 @@ private predicate pathOutOfCallable0( ) { pos = getReturnPosition(mid.getNode()) and innercc = mid.getCallContext() and - not innercc instanceof CallContextCall and + innercc instanceof CallContextNoCall and ap = mid.getAp() and config = mid.getConfiguration() } @@ -2867,7 +2888,7 @@ private module FlowExploration { ) { pos = getReturnPosition(mid.getNode()) and innercc = mid.getCallContext() and - not innercc instanceof CallContextCall and + innercc instanceof CallContextNoCall and ap = mid.getAp() and config = mid.getConfiguration() } diff --git a/csharp/ql/src/semmle/code/csharp/dataflow/internal/DataFlowImpl3.qll b/csharp/ql/src/semmle/code/csharp/dataflow/internal/DataFlowImpl3.qll index 5042dce683f..8c210edbe5f 100644 --- a/csharp/ql/src/semmle/code/csharp/dataflow/internal/DataFlowImpl3.qll +++ b/csharp/ql/src/semmle/code/csharp/dataflow/internal/DataFlowImpl3.qll @@ -1066,7 +1066,7 @@ private module LocalFlowBigStep { * Holds if `node` can be the first node in a maximal subsequence of local * flow steps in a dataflow path. */ - private predicate localFlowEntry(Node node, Configuration config) { + predicate localFlowEntry(Node node, Configuration config) { nodeCand2(node, config) and ( config.isSource(node) or @@ -1650,53 +1650,53 @@ private class AccessPathOption extends TAccessPathOption { * Holds if `node` is reachable with access path `ap` from a source in * the configuration `config`. * - * The Boolean `fromArg` records whether the node is reached through an + * 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. */ private predicate flowFwd( - Node node, boolean fromArg, AccessPathOption argAp, AccessPathFront apf, AccessPath ap, + Node node, CallContext cc, AccessPathOption argAp, AccessPathFront apf, AccessPath ap, Configuration config ) { - flowFwd0(node, fromArg, argAp, apf, ap, config) and + flowFwd0(node, cc, argAp, apf, ap, config) and flowCand(node, _, _, apf, config) } private predicate flowFwd0( - Node node, boolean fromArg, AccessPathOption argAp, AccessPathFront apf, AccessPath ap, + Node node, CallContext cc, AccessPathOption argAp, AccessPathFront apf, AccessPath ap, Configuration config ) { flowCand(node, _, _, _, config) and config.isSource(node) and - fromArg = false and + cc instanceof CallContextAny and argAp = TAccessPathNone() and ap = TNil(getNodeType(node)) and apf = ap.(AccessPathNil).getFront() or flowCand(node, _, _, _, unbind(config)) and ( - exists(Node mid | - flowFwd(mid, fromArg, argAp, apf, ap, config) and - localFlowBigStep(mid, node, true, _, config, _) + exists(Node mid, LocalCallContext localCC | + flowFwdLocalEntry(mid, cc, argAp, apf, ap, localCC, config) and + localFlowBigStep(mid, node, true, _, config, localCC) ) or - exists(Node mid, AccessPathNil nil | - flowFwd(mid, fromArg, argAp, _, nil, config) and - localFlowBigStep(mid, node, false, apf, config, _) and + exists(Node mid, AccessPathNil nil, LocalCallContext localCC | + flowFwdLocalEntry(mid, cc, argAp, _, nil, localCC, config) and + localFlowBigStep(mid, node, false, apf, config, localCC) and apf = ap.(AccessPathNil).getFront() ) or exists(Node mid | flowFwd(mid, _, _, apf, ap, config) and jumpStep(mid, node, config) and - fromArg = false and + cc instanceof CallContextAny and argAp = TAccessPathNone() ) or exists(Node mid, AccessPathNil nil | flowFwd(mid, _, _, _, nil, config) and additionalJumpStep(mid, node, config) and - fromArg = false and + cc instanceof CallContextAny and argAp = TAccessPathNone() and ap = TNil(getNodeType(node)) and apf = ap.(AccessPathNil).getFront() @@ -1704,40 +1704,51 @@ private predicate flowFwd0( ) or // store - exists(TypedContent tc | flowFwdStore(node, tc, pop(tc, ap), apf, fromArg, argAp, config)) + exists(TypedContent tc | flowFwdStore(node, tc, pop(tc, ap), apf, cc, argAp, config)) or // read exists(TypedContent tc | - flowFwdRead(node, _, push(tc, ap), apf, fromArg, argAp, config) and + flowFwdRead(node, _, push(tc, ap), apf, cc, argAp, config) and flowFwdConsCand(tc, apf, ap, config) ) or // flow into a callable - flowFwdIn(_, node, _, _, apf, ap, config) and - fromArg = true and + flowFwdIn(_, node, _, cc, _, apf, ap, config) and if flowCand(node, true, _, apf, config) then argAp = TAccessPathSome(ap) else argAp = TAccessPathNone() or // flow out of a callable exists(DataFlowCall call | - flowFwdOut(call, node, fromArg, argAp, apf, ap, config) and - fromArg = false + exists(DataFlowCallable c | + flowFwdOut(call, node, any(CallContextNoCall innercc), c, argAp, apf, ap, config) and + if reducedViableImplInReturn(c, call) then cc = TReturn(c, call) else cc = TAnyCallContext() + ) or exists(AccessPath argAp0 | flowFwdOutFromArg(call, node, argAp0, apf, ap, config) and - flowFwdIsEntered(call, fromArg, argAp, argAp0, config) + flowFwdIsEntered(call, cc, argAp, argAp0, config) ) ) } +pragma[nomagic] +private predicate flowFwdLocalEntry( + Node node, CallContext cc, AccessPathOption argAp, AccessPathFront apf, AccessPath ap, + LocalCallContext localCC, Configuration config +) { + flowFwd(node, cc, argAp, apf, ap, config) and + localFlowEntry(node, config) and + localCC = getLocalCallContext(cc, node.getEnclosingCallable()) +} + pragma[nomagic] private predicate flowFwdStore( - Node node, TypedContent tc, AccessPath ap0, AccessPathFront apf, boolean fromArg, + Node node, TypedContent tc, AccessPath ap0, AccessPathFront apf, CallContext cc, AccessPathOption argAp, Configuration config ) { exists(Node mid, AccessPathFront apf0 | - flowFwd(mid, fromArg, argAp, apf0, ap0, config) and + flowFwd(mid, cc, argAp, apf0, ap0, config) and flowFwdStore0(mid, tc, node, apf0, apf, config) ) } @@ -1764,20 +1775,20 @@ private predicate flowFwdStore0( pragma[nomagic] private predicate flowFwdRead0( - Node node1, TypedContent tc, AccessPathFrontHead apf0, AccessPath ap0, Node node2, - boolean fromArg, AccessPathOption argAp, Configuration config + Node node1, TypedContent tc, AccessPathFrontHead apf0, AccessPath ap0, Node node2, CallContext cc, + AccessPathOption argAp, Configuration config ) { - flowFwd(node1, fromArg, argAp, apf0, ap0, config) and + flowFwd(node1, cc, argAp, apf0, ap0, config) and readCandFwd(node1, tc, apf0, node2, config) } pragma[nomagic] private predicate flowFwdRead( - Node node, AccessPathFrontHead apf0, AccessPath ap0, AccessPathFront apf, boolean fromArg, + Node node, AccessPathFrontHead apf0, AccessPath ap0, AccessPathFront apf, CallContext cc, AccessPathOption argAp, Configuration config ) { exists(Node mid, TypedContent tc | - flowFwdRead0(mid, tc, apf0, ap0, node, fromArg, argAp, config) and + flowFwdRead0(mid, tc, apf0, ap0, node, cc, argAp, config) and flowCand(node, _, _, apf, unbind(config)) and flowCandConsCand(tc, apf, unbind(config)) ) @@ -1795,13 +1806,16 @@ private predicate flowFwdConsCand( pragma[nomagic] private predicate flowFwdIn( - DataFlowCall call, ParameterNode p, boolean fromArg, AccessPathOption argAp, AccessPathFront apf, - AccessPath ap, Configuration config + DataFlowCall call, ParameterNode p, CallContext outercc, CallContext innercc, + AccessPathOption argAp, AccessPathFront apf, AccessPath ap, Configuration config ) { - exists(ArgumentNode arg, boolean allowsFieldFlow | - flowFwd(arg, fromArg, argAp, apf, ap, config) and + exists(ArgumentNode arg, boolean allowsFieldFlow, DataFlowCallable c | + flowFwd(arg, outercc, argAp, apf, ap, config) and flowIntoCallNodeCand2(call, arg, p, allowsFieldFlow, config) and - flowCand(p, _, _, _, unbind(config)) + c = p.getEnclosingCallable() and + c = resolveCall(call, outercc) and + flowCand(p, _, _, _, unbind(config)) and + if recordDataFlowCallSite(call, c) then innercc = TSpecificCall(call) else innercc = TSomeCall() | ap instanceof AccessPathNil or allowsFieldFlow = true ) @@ -1809,13 +1823,19 @@ private predicate flowFwdIn( pragma[nomagic] private predicate flowFwdOut( - DataFlowCall call, Node node, boolean fromArg, AccessPathOption argAp, AccessPathFront apf, - AccessPath ap, Configuration config + DataFlowCall call, Node node, CallContext innercc, DataFlowCallable innerc, + AccessPathOption argAp, AccessPathFront apf, AccessPath ap, Configuration config ) { exists(ReturnNodeExt ret, boolean allowsFieldFlow | - flowFwd(ret, fromArg, argAp, apf, ap, config) and + flowFwd(ret, innercc, argAp, apf, ap, config) and flowOutOfCallNodeCand2(call, ret, node, allowsFieldFlow, config) and - flowCand(node, _, _, _, unbind(config)) + innerc = ret.getEnclosingCallable() and + flowCand(node, _, _, _, unbind(config)) and + ( + resolveReturn(innercc, innerc, call) + or + innercc.(CallContextCall).matchesCall(call) + ) | ap instanceof AccessPathNil or allowsFieldFlow = true ) @@ -1826,7 +1846,7 @@ private predicate flowFwdOutFromArg( DataFlowCall call, Node node, AccessPath argAp, AccessPathFront apf, AccessPath ap, Configuration config ) { - flowFwdOut(call, node, true, TAccessPathSome(argAp), apf, ap, config) + flowFwdOut(call, node, any(CallContextCall ccc), _, TAccessPathSome(argAp), apf, ap, config) } /** @@ -1834,10 +1854,10 @@ private predicate flowFwdOutFromArg( */ pragma[nomagic] private predicate flowFwdIsEntered( - DataFlowCall call, boolean fromArg, AccessPathOption argAp, AccessPath ap, Configuration config + DataFlowCall call, CallContext cc, AccessPathOption argAp, AccessPath ap, Configuration config ) { exists(ParameterNode p, AccessPathFront apf | - flowFwdIn(call, p, fromArg, argAp, apf, ap, config) and + flowFwdIn(call, p, cc, _, argAp, apf, ap, config) and flowCand(p, true, TAccessPathFrontSome(_), apf, config) ) } @@ -1920,7 +1940,7 @@ private predicate flow0( // flow out of a callable flowOut(_, node, _, _, ap, config) and toReturn = true and - if flowFwd(node, true, TAccessPathSome(_), _, ap, config) + if flowFwd(node, any(CallContextCall ccc), TAccessPathSome(_), _, ap, config) then returnAp = TAccessPathSome(ap) else returnAp = TAccessPathNone() } @@ -2006,9 +2026,10 @@ private predicate flowIsReturned( DataFlowCall call, boolean toReturn, AccessPathOption returnAp, AccessPath ap, Configuration config ) { - exists(ReturnNodeExt ret | + exists(ReturnNodeExt ret, CallContextCall ccc | flowOut(call, ret, toReturn, returnAp, ap, config) and - flowFwd(ret, true, TAccessPathSome(_), _, ap, config) + flowFwd(ret, ccc, TAccessPathSome(_), _, ap, config) and + ccc.matchesCall(call) ) } @@ -2031,7 +2052,7 @@ private newtype TSummaryCtx = exists(ReturnNodeExt ret, Configuration config, AccessPath ap0 | parameterFlow(p, ap, ret.getEnclosingCallable(), config) and flow(ret, true, TAccessPathSome(_), ap0, config) and - flowFwd(ret, true, TAccessPathSome(ap), _, ap0, config) + flowFwd(ret, any(CallContextCall ccc), TAccessPathSome(ap), _, ap0, config) ) } @@ -2352,7 +2373,7 @@ private predicate pathOutOfCallable0( ) { pos = getReturnPosition(mid.getNode()) and innercc = mid.getCallContext() and - not innercc instanceof CallContextCall and + innercc instanceof CallContextNoCall and ap = mid.getAp() and config = mid.getConfiguration() } @@ -2867,7 +2888,7 @@ private module FlowExploration { ) { pos = getReturnPosition(mid.getNode()) and innercc = mid.getCallContext() and - not innercc instanceof CallContextCall and + innercc instanceof CallContextNoCall and ap = mid.getAp() and config = mid.getConfiguration() } diff --git a/csharp/ql/src/semmle/code/csharp/dataflow/internal/DataFlowImpl4.qll b/csharp/ql/src/semmle/code/csharp/dataflow/internal/DataFlowImpl4.qll index 5042dce683f..8c210edbe5f 100644 --- a/csharp/ql/src/semmle/code/csharp/dataflow/internal/DataFlowImpl4.qll +++ b/csharp/ql/src/semmle/code/csharp/dataflow/internal/DataFlowImpl4.qll @@ -1066,7 +1066,7 @@ private module LocalFlowBigStep { * Holds if `node` can be the first node in a maximal subsequence of local * flow steps in a dataflow path. */ - private predicate localFlowEntry(Node node, Configuration config) { + predicate localFlowEntry(Node node, Configuration config) { nodeCand2(node, config) and ( config.isSource(node) or @@ -1650,53 +1650,53 @@ private class AccessPathOption extends TAccessPathOption { * Holds if `node` is reachable with access path `ap` from a source in * the configuration `config`. * - * The Boolean `fromArg` records whether the node is reached through an + * 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. */ private predicate flowFwd( - Node node, boolean fromArg, AccessPathOption argAp, AccessPathFront apf, AccessPath ap, + Node node, CallContext cc, AccessPathOption argAp, AccessPathFront apf, AccessPath ap, Configuration config ) { - flowFwd0(node, fromArg, argAp, apf, ap, config) and + flowFwd0(node, cc, argAp, apf, ap, config) and flowCand(node, _, _, apf, config) } private predicate flowFwd0( - Node node, boolean fromArg, AccessPathOption argAp, AccessPathFront apf, AccessPath ap, + Node node, CallContext cc, AccessPathOption argAp, AccessPathFront apf, AccessPath ap, Configuration config ) { flowCand(node, _, _, _, config) and config.isSource(node) and - fromArg = false and + cc instanceof CallContextAny and argAp = TAccessPathNone() and ap = TNil(getNodeType(node)) and apf = ap.(AccessPathNil).getFront() or flowCand(node, _, _, _, unbind(config)) and ( - exists(Node mid | - flowFwd(mid, fromArg, argAp, apf, ap, config) and - localFlowBigStep(mid, node, true, _, config, _) + exists(Node mid, LocalCallContext localCC | + flowFwdLocalEntry(mid, cc, argAp, apf, ap, localCC, config) and + localFlowBigStep(mid, node, true, _, config, localCC) ) or - exists(Node mid, AccessPathNil nil | - flowFwd(mid, fromArg, argAp, _, nil, config) and - localFlowBigStep(mid, node, false, apf, config, _) and + exists(Node mid, AccessPathNil nil, LocalCallContext localCC | + flowFwdLocalEntry(mid, cc, argAp, _, nil, localCC, config) and + localFlowBigStep(mid, node, false, apf, config, localCC) and apf = ap.(AccessPathNil).getFront() ) or exists(Node mid | flowFwd(mid, _, _, apf, ap, config) and jumpStep(mid, node, config) and - fromArg = false and + cc instanceof CallContextAny and argAp = TAccessPathNone() ) or exists(Node mid, AccessPathNil nil | flowFwd(mid, _, _, _, nil, config) and additionalJumpStep(mid, node, config) and - fromArg = false and + cc instanceof CallContextAny and argAp = TAccessPathNone() and ap = TNil(getNodeType(node)) and apf = ap.(AccessPathNil).getFront() @@ -1704,40 +1704,51 @@ private predicate flowFwd0( ) or // store - exists(TypedContent tc | flowFwdStore(node, tc, pop(tc, ap), apf, fromArg, argAp, config)) + exists(TypedContent tc | flowFwdStore(node, tc, pop(tc, ap), apf, cc, argAp, config)) or // read exists(TypedContent tc | - flowFwdRead(node, _, push(tc, ap), apf, fromArg, argAp, config) and + flowFwdRead(node, _, push(tc, ap), apf, cc, argAp, config) and flowFwdConsCand(tc, apf, ap, config) ) or // flow into a callable - flowFwdIn(_, node, _, _, apf, ap, config) and - fromArg = true and + flowFwdIn(_, node, _, cc, _, apf, ap, config) and if flowCand(node, true, _, apf, config) then argAp = TAccessPathSome(ap) else argAp = TAccessPathNone() or // flow out of a callable exists(DataFlowCall call | - flowFwdOut(call, node, fromArg, argAp, apf, ap, config) and - fromArg = false + exists(DataFlowCallable c | + flowFwdOut(call, node, any(CallContextNoCall innercc), c, argAp, apf, ap, config) and + if reducedViableImplInReturn(c, call) then cc = TReturn(c, call) else cc = TAnyCallContext() + ) or exists(AccessPath argAp0 | flowFwdOutFromArg(call, node, argAp0, apf, ap, config) and - flowFwdIsEntered(call, fromArg, argAp, argAp0, config) + flowFwdIsEntered(call, cc, argAp, argAp0, config) ) ) } +pragma[nomagic] +private predicate flowFwdLocalEntry( + Node node, CallContext cc, AccessPathOption argAp, AccessPathFront apf, AccessPath ap, + LocalCallContext localCC, Configuration config +) { + flowFwd(node, cc, argAp, apf, ap, config) and + localFlowEntry(node, config) and + localCC = getLocalCallContext(cc, node.getEnclosingCallable()) +} + pragma[nomagic] private predicate flowFwdStore( - Node node, TypedContent tc, AccessPath ap0, AccessPathFront apf, boolean fromArg, + Node node, TypedContent tc, AccessPath ap0, AccessPathFront apf, CallContext cc, AccessPathOption argAp, Configuration config ) { exists(Node mid, AccessPathFront apf0 | - flowFwd(mid, fromArg, argAp, apf0, ap0, config) and + flowFwd(mid, cc, argAp, apf0, ap0, config) and flowFwdStore0(mid, tc, node, apf0, apf, config) ) } @@ -1764,20 +1775,20 @@ private predicate flowFwdStore0( pragma[nomagic] private predicate flowFwdRead0( - Node node1, TypedContent tc, AccessPathFrontHead apf0, AccessPath ap0, Node node2, - boolean fromArg, AccessPathOption argAp, Configuration config + Node node1, TypedContent tc, AccessPathFrontHead apf0, AccessPath ap0, Node node2, CallContext cc, + AccessPathOption argAp, Configuration config ) { - flowFwd(node1, fromArg, argAp, apf0, ap0, config) and + flowFwd(node1, cc, argAp, apf0, ap0, config) and readCandFwd(node1, tc, apf0, node2, config) } pragma[nomagic] private predicate flowFwdRead( - Node node, AccessPathFrontHead apf0, AccessPath ap0, AccessPathFront apf, boolean fromArg, + Node node, AccessPathFrontHead apf0, AccessPath ap0, AccessPathFront apf, CallContext cc, AccessPathOption argAp, Configuration config ) { exists(Node mid, TypedContent tc | - flowFwdRead0(mid, tc, apf0, ap0, node, fromArg, argAp, config) and + flowFwdRead0(mid, tc, apf0, ap0, node, cc, argAp, config) and flowCand(node, _, _, apf, unbind(config)) and flowCandConsCand(tc, apf, unbind(config)) ) @@ -1795,13 +1806,16 @@ private predicate flowFwdConsCand( pragma[nomagic] private predicate flowFwdIn( - DataFlowCall call, ParameterNode p, boolean fromArg, AccessPathOption argAp, AccessPathFront apf, - AccessPath ap, Configuration config + DataFlowCall call, ParameterNode p, CallContext outercc, CallContext innercc, + AccessPathOption argAp, AccessPathFront apf, AccessPath ap, Configuration config ) { - exists(ArgumentNode arg, boolean allowsFieldFlow | - flowFwd(arg, fromArg, argAp, apf, ap, config) and + exists(ArgumentNode arg, boolean allowsFieldFlow, DataFlowCallable c | + flowFwd(arg, outercc, argAp, apf, ap, config) and flowIntoCallNodeCand2(call, arg, p, allowsFieldFlow, config) and - flowCand(p, _, _, _, unbind(config)) + c = p.getEnclosingCallable() and + c = resolveCall(call, outercc) and + flowCand(p, _, _, _, unbind(config)) and + if recordDataFlowCallSite(call, c) then innercc = TSpecificCall(call) else innercc = TSomeCall() | ap instanceof AccessPathNil or allowsFieldFlow = true ) @@ -1809,13 +1823,19 @@ private predicate flowFwdIn( pragma[nomagic] private predicate flowFwdOut( - DataFlowCall call, Node node, boolean fromArg, AccessPathOption argAp, AccessPathFront apf, - AccessPath ap, Configuration config + DataFlowCall call, Node node, CallContext innercc, DataFlowCallable innerc, + AccessPathOption argAp, AccessPathFront apf, AccessPath ap, Configuration config ) { exists(ReturnNodeExt ret, boolean allowsFieldFlow | - flowFwd(ret, fromArg, argAp, apf, ap, config) and + flowFwd(ret, innercc, argAp, apf, ap, config) and flowOutOfCallNodeCand2(call, ret, node, allowsFieldFlow, config) and - flowCand(node, _, _, _, unbind(config)) + innerc = ret.getEnclosingCallable() and + flowCand(node, _, _, _, unbind(config)) and + ( + resolveReturn(innercc, innerc, call) + or + innercc.(CallContextCall).matchesCall(call) + ) | ap instanceof AccessPathNil or allowsFieldFlow = true ) @@ -1826,7 +1846,7 @@ private predicate flowFwdOutFromArg( DataFlowCall call, Node node, AccessPath argAp, AccessPathFront apf, AccessPath ap, Configuration config ) { - flowFwdOut(call, node, true, TAccessPathSome(argAp), apf, ap, config) + flowFwdOut(call, node, any(CallContextCall ccc), _, TAccessPathSome(argAp), apf, ap, config) } /** @@ -1834,10 +1854,10 @@ private predicate flowFwdOutFromArg( */ pragma[nomagic] private predicate flowFwdIsEntered( - DataFlowCall call, boolean fromArg, AccessPathOption argAp, AccessPath ap, Configuration config + DataFlowCall call, CallContext cc, AccessPathOption argAp, AccessPath ap, Configuration config ) { exists(ParameterNode p, AccessPathFront apf | - flowFwdIn(call, p, fromArg, argAp, apf, ap, config) and + flowFwdIn(call, p, cc, _, argAp, apf, ap, config) and flowCand(p, true, TAccessPathFrontSome(_), apf, config) ) } @@ -1920,7 +1940,7 @@ private predicate flow0( // flow out of a callable flowOut(_, node, _, _, ap, config) and toReturn = true and - if flowFwd(node, true, TAccessPathSome(_), _, ap, config) + if flowFwd(node, any(CallContextCall ccc), TAccessPathSome(_), _, ap, config) then returnAp = TAccessPathSome(ap) else returnAp = TAccessPathNone() } @@ -2006,9 +2026,10 @@ private predicate flowIsReturned( DataFlowCall call, boolean toReturn, AccessPathOption returnAp, AccessPath ap, Configuration config ) { - exists(ReturnNodeExt ret | + exists(ReturnNodeExt ret, CallContextCall ccc | flowOut(call, ret, toReturn, returnAp, ap, config) and - flowFwd(ret, true, TAccessPathSome(_), _, ap, config) + flowFwd(ret, ccc, TAccessPathSome(_), _, ap, config) and + ccc.matchesCall(call) ) } @@ -2031,7 +2052,7 @@ private newtype TSummaryCtx = exists(ReturnNodeExt ret, Configuration config, AccessPath ap0 | parameterFlow(p, ap, ret.getEnclosingCallable(), config) and flow(ret, true, TAccessPathSome(_), ap0, config) and - flowFwd(ret, true, TAccessPathSome(ap), _, ap0, config) + flowFwd(ret, any(CallContextCall ccc), TAccessPathSome(ap), _, ap0, config) ) } @@ -2352,7 +2373,7 @@ private predicate pathOutOfCallable0( ) { pos = getReturnPosition(mid.getNode()) and innercc = mid.getCallContext() and - not innercc instanceof CallContextCall and + innercc instanceof CallContextNoCall and ap = mid.getAp() and config = mid.getConfiguration() } @@ -2867,7 +2888,7 @@ private module FlowExploration { ) { pos = getReturnPosition(mid.getNode()) and innercc = mid.getCallContext() and - not innercc instanceof CallContextCall and + innercc instanceof CallContextNoCall and ap = mid.getAp() and config = mid.getConfiguration() } diff --git a/csharp/ql/src/semmle/code/csharp/dataflow/internal/DataFlowImpl5.qll b/csharp/ql/src/semmle/code/csharp/dataflow/internal/DataFlowImpl5.qll index 5042dce683f..8c210edbe5f 100644 --- a/csharp/ql/src/semmle/code/csharp/dataflow/internal/DataFlowImpl5.qll +++ b/csharp/ql/src/semmle/code/csharp/dataflow/internal/DataFlowImpl5.qll @@ -1066,7 +1066,7 @@ private module LocalFlowBigStep { * Holds if `node` can be the first node in a maximal subsequence of local * flow steps in a dataflow path. */ - private predicate localFlowEntry(Node node, Configuration config) { + predicate localFlowEntry(Node node, Configuration config) { nodeCand2(node, config) and ( config.isSource(node) or @@ -1650,53 +1650,53 @@ private class AccessPathOption extends TAccessPathOption { * Holds if `node` is reachable with access path `ap` from a source in * the configuration `config`. * - * The Boolean `fromArg` records whether the node is reached through an + * 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. */ private predicate flowFwd( - Node node, boolean fromArg, AccessPathOption argAp, AccessPathFront apf, AccessPath ap, + Node node, CallContext cc, AccessPathOption argAp, AccessPathFront apf, AccessPath ap, Configuration config ) { - flowFwd0(node, fromArg, argAp, apf, ap, config) and + flowFwd0(node, cc, argAp, apf, ap, config) and flowCand(node, _, _, apf, config) } private predicate flowFwd0( - Node node, boolean fromArg, AccessPathOption argAp, AccessPathFront apf, AccessPath ap, + Node node, CallContext cc, AccessPathOption argAp, AccessPathFront apf, AccessPath ap, Configuration config ) { flowCand(node, _, _, _, config) and config.isSource(node) and - fromArg = false and + cc instanceof CallContextAny and argAp = TAccessPathNone() and ap = TNil(getNodeType(node)) and apf = ap.(AccessPathNil).getFront() or flowCand(node, _, _, _, unbind(config)) and ( - exists(Node mid | - flowFwd(mid, fromArg, argAp, apf, ap, config) and - localFlowBigStep(mid, node, true, _, config, _) + exists(Node mid, LocalCallContext localCC | + flowFwdLocalEntry(mid, cc, argAp, apf, ap, localCC, config) and + localFlowBigStep(mid, node, true, _, config, localCC) ) or - exists(Node mid, AccessPathNil nil | - flowFwd(mid, fromArg, argAp, _, nil, config) and - localFlowBigStep(mid, node, false, apf, config, _) and + exists(Node mid, AccessPathNil nil, LocalCallContext localCC | + flowFwdLocalEntry(mid, cc, argAp, _, nil, localCC, config) and + localFlowBigStep(mid, node, false, apf, config, localCC) and apf = ap.(AccessPathNil).getFront() ) or exists(Node mid | flowFwd(mid, _, _, apf, ap, config) and jumpStep(mid, node, config) and - fromArg = false and + cc instanceof CallContextAny and argAp = TAccessPathNone() ) or exists(Node mid, AccessPathNil nil | flowFwd(mid, _, _, _, nil, config) and additionalJumpStep(mid, node, config) and - fromArg = false and + cc instanceof CallContextAny and argAp = TAccessPathNone() and ap = TNil(getNodeType(node)) and apf = ap.(AccessPathNil).getFront() @@ -1704,40 +1704,51 @@ private predicate flowFwd0( ) or // store - exists(TypedContent tc | flowFwdStore(node, tc, pop(tc, ap), apf, fromArg, argAp, config)) + exists(TypedContent tc | flowFwdStore(node, tc, pop(tc, ap), apf, cc, argAp, config)) or // read exists(TypedContent tc | - flowFwdRead(node, _, push(tc, ap), apf, fromArg, argAp, config) and + flowFwdRead(node, _, push(tc, ap), apf, cc, argAp, config) and flowFwdConsCand(tc, apf, ap, config) ) or // flow into a callable - flowFwdIn(_, node, _, _, apf, ap, config) and - fromArg = true and + flowFwdIn(_, node, _, cc, _, apf, ap, config) and if flowCand(node, true, _, apf, config) then argAp = TAccessPathSome(ap) else argAp = TAccessPathNone() or // flow out of a callable exists(DataFlowCall call | - flowFwdOut(call, node, fromArg, argAp, apf, ap, config) and - fromArg = false + exists(DataFlowCallable c | + flowFwdOut(call, node, any(CallContextNoCall innercc), c, argAp, apf, ap, config) and + if reducedViableImplInReturn(c, call) then cc = TReturn(c, call) else cc = TAnyCallContext() + ) or exists(AccessPath argAp0 | flowFwdOutFromArg(call, node, argAp0, apf, ap, config) and - flowFwdIsEntered(call, fromArg, argAp, argAp0, config) + flowFwdIsEntered(call, cc, argAp, argAp0, config) ) ) } +pragma[nomagic] +private predicate flowFwdLocalEntry( + Node node, CallContext cc, AccessPathOption argAp, AccessPathFront apf, AccessPath ap, + LocalCallContext localCC, Configuration config +) { + flowFwd(node, cc, argAp, apf, ap, config) and + localFlowEntry(node, config) and + localCC = getLocalCallContext(cc, node.getEnclosingCallable()) +} + pragma[nomagic] private predicate flowFwdStore( - Node node, TypedContent tc, AccessPath ap0, AccessPathFront apf, boolean fromArg, + Node node, TypedContent tc, AccessPath ap0, AccessPathFront apf, CallContext cc, AccessPathOption argAp, Configuration config ) { exists(Node mid, AccessPathFront apf0 | - flowFwd(mid, fromArg, argAp, apf0, ap0, config) and + flowFwd(mid, cc, argAp, apf0, ap0, config) and flowFwdStore0(mid, tc, node, apf0, apf, config) ) } @@ -1764,20 +1775,20 @@ private predicate flowFwdStore0( pragma[nomagic] private predicate flowFwdRead0( - Node node1, TypedContent tc, AccessPathFrontHead apf0, AccessPath ap0, Node node2, - boolean fromArg, AccessPathOption argAp, Configuration config + Node node1, TypedContent tc, AccessPathFrontHead apf0, AccessPath ap0, Node node2, CallContext cc, + AccessPathOption argAp, Configuration config ) { - flowFwd(node1, fromArg, argAp, apf0, ap0, config) and + flowFwd(node1, cc, argAp, apf0, ap0, config) and readCandFwd(node1, tc, apf0, node2, config) } pragma[nomagic] private predicate flowFwdRead( - Node node, AccessPathFrontHead apf0, AccessPath ap0, AccessPathFront apf, boolean fromArg, + Node node, AccessPathFrontHead apf0, AccessPath ap0, AccessPathFront apf, CallContext cc, AccessPathOption argAp, Configuration config ) { exists(Node mid, TypedContent tc | - flowFwdRead0(mid, tc, apf0, ap0, node, fromArg, argAp, config) and + flowFwdRead0(mid, tc, apf0, ap0, node, cc, argAp, config) and flowCand(node, _, _, apf, unbind(config)) and flowCandConsCand(tc, apf, unbind(config)) ) @@ -1795,13 +1806,16 @@ private predicate flowFwdConsCand( pragma[nomagic] private predicate flowFwdIn( - DataFlowCall call, ParameterNode p, boolean fromArg, AccessPathOption argAp, AccessPathFront apf, - AccessPath ap, Configuration config + DataFlowCall call, ParameterNode p, CallContext outercc, CallContext innercc, + AccessPathOption argAp, AccessPathFront apf, AccessPath ap, Configuration config ) { - exists(ArgumentNode arg, boolean allowsFieldFlow | - flowFwd(arg, fromArg, argAp, apf, ap, config) and + exists(ArgumentNode arg, boolean allowsFieldFlow, DataFlowCallable c | + flowFwd(arg, outercc, argAp, apf, ap, config) and flowIntoCallNodeCand2(call, arg, p, allowsFieldFlow, config) and - flowCand(p, _, _, _, unbind(config)) + c = p.getEnclosingCallable() and + c = resolveCall(call, outercc) and + flowCand(p, _, _, _, unbind(config)) and + if recordDataFlowCallSite(call, c) then innercc = TSpecificCall(call) else innercc = TSomeCall() | ap instanceof AccessPathNil or allowsFieldFlow = true ) @@ -1809,13 +1823,19 @@ private predicate flowFwdIn( pragma[nomagic] private predicate flowFwdOut( - DataFlowCall call, Node node, boolean fromArg, AccessPathOption argAp, AccessPathFront apf, - AccessPath ap, Configuration config + DataFlowCall call, Node node, CallContext innercc, DataFlowCallable innerc, + AccessPathOption argAp, AccessPathFront apf, AccessPath ap, Configuration config ) { exists(ReturnNodeExt ret, boolean allowsFieldFlow | - flowFwd(ret, fromArg, argAp, apf, ap, config) and + flowFwd(ret, innercc, argAp, apf, ap, config) and flowOutOfCallNodeCand2(call, ret, node, allowsFieldFlow, config) and - flowCand(node, _, _, _, unbind(config)) + innerc = ret.getEnclosingCallable() and + flowCand(node, _, _, _, unbind(config)) and + ( + resolveReturn(innercc, innerc, call) + or + innercc.(CallContextCall).matchesCall(call) + ) | ap instanceof AccessPathNil or allowsFieldFlow = true ) @@ -1826,7 +1846,7 @@ private predicate flowFwdOutFromArg( DataFlowCall call, Node node, AccessPath argAp, AccessPathFront apf, AccessPath ap, Configuration config ) { - flowFwdOut(call, node, true, TAccessPathSome(argAp), apf, ap, config) + flowFwdOut(call, node, any(CallContextCall ccc), _, TAccessPathSome(argAp), apf, ap, config) } /** @@ -1834,10 +1854,10 @@ private predicate flowFwdOutFromArg( */ pragma[nomagic] private predicate flowFwdIsEntered( - DataFlowCall call, boolean fromArg, AccessPathOption argAp, AccessPath ap, Configuration config + DataFlowCall call, CallContext cc, AccessPathOption argAp, AccessPath ap, Configuration config ) { exists(ParameterNode p, AccessPathFront apf | - flowFwdIn(call, p, fromArg, argAp, apf, ap, config) and + flowFwdIn(call, p, cc, _, argAp, apf, ap, config) and flowCand(p, true, TAccessPathFrontSome(_), apf, config) ) } @@ -1920,7 +1940,7 @@ private predicate flow0( // flow out of a callable flowOut(_, node, _, _, ap, config) and toReturn = true and - if flowFwd(node, true, TAccessPathSome(_), _, ap, config) + if flowFwd(node, any(CallContextCall ccc), TAccessPathSome(_), _, ap, config) then returnAp = TAccessPathSome(ap) else returnAp = TAccessPathNone() } @@ -2006,9 +2026,10 @@ private predicate flowIsReturned( DataFlowCall call, boolean toReturn, AccessPathOption returnAp, AccessPath ap, Configuration config ) { - exists(ReturnNodeExt ret | + exists(ReturnNodeExt ret, CallContextCall ccc | flowOut(call, ret, toReturn, returnAp, ap, config) and - flowFwd(ret, true, TAccessPathSome(_), _, ap, config) + flowFwd(ret, ccc, TAccessPathSome(_), _, ap, config) and + ccc.matchesCall(call) ) } @@ -2031,7 +2052,7 @@ private newtype TSummaryCtx = exists(ReturnNodeExt ret, Configuration config, AccessPath ap0 | parameterFlow(p, ap, ret.getEnclosingCallable(), config) and flow(ret, true, TAccessPathSome(_), ap0, config) and - flowFwd(ret, true, TAccessPathSome(ap), _, ap0, config) + flowFwd(ret, any(CallContextCall ccc), TAccessPathSome(ap), _, ap0, config) ) } @@ -2352,7 +2373,7 @@ private predicate pathOutOfCallable0( ) { pos = getReturnPosition(mid.getNode()) and innercc = mid.getCallContext() and - not innercc instanceof CallContextCall and + innercc instanceof CallContextNoCall and ap = mid.getAp() and config = mid.getConfiguration() } @@ -2867,7 +2888,7 @@ private module FlowExploration { ) { pos = getReturnPosition(mid.getNode()) and innercc = mid.getCallContext() and - not innercc instanceof CallContextCall and + innercc instanceof CallContextNoCall and ap = mid.getAp() and config = mid.getConfiguration() } diff --git a/csharp/ql/src/semmle/code/csharp/dataflow/internal/DataFlowImplCommon.qll b/csharp/ql/src/semmle/code/csharp/dataflow/internal/DataFlowImplCommon.qll index 27ab1d01feb..892250f44bb 100644 --- a/csharp/ql/src/semmle/code/csharp/dataflow/internal/DataFlowImplCommon.qll +++ b/csharp/ql/src/semmle/code/csharp/dataflow/internal/DataFlowImplCommon.qll @@ -512,13 +512,19 @@ abstract class CallContext extends TCallContext { abstract predicate relevantFor(DataFlowCallable callable); } -class CallContextAny extends CallContext, TAnyCallContext { +abstract class CallContextNoCall extends CallContext { } + +class CallContextAny extends CallContextNoCall, TAnyCallContext { override string toString() { result = "CcAny" } override predicate relevantFor(DataFlowCallable callable) { any() } } -abstract class CallContextCall extends CallContext { } +abstract class CallContextCall extends CallContext { + /** Holds if this call context may be `call`. */ + bindingset[call] + abstract predicate matchesCall(DataFlowCall call); +} class CallContextSpecificCall extends CallContextCall, TSpecificCall { override string toString() { @@ -529,6 +535,8 @@ class CallContextSpecificCall extends CallContextCall, TSpecificCall { recordDataFlowCallSite(getCall(), callable) } + override predicate matchesCall(DataFlowCall call) { call = this.getCall() } + DataFlowCall getCall() { this = TSpecificCall(result) } } @@ -538,9 +546,11 @@ class CallContextSomeCall extends CallContextCall, TSomeCall { override predicate relevantFor(DataFlowCallable callable) { exists(ParameterNode p | p.getEnclosingCallable() = callable) } + + override predicate matchesCall(DataFlowCall call) { any() } } -class CallContextReturn extends CallContext, TReturn { +class CallContextReturn extends CallContextNoCall, TReturn { override string toString() { exists(DataFlowCall call | this = TReturn(_, call) | result = "CcReturn(" + call + ")") } diff --git a/csharp/ql/src/semmle/code/csharp/frameworks/system/Xml.qll b/csharp/ql/src/semmle/code/csharp/frameworks/system/Xml.qll index 21ca1431012..c84fa47a103 100644 --- a/csharp/ql/src/semmle/code/csharp/frameworks/system/Xml.qll +++ b/csharp/ql/src/semmle/code/csharp/frameworks/system/Xml.qll @@ -2,7 +2,7 @@ import csharp private import semmle.code.csharp.frameworks.System -private import semmle.code.csharp.dataflow.DataFlow2 +private import semmle.code.csharp.dataflow.DataFlow3 /** The `System.Xml` namespace. */ class SystemXmlNamespace extends Namespace { @@ -163,7 +163,7 @@ class XmlReaderSettingsCreation extends ObjectCreation { } } -private class SettingsDataFlowConfig extends DataFlow2::Configuration { +private class SettingsDataFlowConfig extends DataFlow3::Configuration { SettingsDataFlowConfig() { this = "SettingsDataFlowConfig" } override predicate isSource(DataFlow::Node source) { diff --git a/csharp/ql/test/library-tests/expressions/ArrayCreation11.expected b/csharp/ql/test/library-tests/expressions/ArrayCreation11.expected new file mode 100644 index 00000000000..f0e5752ac75 --- /dev/null +++ b/csharp/ql/test/library-tests/expressions/ArrayCreation11.expected @@ -0,0 +1,3 @@ +| expressions.cs:168:27:168:44 | array creation of type Object[] | expressions.cs:168:27:168:44 | 1 | true | +| expressions.cs:409:23:409:65 | array creation of type Int32[,] | expressions.cs:409:23:409:65 | 3 | true | +| expressions.cs:409:23:409:65 | array creation of type Int32[,] | expressions.cs:409:23:409:65 | 3 | true | diff --git a/csharp/ql/test/library-tests/expressions/ArrayCreation11.ql b/csharp/ql/test/library-tests/expressions/ArrayCreation11.ql new file mode 100644 index 00000000000..6d7a501fefa --- /dev/null +++ b/csharp/ql/test/library-tests/expressions/ArrayCreation11.ql @@ -0,0 +1,13 @@ +/** + * @name Test for array creations + */ + +import csharp + +private boolean isImplicit(Expr expr) { + if expr.isImplicit() then result = true else result = false +} + +from ArrayCreation ac, Expr expr +where ac.isImplicitlySized() and not ac.isImplicitlyTyped() and expr = ac.getALengthArgument() +select ac, expr, isImplicit(expr) diff --git a/java/ql/src/semmle/code/java/dataflow/internal/DataFlowImpl.qll b/java/ql/src/semmle/code/java/dataflow/internal/DataFlowImpl.qll index 5042dce683f..8c210edbe5f 100644 --- a/java/ql/src/semmle/code/java/dataflow/internal/DataFlowImpl.qll +++ b/java/ql/src/semmle/code/java/dataflow/internal/DataFlowImpl.qll @@ -1066,7 +1066,7 @@ private module LocalFlowBigStep { * Holds if `node` can be the first node in a maximal subsequence of local * flow steps in a dataflow path. */ - private predicate localFlowEntry(Node node, Configuration config) { + predicate localFlowEntry(Node node, Configuration config) { nodeCand2(node, config) and ( config.isSource(node) or @@ -1650,53 +1650,53 @@ private class AccessPathOption extends TAccessPathOption { * Holds if `node` is reachable with access path `ap` from a source in * the configuration `config`. * - * The Boolean `fromArg` records whether the node is reached through an + * 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. */ private predicate flowFwd( - Node node, boolean fromArg, AccessPathOption argAp, AccessPathFront apf, AccessPath ap, + Node node, CallContext cc, AccessPathOption argAp, AccessPathFront apf, AccessPath ap, Configuration config ) { - flowFwd0(node, fromArg, argAp, apf, ap, config) and + flowFwd0(node, cc, argAp, apf, ap, config) and flowCand(node, _, _, apf, config) } private predicate flowFwd0( - Node node, boolean fromArg, AccessPathOption argAp, AccessPathFront apf, AccessPath ap, + Node node, CallContext cc, AccessPathOption argAp, AccessPathFront apf, AccessPath ap, Configuration config ) { flowCand(node, _, _, _, config) and config.isSource(node) and - fromArg = false and + cc instanceof CallContextAny and argAp = TAccessPathNone() and ap = TNil(getNodeType(node)) and apf = ap.(AccessPathNil).getFront() or flowCand(node, _, _, _, unbind(config)) and ( - exists(Node mid | - flowFwd(mid, fromArg, argAp, apf, ap, config) and - localFlowBigStep(mid, node, true, _, config, _) + exists(Node mid, LocalCallContext localCC | + flowFwdLocalEntry(mid, cc, argAp, apf, ap, localCC, config) and + localFlowBigStep(mid, node, true, _, config, localCC) ) or - exists(Node mid, AccessPathNil nil | - flowFwd(mid, fromArg, argAp, _, nil, config) and - localFlowBigStep(mid, node, false, apf, config, _) and + exists(Node mid, AccessPathNil nil, LocalCallContext localCC | + flowFwdLocalEntry(mid, cc, argAp, _, nil, localCC, config) and + localFlowBigStep(mid, node, false, apf, config, localCC) and apf = ap.(AccessPathNil).getFront() ) or exists(Node mid | flowFwd(mid, _, _, apf, ap, config) and jumpStep(mid, node, config) and - fromArg = false and + cc instanceof CallContextAny and argAp = TAccessPathNone() ) or exists(Node mid, AccessPathNil nil | flowFwd(mid, _, _, _, nil, config) and additionalJumpStep(mid, node, config) and - fromArg = false and + cc instanceof CallContextAny and argAp = TAccessPathNone() and ap = TNil(getNodeType(node)) and apf = ap.(AccessPathNil).getFront() @@ -1704,40 +1704,51 @@ private predicate flowFwd0( ) or // store - exists(TypedContent tc | flowFwdStore(node, tc, pop(tc, ap), apf, fromArg, argAp, config)) + exists(TypedContent tc | flowFwdStore(node, tc, pop(tc, ap), apf, cc, argAp, config)) or // read exists(TypedContent tc | - flowFwdRead(node, _, push(tc, ap), apf, fromArg, argAp, config) and + flowFwdRead(node, _, push(tc, ap), apf, cc, argAp, config) and flowFwdConsCand(tc, apf, ap, config) ) or // flow into a callable - flowFwdIn(_, node, _, _, apf, ap, config) and - fromArg = true and + flowFwdIn(_, node, _, cc, _, apf, ap, config) and if flowCand(node, true, _, apf, config) then argAp = TAccessPathSome(ap) else argAp = TAccessPathNone() or // flow out of a callable exists(DataFlowCall call | - flowFwdOut(call, node, fromArg, argAp, apf, ap, config) and - fromArg = false + exists(DataFlowCallable c | + flowFwdOut(call, node, any(CallContextNoCall innercc), c, argAp, apf, ap, config) and + if reducedViableImplInReturn(c, call) then cc = TReturn(c, call) else cc = TAnyCallContext() + ) or exists(AccessPath argAp0 | flowFwdOutFromArg(call, node, argAp0, apf, ap, config) and - flowFwdIsEntered(call, fromArg, argAp, argAp0, config) + flowFwdIsEntered(call, cc, argAp, argAp0, config) ) ) } +pragma[nomagic] +private predicate flowFwdLocalEntry( + Node node, CallContext cc, AccessPathOption argAp, AccessPathFront apf, AccessPath ap, + LocalCallContext localCC, Configuration config +) { + flowFwd(node, cc, argAp, apf, ap, config) and + localFlowEntry(node, config) and + localCC = getLocalCallContext(cc, node.getEnclosingCallable()) +} + pragma[nomagic] private predicate flowFwdStore( - Node node, TypedContent tc, AccessPath ap0, AccessPathFront apf, boolean fromArg, + Node node, TypedContent tc, AccessPath ap0, AccessPathFront apf, CallContext cc, AccessPathOption argAp, Configuration config ) { exists(Node mid, AccessPathFront apf0 | - flowFwd(mid, fromArg, argAp, apf0, ap0, config) and + flowFwd(mid, cc, argAp, apf0, ap0, config) and flowFwdStore0(mid, tc, node, apf0, apf, config) ) } @@ -1764,20 +1775,20 @@ private predicate flowFwdStore0( pragma[nomagic] private predicate flowFwdRead0( - Node node1, TypedContent tc, AccessPathFrontHead apf0, AccessPath ap0, Node node2, - boolean fromArg, AccessPathOption argAp, Configuration config + Node node1, TypedContent tc, AccessPathFrontHead apf0, AccessPath ap0, Node node2, CallContext cc, + AccessPathOption argAp, Configuration config ) { - flowFwd(node1, fromArg, argAp, apf0, ap0, config) and + flowFwd(node1, cc, argAp, apf0, ap0, config) and readCandFwd(node1, tc, apf0, node2, config) } pragma[nomagic] private predicate flowFwdRead( - Node node, AccessPathFrontHead apf0, AccessPath ap0, AccessPathFront apf, boolean fromArg, + Node node, AccessPathFrontHead apf0, AccessPath ap0, AccessPathFront apf, CallContext cc, AccessPathOption argAp, Configuration config ) { exists(Node mid, TypedContent tc | - flowFwdRead0(mid, tc, apf0, ap0, node, fromArg, argAp, config) and + flowFwdRead0(mid, tc, apf0, ap0, node, cc, argAp, config) and flowCand(node, _, _, apf, unbind(config)) and flowCandConsCand(tc, apf, unbind(config)) ) @@ -1795,13 +1806,16 @@ private predicate flowFwdConsCand( pragma[nomagic] private predicate flowFwdIn( - DataFlowCall call, ParameterNode p, boolean fromArg, AccessPathOption argAp, AccessPathFront apf, - AccessPath ap, Configuration config + DataFlowCall call, ParameterNode p, CallContext outercc, CallContext innercc, + AccessPathOption argAp, AccessPathFront apf, AccessPath ap, Configuration config ) { - exists(ArgumentNode arg, boolean allowsFieldFlow | - flowFwd(arg, fromArg, argAp, apf, ap, config) and + exists(ArgumentNode arg, boolean allowsFieldFlow, DataFlowCallable c | + flowFwd(arg, outercc, argAp, apf, ap, config) and flowIntoCallNodeCand2(call, arg, p, allowsFieldFlow, config) and - flowCand(p, _, _, _, unbind(config)) + c = p.getEnclosingCallable() and + c = resolveCall(call, outercc) and + flowCand(p, _, _, _, unbind(config)) and + if recordDataFlowCallSite(call, c) then innercc = TSpecificCall(call) else innercc = TSomeCall() | ap instanceof AccessPathNil or allowsFieldFlow = true ) @@ -1809,13 +1823,19 @@ private predicate flowFwdIn( pragma[nomagic] private predicate flowFwdOut( - DataFlowCall call, Node node, boolean fromArg, AccessPathOption argAp, AccessPathFront apf, - AccessPath ap, Configuration config + DataFlowCall call, Node node, CallContext innercc, DataFlowCallable innerc, + AccessPathOption argAp, AccessPathFront apf, AccessPath ap, Configuration config ) { exists(ReturnNodeExt ret, boolean allowsFieldFlow | - flowFwd(ret, fromArg, argAp, apf, ap, config) and + flowFwd(ret, innercc, argAp, apf, ap, config) and flowOutOfCallNodeCand2(call, ret, node, allowsFieldFlow, config) and - flowCand(node, _, _, _, unbind(config)) + innerc = ret.getEnclosingCallable() and + flowCand(node, _, _, _, unbind(config)) and + ( + resolveReturn(innercc, innerc, call) + or + innercc.(CallContextCall).matchesCall(call) + ) | ap instanceof AccessPathNil or allowsFieldFlow = true ) @@ -1826,7 +1846,7 @@ private predicate flowFwdOutFromArg( DataFlowCall call, Node node, AccessPath argAp, AccessPathFront apf, AccessPath ap, Configuration config ) { - flowFwdOut(call, node, true, TAccessPathSome(argAp), apf, ap, config) + flowFwdOut(call, node, any(CallContextCall ccc), _, TAccessPathSome(argAp), apf, ap, config) } /** @@ -1834,10 +1854,10 @@ private predicate flowFwdOutFromArg( */ pragma[nomagic] private predicate flowFwdIsEntered( - DataFlowCall call, boolean fromArg, AccessPathOption argAp, AccessPath ap, Configuration config + DataFlowCall call, CallContext cc, AccessPathOption argAp, AccessPath ap, Configuration config ) { exists(ParameterNode p, AccessPathFront apf | - flowFwdIn(call, p, fromArg, argAp, apf, ap, config) and + flowFwdIn(call, p, cc, _, argAp, apf, ap, config) and flowCand(p, true, TAccessPathFrontSome(_), apf, config) ) } @@ -1920,7 +1940,7 @@ private predicate flow0( // flow out of a callable flowOut(_, node, _, _, ap, config) and toReturn = true and - if flowFwd(node, true, TAccessPathSome(_), _, ap, config) + if flowFwd(node, any(CallContextCall ccc), TAccessPathSome(_), _, ap, config) then returnAp = TAccessPathSome(ap) else returnAp = TAccessPathNone() } @@ -2006,9 +2026,10 @@ private predicate flowIsReturned( DataFlowCall call, boolean toReturn, AccessPathOption returnAp, AccessPath ap, Configuration config ) { - exists(ReturnNodeExt ret | + exists(ReturnNodeExt ret, CallContextCall ccc | flowOut(call, ret, toReturn, returnAp, ap, config) and - flowFwd(ret, true, TAccessPathSome(_), _, ap, config) + flowFwd(ret, ccc, TAccessPathSome(_), _, ap, config) and + ccc.matchesCall(call) ) } @@ -2031,7 +2052,7 @@ private newtype TSummaryCtx = exists(ReturnNodeExt ret, Configuration config, AccessPath ap0 | parameterFlow(p, ap, ret.getEnclosingCallable(), config) and flow(ret, true, TAccessPathSome(_), ap0, config) and - flowFwd(ret, true, TAccessPathSome(ap), _, ap0, config) + flowFwd(ret, any(CallContextCall ccc), TAccessPathSome(ap), _, ap0, config) ) } @@ -2352,7 +2373,7 @@ private predicate pathOutOfCallable0( ) { pos = getReturnPosition(mid.getNode()) and innercc = mid.getCallContext() and - not innercc instanceof CallContextCall and + innercc instanceof CallContextNoCall and ap = mid.getAp() and config = mid.getConfiguration() } @@ -2867,7 +2888,7 @@ private module FlowExploration { ) { pos = getReturnPosition(mid.getNode()) and innercc = mid.getCallContext() and - not innercc instanceof CallContextCall and + innercc instanceof CallContextNoCall and ap = mid.getAp() and config = mid.getConfiguration() } diff --git a/java/ql/src/semmle/code/java/dataflow/internal/DataFlowImpl2.qll b/java/ql/src/semmle/code/java/dataflow/internal/DataFlowImpl2.qll index 5042dce683f..8c210edbe5f 100644 --- a/java/ql/src/semmle/code/java/dataflow/internal/DataFlowImpl2.qll +++ b/java/ql/src/semmle/code/java/dataflow/internal/DataFlowImpl2.qll @@ -1066,7 +1066,7 @@ private module LocalFlowBigStep { * Holds if `node` can be the first node in a maximal subsequence of local * flow steps in a dataflow path. */ - private predicate localFlowEntry(Node node, Configuration config) { + predicate localFlowEntry(Node node, Configuration config) { nodeCand2(node, config) and ( config.isSource(node) or @@ -1650,53 +1650,53 @@ private class AccessPathOption extends TAccessPathOption { * Holds if `node` is reachable with access path `ap` from a source in * the configuration `config`. * - * The Boolean `fromArg` records whether the node is reached through an + * 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. */ private predicate flowFwd( - Node node, boolean fromArg, AccessPathOption argAp, AccessPathFront apf, AccessPath ap, + Node node, CallContext cc, AccessPathOption argAp, AccessPathFront apf, AccessPath ap, Configuration config ) { - flowFwd0(node, fromArg, argAp, apf, ap, config) and + flowFwd0(node, cc, argAp, apf, ap, config) and flowCand(node, _, _, apf, config) } private predicate flowFwd0( - Node node, boolean fromArg, AccessPathOption argAp, AccessPathFront apf, AccessPath ap, + Node node, CallContext cc, AccessPathOption argAp, AccessPathFront apf, AccessPath ap, Configuration config ) { flowCand(node, _, _, _, config) and config.isSource(node) and - fromArg = false and + cc instanceof CallContextAny and argAp = TAccessPathNone() and ap = TNil(getNodeType(node)) and apf = ap.(AccessPathNil).getFront() or flowCand(node, _, _, _, unbind(config)) and ( - exists(Node mid | - flowFwd(mid, fromArg, argAp, apf, ap, config) and - localFlowBigStep(mid, node, true, _, config, _) + exists(Node mid, LocalCallContext localCC | + flowFwdLocalEntry(mid, cc, argAp, apf, ap, localCC, config) and + localFlowBigStep(mid, node, true, _, config, localCC) ) or - exists(Node mid, AccessPathNil nil | - flowFwd(mid, fromArg, argAp, _, nil, config) and - localFlowBigStep(mid, node, false, apf, config, _) and + exists(Node mid, AccessPathNil nil, LocalCallContext localCC | + flowFwdLocalEntry(mid, cc, argAp, _, nil, localCC, config) and + localFlowBigStep(mid, node, false, apf, config, localCC) and apf = ap.(AccessPathNil).getFront() ) or exists(Node mid | flowFwd(mid, _, _, apf, ap, config) and jumpStep(mid, node, config) and - fromArg = false and + cc instanceof CallContextAny and argAp = TAccessPathNone() ) or exists(Node mid, AccessPathNil nil | flowFwd(mid, _, _, _, nil, config) and additionalJumpStep(mid, node, config) and - fromArg = false and + cc instanceof CallContextAny and argAp = TAccessPathNone() and ap = TNil(getNodeType(node)) and apf = ap.(AccessPathNil).getFront() @@ -1704,40 +1704,51 @@ private predicate flowFwd0( ) or // store - exists(TypedContent tc | flowFwdStore(node, tc, pop(tc, ap), apf, fromArg, argAp, config)) + exists(TypedContent tc | flowFwdStore(node, tc, pop(tc, ap), apf, cc, argAp, config)) or // read exists(TypedContent tc | - flowFwdRead(node, _, push(tc, ap), apf, fromArg, argAp, config) and + flowFwdRead(node, _, push(tc, ap), apf, cc, argAp, config) and flowFwdConsCand(tc, apf, ap, config) ) or // flow into a callable - flowFwdIn(_, node, _, _, apf, ap, config) and - fromArg = true and + flowFwdIn(_, node, _, cc, _, apf, ap, config) and if flowCand(node, true, _, apf, config) then argAp = TAccessPathSome(ap) else argAp = TAccessPathNone() or // flow out of a callable exists(DataFlowCall call | - flowFwdOut(call, node, fromArg, argAp, apf, ap, config) and - fromArg = false + exists(DataFlowCallable c | + flowFwdOut(call, node, any(CallContextNoCall innercc), c, argAp, apf, ap, config) and + if reducedViableImplInReturn(c, call) then cc = TReturn(c, call) else cc = TAnyCallContext() + ) or exists(AccessPath argAp0 | flowFwdOutFromArg(call, node, argAp0, apf, ap, config) and - flowFwdIsEntered(call, fromArg, argAp, argAp0, config) + flowFwdIsEntered(call, cc, argAp, argAp0, config) ) ) } +pragma[nomagic] +private predicate flowFwdLocalEntry( + Node node, CallContext cc, AccessPathOption argAp, AccessPathFront apf, AccessPath ap, + LocalCallContext localCC, Configuration config +) { + flowFwd(node, cc, argAp, apf, ap, config) and + localFlowEntry(node, config) and + localCC = getLocalCallContext(cc, node.getEnclosingCallable()) +} + pragma[nomagic] private predicate flowFwdStore( - Node node, TypedContent tc, AccessPath ap0, AccessPathFront apf, boolean fromArg, + Node node, TypedContent tc, AccessPath ap0, AccessPathFront apf, CallContext cc, AccessPathOption argAp, Configuration config ) { exists(Node mid, AccessPathFront apf0 | - flowFwd(mid, fromArg, argAp, apf0, ap0, config) and + flowFwd(mid, cc, argAp, apf0, ap0, config) and flowFwdStore0(mid, tc, node, apf0, apf, config) ) } @@ -1764,20 +1775,20 @@ private predicate flowFwdStore0( pragma[nomagic] private predicate flowFwdRead0( - Node node1, TypedContent tc, AccessPathFrontHead apf0, AccessPath ap0, Node node2, - boolean fromArg, AccessPathOption argAp, Configuration config + Node node1, TypedContent tc, AccessPathFrontHead apf0, AccessPath ap0, Node node2, CallContext cc, + AccessPathOption argAp, Configuration config ) { - flowFwd(node1, fromArg, argAp, apf0, ap0, config) and + flowFwd(node1, cc, argAp, apf0, ap0, config) and readCandFwd(node1, tc, apf0, node2, config) } pragma[nomagic] private predicate flowFwdRead( - Node node, AccessPathFrontHead apf0, AccessPath ap0, AccessPathFront apf, boolean fromArg, + Node node, AccessPathFrontHead apf0, AccessPath ap0, AccessPathFront apf, CallContext cc, AccessPathOption argAp, Configuration config ) { exists(Node mid, TypedContent tc | - flowFwdRead0(mid, tc, apf0, ap0, node, fromArg, argAp, config) and + flowFwdRead0(mid, tc, apf0, ap0, node, cc, argAp, config) and flowCand(node, _, _, apf, unbind(config)) and flowCandConsCand(tc, apf, unbind(config)) ) @@ -1795,13 +1806,16 @@ private predicate flowFwdConsCand( pragma[nomagic] private predicate flowFwdIn( - DataFlowCall call, ParameterNode p, boolean fromArg, AccessPathOption argAp, AccessPathFront apf, - AccessPath ap, Configuration config + DataFlowCall call, ParameterNode p, CallContext outercc, CallContext innercc, + AccessPathOption argAp, AccessPathFront apf, AccessPath ap, Configuration config ) { - exists(ArgumentNode arg, boolean allowsFieldFlow | - flowFwd(arg, fromArg, argAp, apf, ap, config) and + exists(ArgumentNode arg, boolean allowsFieldFlow, DataFlowCallable c | + flowFwd(arg, outercc, argAp, apf, ap, config) and flowIntoCallNodeCand2(call, arg, p, allowsFieldFlow, config) and - flowCand(p, _, _, _, unbind(config)) + c = p.getEnclosingCallable() and + c = resolveCall(call, outercc) and + flowCand(p, _, _, _, unbind(config)) and + if recordDataFlowCallSite(call, c) then innercc = TSpecificCall(call) else innercc = TSomeCall() | ap instanceof AccessPathNil or allowsFieldFlow = true ) @@ -1809,13 +1823,19 @@ private predicate flowFwdIn( pragma[nomagic] private predicate flowFwdOut( - DataFlowCall call, Node node, boolean fromArg, AccessPathOption argAp, AccessPathFront apf, - AccessPath ap, Configuration config + DataFlowCall call, Node node, CallContext innercc, DataFlowCallable innerc, + AccessPathOption argAp, AccessPathFront apf, AccessPath ap, Configuration config ) { exists(ReturnNodeExt ret, boolean allowsFieldFlow | - flowFwd(ret, fromArg, argAp, apf, ap, config) and + flowFwd(ret, innercc, argAp, apf, ap, config) and flowOutOfCallNodeCand2(call, ret, node, allowsFieldFlow, config) and - flowCand(node, _, _, _, unbind(config)) + innerc = ret.getEnclosingCallable() and + flowCand(node, _, _, _, unbind(config)) and + ( + resolveReturn(innercc, innerc, call) + or + innercc.(CallContextCall).matchesCall(call) + ) | ap instanceof AccessPathNil or allowsFieldFlow = true ) @@ -1826,7 +1846,7 @@ private predicate flowFwdOutFromArg( DataFlowCall call, Node node, AccessPath argAp, AccessPathFront apf, AccessPath ap, Configuration config ) { - flowFwdOut(call, node, true, TAccessPathSome(argAp), apf, ap, config) + flowFwdOut(call, node, any(CallContextCall ccc), _, TAccessPathSome(argAp), apf, ap, config) } /** @@ -1834,10 +1854,10 @@ private predicate flowFwdOutFromArg( */ pragma[nomagic] private predicate flowFwdIsEntered( - DataFlowCall call, boolean fromArg, AccessPathOption argAp, AccessPath ap, Configuration config + DataFlowCall call, CallContext cc, AccessPathOption argAp, AccessPath ap, Configuration config ) { exists(ParameterNode p, AccessPathFront apf | - flowFwdIn(call, p, fromArg, argAp, apf, ap, config) and + flowFwdIn(call, p, cc, _, argAp, apf, ap, config) and flowCand(p, true, TAccessPathFrontSome(_), apf, config) ) } @@ -1920,7 +1940,7 @@ private predicate flow0( // flow out of a callable flowOut(_, node, _, _, ap, config) and toReturn = true and - if flowFwd(node, true, TAccessPathSome(_), _, ap, config) + if flowFwd(node, any(CallContextCall ccc), TAccessPathSome(_), _, ap, config) then returnAp = TAccessPathSome(ap) else returnAp = TAccessPathNone() } @@ -2006,9 +2026,10 @@ private predicate flowIsReturned( DataFlowCall call, boolean toReturn, AccessPathOption returnAp, AccessPath ap, Configuration config ) { - exists(ReturnNodeExt ret | + exists(ReturnNodeExt ret, CallContextCall ccc | flowOut(call, ret, toReturn, returnAp, ap, config) and - flowFwd(ret, true, TAccessPathSome(_), _, ap, config) + flowFwd(ret, ccc, TAccessPathSome(_), _, ap, config) and + ccc.matchesCall(call) ) } @@ -2031,7 +2052,7 @@ private newtype TSummaryCtx = exists(ReturnNodeExt ret, Configuration config, AccessPath ap0 | parameterFlow(p, ap, ret.getEnclosingCallable(), config) and flow(ret, true, TAccessPathSome(_), ap0, config) and - flowFwd(ret, true, TAccessPathSome(ap), _, ap0, config) + flowFwd(ret, any(CallContextCall ccc), TAccessPathSome(ap), _, ap0, config) ) } @@ -2352,7 +2373,7 @@ private predicate pathOutOfCallable0( ) { pos = getReturnPosition(mid.getNode()) and innercc = mid.getCallContext() and - not innercc instanceof CallContextCall and + innercc instanceof CallContextNoCall and ap = mid.getAp() and config = mid.getConfiguration() } @@ -2867,7 +2888,7 @@ private module FlowExploration { ) { pos = getReturnPosition(mid.getNode()) and innercc = mid.getCallContext() and - not innercc instanceof CallContextCall and + innercc instanceof CallContextNoCall and ap = mid.getAp() and config = mid.getConfiguration() } diff --git a/java/ql/src/semmle/code/java/dataflow/internal/DataFlowImpl3.qll b/java/ql/src/semmle/code/java/dataflow/internal/DataFlowImpl3.qll index 5042dce683f..8c210edbe5f 100644 --- a/java/ql/src/semmle/code/java/dataflow/internal/DataFlowImpl3.qll +++ b/java/ql/src/semmle/code/java/dataflow/internal/DataFlowImpl3.qll @@ -1066,7 +1066,7 @@ private module LocalFlowBigStep { * Holds if `node` can be the first node in a maximal subsequence of local * flow steps in a dataflow path. */ - private predicate localFlowEntry(Node node, Configuration config) { + predicate localFlowEntry(Node node, Configuration config) { nodeCand2(node, config) and ( config.isSource(node) or @@ -1650,53 +1650,53 @@ private class AccessPathOption extends TAccessPathOption { * Holds if `node` is reachable with access path `ap` from a source in * the configuration `config`. * - * The Boolean `fromArg` records whether the node is reached through an + * 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. */ private predicate flowFwd( - Node node, boolean fromArg, AccessPathOption argAp, AccessPathFront apf, AccessPath ap, + Node node, CallContext cc, AccessPathOption argAp, AccessPathFront apf, AccessPath ap, Configuration config ) { - flowFwd0(node, fromArg, argAp, apf, ap, config) and + flowFwd0(node, cc, argAp, apf, ap, config) and flowCand(node, _, _, apf, config) } private predicate flowFwd0( - Node node, boolean fromArg, AccessPathOption argAp, AccessPathFront apf, AccessPath ap, + Node node, CallContext cc, AccessPathOption argAp, AccessPathFront apf, AccessPath ap, Configuration config ) { flowCand(node, _, _, _, config) and config.isSource(node) and - fromArg = false and + cc instanceof CallContextAny and argAp = TAccessPathNone() and ap = TNil(getNodeType(node)) and apf = ap.(AccessPathNil).getFront() or flowCand(node, _, _, _, unbind(config)) and ( - exists(Node mid | - flowFwd(mid, fromArg, argAp, apf, ap, config) and - localFlowBigStep(mid, node, true, _, config, _) + exists(Node mid, LocalCallContext localCC | + flowFwdLocalEntry(mid, cc, argAp, apf, ap, localCC, config) and + localFlowBigStep(mid, node, true, _, config, localCC) ) or - exists(Node mid, AccessPathNil nil | - flowFwd(mid, fromArg, argAp, _, nil, config) and - localFlowBigStep(mid, node, false, apf, config, _) and + exists(Node mid, AccessPathNil nil, LocalCallContext localCC | + flowFwdLocalEntry(mid, cc, argAp, _, nil, localCC, config) and + localFlowBigStep(mid, node, false, apf, config, localCC) and apf = ap.(AccessPathNil).getFront() ) or exists(Node mid | flowFwd(mid, _, _, apf, ap, config) and jumpStep(mid, node, config) and - fromArg = false and + cc instanceof CallContextAny and argAp = TAccessPathNone() ) or exists(Node mid, AccessPathNil nil | flowFwd(mid, _, _, _, nil, config) and additionalJumpStep(mid, node, config) and - fromArg = false and + cc instanceof CallContextAny and argAp = TAccessPathNone() and ap = TNil(getNodeType(node)) and apf = ap.(AccessPathNil).getFront() @@ -1704,40 +1704,51 @@ private predicate flowFwd0( ) or // store - exists(TypedContent tc | flowFwdStore(node, tc, pop(tc, ap), apf, fromArg, argAp, config)) + exists(TypedContent tc | flowFwdStore(node, tc, pop(tc, ap), apf, cc, argAp, config)) or // read exists(TypedContent tc | - flowFwdRead(node, _, push(tc, ap), apf, fromArg, argAp, config) and + flowFwdRead(node, _, push(tc, ap), apf, cc, argAp, config) and flowFwdConsCand(tc, apf, ap, config) ) or // flow into a callable - flowFwdIn(_, node, _, _, apf, ap, config) and - fromArg = true and + flowFwdIn(_, node, _, cc, _, apf, ap, config) and if flowCand(node, true, _, apf, config) then argAp = TAccessPathSome(ap) else argAp = TAccessPathNone() or // flow out of a callable exists(DataFlowCall call | - flowFwdOut(call, node, fromArg, argAp, apf, ap, config) and - fromArg = false + exists(DataFlowCallable c | + flowFwdOut(call, node, any(CallContextNoCall innercc), c, argAp, apf, ap, config) and + if reducedViableImplInReturn(c, call) then cc = TReturn(c, call) else cc = TAnyCallContext() + ) or exists(AccessPath argAp0 | flowFwdOutFromArg(call, node, argAp0, apf, ap, config) and - flowFwdIsEntered(call, fromArg, argAp, argAp0, config) + flowFwdIsEntered(call, cc, argAp, argAp0, config) ) ) } +pragma[nomagic] +private predicate flowFwdLocalEntry( + Node node, CallContext cc, AccessPathOption argAp, AccessPathFront apf, AccessPath ap, + LocalCallContext localCC, Configuration config +) { + flowFwd(node, cc, argAp, apf, ap, config) and + localFlowEntry(node, config) and + localCC = getLocalCallContext(cc, node.getEnclosingCallable()) +} + pragma[nomagic] private predicate flowFwdStore( - Node node, TypedContent tc, AccessPath ap0, AccessPathFront apf, boolean fromArg, + Node node, TypedContent tc, AccessPath ap0, AccessPathFront apf, CallContext cc, AccessPathOption argAp, Configuration config ) { exists(Node mid, AccessPathFront apf0 | - flowFwd(mid, fromArg, argAp, apf0, ap0, config) and + flowFwd(mid, cc, argAp, apf0, ap0, config) and flowFwdStore0(mid, tc, node, apf0, apf, config) ) } @@ -1764,20 +1775,20 @@ private predicate flowFwdStore0( pragma[nomagic] private predicate flowFwdRead0( - Node node1, TypedContent tc, AccessPathFrontHead apf0, AccessPath ap0, Node node2, - boolean fromArg, AccessPathOption argAp, Configuration config + Node node1, TypedContent tc, AccessPathFrontHead apf0, AccessPath ap0, Node node2, CallContext cc, + AccessPathOption argAp, Configuration config ) { - flowFwd(node1, fromArg, argAp, apf0, ap0, config) and + flowFwd(node1, cc, argAp, apf0, ap0, config) and readCandFwd(node1, tc, apf0, node2, config) } pragma[nomagic] private predicate flowFwdRead( - Node node, AccessPathFrontHead apf0, AccessPath ap0, AccessPathFront apf, boolean fromArg, + Node node, AccessPathFrontHead apf0, AccessPath ap0, AccessPathFront apf, CallContext cc, AccessPathOption argAp, Configuration config ) { exists(Node mid, TypedContent tc | - flowFwdRead0(mid, tc, apf0, ap0, node, fromArg, argAp, config) and + flowFwdRead0(mid, tc, apf0, ap0, node, cc, argAp, config) and flowCand(node, _, _, apf, unbind(config)) and flowCandConsCand(tc, apf, unbind(config)) ) @@ -1795,13 +1806,16 @@ private predicate flowFwdConsCand( pragma[nomagic] private predicate flowFwdIn( - DataFlowCall call, ParameterNode p, boolean fromArg, AccessPathOption argAp, AccessPathFront apf, - AccessPath ap, Configuration config + DataFlowCall call, ParameterNode p, CallContext outercc, CallContext innercc, + AccessPathOption argAp, AccessPathFront apf, AccessPath ap, Configuration config ) { - exists(ArgumentNode arg, boolean allowsFieldFlow | - flowFwd(arg, fromArg, argAp, apf, ap, config) and + exists(ArgumentNode arg, boolean allowsFieldFlow, DataFlowCallable c | + flowFwd(arg, outercc, argAp, apf, ap, config) and flowIntoCallNodeCand2(call, arg, p, allowsFieldFlow, config) and - flowCand(p, _, _, _, unbind(config)) + c = p.getEnclosingCallable() and + c = resolveCall(call, outercc) and + flowCand(p, _, _, _, unbind(config)) and + if recordDataFlowCallSite(call, c) then innercc = TSpecificCall(call) else innercc = TSomeCall() | ap instanceof AccessPathNil or allowsFieldFlow = true ) @@ -1809,13 +1823,19 @@ private predicate flowFwdIn( pragma[nomagic] private predicate flowFwdOut( - DataFlowCall call, Node node, boolean fromArg, AccessPathOption argAp, AccessPathFront apf, - AccessPath ap, Configuration config + DataFlowCall call, Node node, CallContext innercc, DataFlowCallable innerc, + AccessPathOption argAp, AccessPathFront apf, AccessPath ap, Configuration config ) { exists(ReturnNodeExt ret, boolean allowsFieldFlow | - flowFwd(ret, fromArg, argAp, apf, ap, config) and + flowFwd(ret, innercc, argAp, apf, ap, config) and flowOutOfCallNodeCand2(call, ret, node, allowsFieldFlow, config) and - flowCand(node, _, _, _, unbind(config)) + innerc = ret.getEnclosingCallable() and + flowCand(node, _, _, _, unbind(config)) and + ( + resolveReturn(innercc, innerc, call) + or + innercc.(CallContextCall).matchesCall(call) + ) | ap instanceof AccessPathNil or allowsFieldFlow = true ) @@ -1826,7 +1846,7 @@ private predicate flowFwdOutFromArg( DataFlowCall call, Node node, AccessPath argAp, AccessPathFront apf, AccessPath ap, Configuration config ) { - flowFwdOut(call, node, true, TAccessPathSome(argAp), apf, ap, config) + flowFwdOut(call, node, any(CallContextCall ccc), _, TAccessPathSome(argAp), apf, ap, config) } /** @@ -1834,10 +1854,10 @@ private predicate flowFwdOutFromArg( */ pragma[nomagic] private predicate flowFwdIsEntered( - DataFlowCall call, boolean fromArg, AccessPathOption argAp, AccessPath ap, Configuration config + DataFlowCall call, CallContext cc, AccessPathOption argAp, AccessPath ap, Configuration config ) { exists(ParameterNode p, AccessPathFront apf | - flowFwdIn(call, p, fromArg, argAp, apf, ap, config) and + flowFwdIn(call, p, cc, _, argAp, apf, ap, config) and flowCand(p, true, TAccessPathFrontSome(_), apf, config) ) } @@ -1920,7 +1940,7 @@ private predicate flow0( // flow out of a callable flowOut(_, node, _, _, ap, config) and toReturn = true and - if flowFwd(node, true, TAccessPathSome(_), _, ap, config) + if flowFwd(node, any(CallContextCall ccc), TAccessPathSome(_), _, ap, config) then returnAp = TAccessPathSome(ap) else returnAp = TAccessPathNone() } @@ -2006,9 +2026,10 @@ private predicate flowIsReturned( DataFlowCall call, boolean toReturn, AccessPathOption returnAp, AccessPath ap, Configuration config ) { - exists(ReturnNodeExt ret | + exists(ReturnNodeExt ret, CallContextCall ccc | flowOut(call, ret, toReturn, returnAp, ap, config) and - flowFwd(ret, true, TAccessPathSome(_), _, ap, config) + flowFwd(ret, ccc, TAccessPathSome(_), _, ap, config) and + ccc.matchesCall(call) ) } @@ -2031,7 +2052,7 @@ private newtype TSummaryCtx = exists(ReturnNodeExt ret, Configuration config, AccessPath ap0 | parameterFlow(p, ap, ret.getEnclosingCallable(), config) and flow(ret, true, TAccessPathSome(_), ap0, config) and - flowFwd(ret, true, TAccessPathSome(ap), _, ap0, config) + flowFwd(ret, any(CallContextCall ccc), TAccessPathSome(ap), _, ap0, config) ) } @@ -2352,7 +2373,7 @@ private predicate pathOutOfCallable0( ) { pos = getReturnPosition(mid.getNode()) and innercc = mid.getCallContext() and - not innercc instanceof CallContextCall and + innercc instanceof CallContextNoCall and ap = mid.getAp() and config = mid.getConfiguration() } @@ -2867,7 +2888,7 @@ private module FlowExploration { ) { pos = getReturnPosition(mid.getNode()) and innercc = mid.getCallContext() and - not innercc instanceof CallContextCall and + innercc instanceof CallContextNoCall and ap = mid.getAp() and config = mid.getConfiguration() } diff --git a/java/ql/src/semmle/code/java/dataflow/internal/DataFlowImpl4.qll b/java/ql/src/semmle/code/java/dataflow/internal/DataFlowImpl4.qll index 5042dce683f..8c210edbe5f 100644 --- a/java/ql/src/semmle/code/java/dataflow/internal/DataFlowImpl4.qll +++ b/java/ql/src/semmle/code/java/dataflow/internal/DataFlowImpl4.qll @@ -1066,7 +1066,7 @@ private module LocalFlowBigStep { * Holds if `node` can be the first node in a maximal subsequence of local * flow steps in a dataflow path. */ - private predicate localFlowEntry(Node node, Configuration config) { + predicate localFlowEntry(Node node, Configuration config) { nodeCand2(node, config) and ( config.isSource(node) or @@ -1650,53 +1650,53 @@ private class AccessPathOption extends TAccessPathOption { * Holds if `node` is reachable with access path `ap` from a source in * the configuration `config`. * - * The Boolean `fromArg` records whether the node is reached through an + * 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. */ private predicate flowFwd( - Node node, boolean fromArg, AccessPathOption argAp, AccessPathFront apf, AccessPath ap, + Node node, CallContext cc, AccessPathOption argAp, AccessPathFront apf, AccessPath ap, Configuration config ) { - flowFwd0(node, fromArg, argAp, apf, ap, config) and + flowFwd0(node, cc, argAp, apf, ap, config) and flowCand(node, _, _, apf, config) } private predicate flowFwd0( - Node node, boolean fromArg, AccessPathOption argAp, AccessPathFront apf, AccessPath ap, + Node node, CallContext cc, AccessPathOption argAp, AccessPathFront apf, AccessPath ap, Configuration config ) { flowCand(node, _, _, _, config) and config.isSource(node) and - fromArg = false and + cc instanceof CallContextAny and argAp = TAccessPathNone() and ap = TNil(getNodeType(node)) and apf = ap.(AccessPathNil).getFront() or flowCand(node, _, _, _, unbind(config)) and ( - exists(Node mid | - flowFwd(mid, fromArg, argAp, apf, ap, config) and - localFlowBigStep(mid, node, true, _, config, _) + exists(Node mid, LocalCallContext localCC | + flowFwdLocalEntry(mid, cc, argAp, apf, ap, localCC, config) and + localFlowBigStep(mid, node, true, _, config, localCC) ) or - exists(Node mid, AccessPathNil nil | - flowFwd(mid, fromArg, argAp, _, nil, config) and - localFlowBigStep(mid, node, false, apf, config, _) and + exists(Node mid, AccessPathNil nil, LocalCallContext localCC | + flowFwdLocalEntry(mid, cc, argAp, _, nil, localCC, config) and + localFlowBigStep(mid, node, false, apf, config, localCC) and apf = ap.(AccessPathNil).getFront() ) or exists(Node mid | flowFwd(mid, _, _, apf, ap, config) and jumpStep(mid, node, config) and - fromArg = false and + cc instanceof CallContextAny and argAp = TAccessPathNone() ) or exists(Node mid, AccessPathNil nil | flowFwd(mid, _, _, _, nil, config) and additionalJumpStep(mid, node, config) and - fromArg = false and + cc instanceof CallContextAny and argAp = TAccessPathNone() and ap = TNil(getNodeType(node)) and apf = ap.(AccessPathNil).getFront() @@ -1704,40 +1704,51 @@ private predicate flowFwd0( ) or // store - exists(TypedContent tc | flowFwdStore(node, tc, pop(tc, ap), apf, fromArg, argAp, config)) + exists(TypedContent tc | flowFwdStore(node, tc, pop(tc, ap), apf, cc, argAp, config)) or // read exists(TypedContent tc | - flowFwdRead(node, _, push(tc, ap), apf, fromArg, argAp, config) and + flowFwdRead(node, _, push(tc, ap), apf, cc, argAp, config) and flowFwdConsCand(tc, apf, ap, config) ) or // flow into a callable - flowFwdIn(_, node, _, _, apf, ap, config) and - fromArg = true and + flowFwdIn(_, node, _, cc, _, apf, ap, config) and if flowCand(node, true, _, apf, config) then argAp = TAccessPathSome(ap) else argAp = TAccessPathNone() or // flow out of a callable exists(DataFlowCall call | - flowFwdOut(call, node, fromArg, argAp, apf, ap, config) and - fromArg = false + exists(DataFlowCallable c | + flowFwdOut(call, node, any(CallContextNoCall innercc), c, argAp, apf, ap, config) and + if reducedViableImplInReturn(c, call) then cc = TReturn(c, call) else cc = TAnyCallContext() + ) or exists(AccessPath argAp0 | flowFwdOutFromArg(call, node, argAp0, apf, ap, config) and - flowFwdIsEntered(call, fromArg, argAp, argAp0, config) + flowFwdIsEntered(call, cc, argAp, argAp0, config) ) ) } +pragma[nomagic] +private predicate flowFwdLocalEntry( + Node node, CallContext cc, AccessPathOption argAp, AccessPathFront apf, AccessPath ap, + LocalCallContext localCC, Configuration config +) { + flowFwd(node, cc, argAp, apf, ap, config) and + localFlowEntry(node, config) and + localCC = getLocalCallContext(cc, node.getEnclosingCallable()) +} + pragma[nomagic] private predicate flowFwdStore( - Node node, TypedContent tc, AccessPath ap0, AccessPathFront apf, boolean fromArg, + Node node, TypedContent tc, AccessPath ap0, AccessPathFront apf, CallContext cc, AccessPathOption argAp, Configuration config ) { exists(Node mid, AccessPathFront apf0 | - flowFwd(mid, fromArg, argAp, apf0, ap0, config) and + flowFwd(mid, cc, argAp, apf0, ap0, config) and flowFwdStore0(mid, tc, node, apf0, apf, config) ) } @@ -1764,20 +1775,20 @@ private predicate flowFwdStore0( pragma[nomagic] private predicate flowFwdRead0( - Node node1, TypedContent tc, AccessPathFrontHead apf0, AccessPath ap0, Node node2, - boolean fromArg, AccessPathOption argAp, Configuration config + Node node1, TypedContent tc, AccessPathFrontHead apf0, AccessPath ap0, Node node2, CallContext cc, + AccessPathOption argAp, Configuration config ) { - flowFwd(node1, fromArg, argAp, apf0, ap0, config) and + flowFwd(node1, cc, argAp, apf0, ap0, config) and readCandFwd(node1, tc, apf0, node2, config) } pragma[nomagic] private predicate flowFwdRead( - Node node, AccessPathFrontHead apf0, AccessPath ap0, AccessPathFront apf, boolean fromArg, + Node node, AccessPathFrontHead apf0, AccessPath ap0, AccessPathFront apf, CallContext cc, AccessPathOption argAp, Configuration config ) { exists(Node mid, TypedContent tc | - flowFwdRead0(mid, tc, apf0, ap0, node, fromArg, argAp, config) and + flowFwdRead0(mid, tc, apf0, ap0, node, cc, argAp, config) and flowCand(node, _, _, apf, unbind(config)) and flowCandConsCand(tc, apf, unbind(config)) ) @@ -1795,13 +1806,16 @@ private predicate flowFwdConsCand( pragma[nomagic] private predicate flowFwdIn( - DataFlowCall call, ParameterNode p, boolean fromArg, AccessPathOption argAp, AccessPathFront apf, - AccessPath ap, Configuration config + DataFlowCall call, ParameterNode p, CallContext outercc, CallContext innercc, + AccessPathOption argAp, AccessPathFront apf, AccessPath ap, Configuration config ) { - exists(ArgumentNode arg, boolean allowsFieldFlow | - flowFwd(arg, fromArg, argAp, apf, ap, config) and + exists(ArgumentNode arg, boolean allowsFieldFlow, DataFlowCallable c | + flowFwd(arg, outercc, argAp, apf, ap, config) and flowIntoCallNodeCand2(call, arg, p, allowsFieldFlow, config) and - flowCand(p, _, _, _, unbind(config)) + c = p.getEnclosingCallable() and + c = resolveCall(call, outercc) and + flowCand(p, _, _, _, unbind(config)) and + if recordDataFlowCallSite(call, c) then innercc = TSpecificCall(call) else innercc = TSomeCall() | ap instanceof AccessPathNil or allowsFieldFlow = true ) @@ -1809,13 +1823,19 @@ private predicate flowFwdIn( pragma[nomagic] private predicate flowFwdOut( - DataFlowCall call, Node node, boolean fromArg, AccessPathOption argAp, AccessPathFront apf, - AccessPath ap, Configuration config + DataFlowCall call, Node node, CallContext innercc, DataFlowCallable innerc, + AccessPathOption argAp, AccessPathFront apf, AccessPath ap, Configuration config ) { exists(ReturnNodeExt ret, boolean allowsFieldFlow | - flowFwd(ret, fromArg, argAp, apf, ap, config) and + flowFwd(ret, innercc, argAp, apf, ap, config) and flowOutOfCallNodeCand2(call, ret, node, allowsFieldFlow, config) and - flowCand(node, _, _, _, unbind(config)) + innerc = ret.getEnclosingCallable() and + flowCand(node, _, _, _, unbind(config)) and + ( + resolveReturn(innercc, innerc, call) + or + innercc.(CallContextCall).matchesCall(call) + ) | ap instanceof AccessPathNil or allowsFieldFlow = true ) @@ -1826,7 +1846,7 @@ private predicate flowFwdOutFromArg( DataFlowCall call, Node node, AccessPath argAp, AccessPathFront apf, AccessPath ap, Configuration config ) { - flowFwdOut(call, node, true, TAccessPathSome(argAp), apf, ap, config) + flowFwdOut(call, node, any(CallContextCall ccc), _, TAccessPathSome(argAp), apf, ap, config) } /** @@ -1834,10 +1854,10 @@ private predicate flowFwdOutFromArg( */ pragma[nomagic] private predicate flowFwdIsEntered( - DataFlowCall call, boolean fromArg, AccessPathOption argAp, AccessPath ap, Configuration config + DataFlowCall call, CallContext cc, AccessPathOption argAp, AccessPath ap, Configuration config ) { exists(ParameterNode p, AccessPathFront apf | - flowFwdIn(call, p, fromArg, argAp, apf, ap, config) and + flowFwdIn(call, p, cc, _, argAp, apf, ap, config) and flowCand(p, true, TAccessPathFrontSome(_), apf, config) ) } @@ -1920,7 +1940,7 @@ private predicate flow0( // flow out of a callable flowOut(_, node, _, _, ap, config) and toReturn = true and - if flowFwd(node, true, TAccessPathSome(_), _, ap, config) + if flowFwd(node, any(CallContextCall ccc), TAccessPathSome(_), _, ap, config) then returnAp = TAccessPathSome(ap) else returnAp = TAccessPathNone() } @@ -2006,9 +2026,10 @@ private predicate flowIsReturned( DataFlowCall call, boolean toReturn, AccessPathOption returnAp, AccessPath ap, Configuration config ) { - exists(ReturnNodeExt ret | + exists(ReturnNodeExt ret, CallContextCall ccc | flowOut(call, ret, toReturn, returnAp, ap, config) and - flowFwd(ret, true, TAccessPathSome(_), _, ap, config) + flowFwd(ret, ccc, TAccessPathSome(_), _, ap, config) and + ccc.matchesCall(call) ) } @@ -2031,7 +2052,7 @@ private newtype TSummaryCtx = exists(ReturnNodeExt ret, Configuration config, AccessPath ap0 | parameterFlow(p, ap, ret.getEnclosingCallable(), config) and flow(ret, true, TAccessPathSome(_), ap0, config) and - flowFwd(ret, true, TAccessPathSome(ap), _, ap0, config) + flowFwd(ret, any(CallContextCall ccc), TAccessPathSome(ap), _, ap0, config) ) } @@ -2352,7 +2373,7 @@ private predicate pathOutOfCallable0( ) { pos = getReturnPosition(mid.getNode()) and innercc = mid.getCallContext() and - not innercc instanceof CallContextCall and + innercc instanceof CallContextNoCall and ap = mid.getAp() and config = mid.getConfiguration() } @@ -2867,7 +2888,7 @@ private module FlowExploration { ) { pos = getReturnPosition(mid.getNode()) and innercc = mid.getCallContext() and - not innercc instanceof CallContextCall and + innercc instanceof CallContextNoCall and ap = mid.getAp() and config = mid.getConfiguration() } diff --git a/java/ql/src/semmle/code/java/dataflow/internal/DataFlowImpl5.qll b/java/ql/src/semmle/code/java/dataflow/internal/DataFlowImpl5.qll index 5042dce683f..8c210edbe5f 100644 --- a/java/ql/src/semmle/code/java/dataflow/internal/DataFlowImpl5.qll +++ b/java/ql/src/semmle/code/java/dataflow/internal/DataFlowImpl5.qll @@ -1066,7 +1066,7 @@ private module LocalFlowBigStep { * Holds if `node` can be the first node in a maximal subsequence of local * flow steps in a dataflow path. */ - private predicate localFlowEntry(Node node, Configuration config) { + predicate localFlowEntry(Node node, Configuration config) { nodeCand2(node, config) and ( config.isSource(node) or @@ -1650,53 +1650,53 @@ private class AccessPathOption extends TAccessPathOption { * Holds if `node` is reachable with access path `ap` from a source in * the configuration `config`. * - * The Boolean `fromArg` records whether the node is reached through an + * 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. */ private predicate flowFwd( - Node node, boolean fromArg, AccessPathOption argAp, AccessPathFront apf, AccessPath ap, + Node node, CallContext cc, AccessPathOption argAp, AccessPathFront apf, AccessPath ap, Configuration config ) { - flowFwd0(node, fromArg, argAp, apf, ap, config) and + flowFwd0(node, cc, argAp, apf, ap, config) and flowCand(node, _, _, apf, config) } private predicate flowFwd0( - Node node, boolean fromArg, AccessPathOption argAp, AccessPathFront apf, AccessPath ap, + Node node, CallContext cc, AccessPathOption argAp, AccessPathFront apf, AccessPath ap, Configuration config ) { flowCand(node, _, _, _, config) and config.isSource(node) and - fromArg = false and + cc instanceof CallContextAny and argAp = TAccessPathNone() and ap = TNil(getNodeType(node)) and apf = ap.(AccessPathNil).getFront() or flowCand(node, _, _, _, unbind(config)) and ( - exists(Node mid | - flowFwd(mid, fromArg, argAp, apf, ap, config) and - localFlowBigStep(mid, node, true, _, config, _) + exists(Node mid, LocalCallContext localCC | + flowFwdLocalEntry(mid, cc, argAp, apf, ap, localCC, config) and + localFlowBigStep(mid, node, true, _, config, localCC) ) or - exists(Node mid, AccessPathNil nil | - flowFwd(mid, fromArg, argAp, _, nil, config) and - localFlowBigStep(mid, node, false, apf, config, _) and + exists(Node mid, AccessPathNil nil, LocalCallContext localCC | + flowFwdLocalEntry(mid, cc, argAp, _, nil, localCC, config) and + localFlowBigStep(mid, node, false, apf, config, localCC) and apf = ap.(AccessPathNil).getFront() ) or exists(Node mid | flowFwd(mid, _, _, apf, ap, config) and jumpStep(mid, node, config) and - fromArg = false and + cc instanceof CallContextAny and argAp = TAccessPathNone() ) or exists(Node mid, AccessPathNil nil | flowFwd(mid, _, _, _, nil, config) and additionalJumpStep(mid, node, config) and - fromArg = false and + cc instanceof CallContextAny and argAp = TAccessPathNone() and ap = TNil(getNodeType(node)) and apf = ap.(AccessPathNil).getFront() @@ -1704,40 +1704,51 @@ private predicate flowFwd0( ) or // store - exists(TypedContent tc | flowFwdStore(node, tc, pop(tc, ap), apf, fromArg, argAp, config)) + exists(TypedContent tc | flowFwdStore(node, tc, pop(tc, ap), apf, cc, argAp, config)) or // read exists(TypedContent tc | - flowFwdRead(node, _, push(tc, ap), apf, fromArg, argAp, config) and + flowFwdRead(node, _, push(tc, ap), apf, cc, argAp, config) and flowFwdConsCand(tc, apf, ap, config) ) or // flow into a callable - flowFwdIn(_, node, _, _, apf, ap, config) and - fromArg = true and + flowFwdIn(_, node, _, cc, _, apf, ap, config) and if flowCand(node, true, _, apf, config) then argAp = TAccessPathSome(ap) else argAp = TAccessPathNone() or // flow out of a callable exists(DataFlowCall call | - flowFwdOut(call, node, fromArg, argAp, apf, ap, config) and - fromArg = false + exists(DataFlowCallable c | + flowFwdOut(call, node, any(CallContextNoCall innercc), c, argAp, apf, ap, config) and + if reducedViableImplInReturn(c, call) then cc = TReturn(c, call) else cc = TAnyCallContext() + ) or exists(AccessPath argAp0 | flowFwdOutFromArg(call, node, argAp0, apf, ap, config) and - flowFwdIsEntered(call, fromArg, argAp, argAp0, config) + flowFwdIsEntered(call, cc, argAp, argAp0, config) ) ) } +pragma[nomagic] +private predicate flowFwdLocalEntry( + Node node, CallContext cc, AccessPathOption argAp, AccessPathFront apf, AccessPath ap, + LocalCallContext localCC, Configuration config +) { + flowFwd(node, cc, argAp, apf, ap, config) and + localFlowEntry(node, config) and + localCC = getLocalCallContext(cc, node.getEnclosingCallable()) +} + pragma[nomagic] private predicate flowFwdStore( - Node node, TypedContent tc, AccessPath ap0, AccessPathFront apf, boolean fromArg, + Node node, TypedContent tc, AccessPath ap0, AccessPathFront apf, CallContext cc, AccessPathOption argAp, Configuration config ) { exists(Node mid, AccessPathFront apf0 | - flowFwd(mid, fromArg, argAp, apf0, ap0, config) and + flowFwd(mid, cc, argAp, apf0, ap0, config) and flowFwdStore0(mid, tc, node, apf0, apf, config) ) } @@ -1764,20 +1775,20 @@ private predicate flowFwdStore0( pragma[nomagic] private predicate flowFwdRead0( - Node node1, TypedContent tc, AccessPathFrontHead apf0, AccessPath ap0, Node node2, - boolean fromArg, AccessPathOption argAp, Configuration config + Node node1, TypedContent tc, AccessPathFrontHead apf0, AccessPath ap0, Node node2, CallContext cc, + AccessPathOption argAp, Configuration config ) { - flowFwd(node1, fromArg, argAp, apf0, ap0, config) and + flowFwd(node1, cc, argAp, apf0, ap0, config) and readCandFwd(node1, tc, apf0, node2, config) } pragma[nomagic] private predicate flowFwdRead( - Node node, AccessPathFrontHead apf0, AccessPath ap0, AccessPathFront apf, boolean fromArg, + Node node, AccessPathFrontHead apf0, AccessPath ap0, AccessPathFront apf, CallContext cc, AccessPathOption argAp, Configuration config ) { exists(Node mid, TypedContent tc | - flowFwdRead0(mid, tc, apf0, ap0, node, fromArg, argAp, config) and + flowFwdRead0(mid, tc, apf0, ap0, node, cc, argAp, config) and flowCand(node, _, _, apf, unbind(config)) and flowCandConsCand(tc, apf, unbind(config)) ) @@ -1795,13 +1806,16 @@ private predicate flowFwdConsCand( pragma[nomagic] private predicate flowFwdIn( - DataFlowCall call, ParameterNode p, boolean fromArg, AccessPathOption argAp, AccessPathFront apf, - AccessPath ap, Configuration config + DataFlowCall call, ParameterNode p, CallContext outercc, CallContext innercc, + AccessPathOption argAp, AccessPathFront apf, AccessPath ap, Configuration config ) { - exists(ArgumentNode arg, boolean allowsFieldFlow | - flowFwd(arg, fromArg, argAp, apf, ap, config) and + exists(ArgumentNode arg, boolean allowsFieldFlow, DataFlowCallable c | + flowFwd(arg, outercc, argAp, apf, ap, config) and flowIntoCallNodeCand2(call, arg, p, allowsFieldFlow, config) and - flowCand(p, _, _, _, unbind(config)) + c = p.getEnclosingCallable() and + c = resolveCall(call, outercc) and + flowCand(p, _, _, _, unbind(config)) and + if recordDataFlowCallSite(call, c) then innercc = TSpecificCall(call) else innercc = TSomeCall() | ap instanceof AccessPathNil or allowsFieldFlow = true ) @@ -1809,13 +1823,19 @@ private predicate flowFwdIn( pragma[nomagic] private predicate flowFwdOut( - DataFlowCall call, Node node, boolean fromArg, AccessPathOption argAp, AccessPathFront apf, - AccessPath ap, Configuration config + DataFlowCall call, Node node, CallContext innercc, DataFlowCallable innerc, + AccessPathOption argAp, AccessPathFront apf, AccessPath ap, Configuration config ) { exists(ReturnNodeExt ret, boolean allowsFieldFlow | - flowFwd(ret, fromArg, argAp, apf, ap, config) and + flowFwd(ret, innercc, argAp, apf, ap, config) and flowOutOfCallNodeCand2(call, ret, node, allowsFieldFlow, config) and - flowCand(node, _, _, _, unbind(config)) + innerc = ret.getEnclosingCallable() and + flowCand(node, _, _, _, unbind(config)) and + ( + resolveReturn(innercc, innerc, call) + or + innercc.(CallContextCall).matchesCall(call) + ) | ap instanceof AccessPathNil or allowsFieldFlow = true ) @@ -1826,7 +1846,7 @@ private predicate flowFwdOutFromArg( DataFlowCall call, Node node, AccessPath argAp, AccessPathFront apf, AccessPath ap, Configuration config ) { - flowFwdOut(call, node, true, TAccessPathSome(argAp), apf, ap, config) + flowFwdOut(call, node, any(CallContextCall ccc), _, TAccessPathSome(argAp), apf, ap, config) } /** @@ -1834,10 +1854,10 @@ private predicate flowFwdOutFromArg( */ pragma[nomagic] private predicate flowFwdIsEntered( - DataFlowCall call, boolean fromArg, AccessPathOption argAp, AccessPath ap, Configuration config + DataFlowCall call, CallContext cc, AccessPathOption argAp, AccessPath ap, Configuration config ) { exists(ParameterNode p, AccessPathFront apf | - flowFwdIn(call, p, fromArg, argAp, apf, ap, config) and + flowFwdIn(call, p, cc, _, argAp, apf, ap, config) and flowCand(p, true, TAccessPathFrontSome(_), apf, config) ) } @@ -1920,7 +1940,7 @@ private predicate flow0( // flow out of a callable flowOut(_, node, _, _, ap, config) and toReturn = true and - if flowFwd(node, true, TAccessPathSome(_), _, ap, config) + if flowFwd(node, any(CallContextCall ccc), TAccessPathSome(_), _, ap, config) then returnAp = TAccessPathSome(ap) else returnAp = TAccessPathNone() } @@ -2006,9 +2026,10 @@ private predicate flowIsReturned( DataFlowCall call, boolean toReturn, AccessPathOption returnAp, AccessPath ap, Configuration config ) { - exists(ReturnNodeExt ret | + exists(ReturnNodeExt ret, CallContextCall ccc | flowOut(call, ret, toReturn, returnAp, ap, config) and - flowFwd(ret, true, TAccessPathSome(_), _, ap, config) + flowFwd(ret, ccc, TAccessPathSome(_), _, ap, config) and + ccc.matchesCall(call) ) } @@ -2031,7 +2052,7 @@ private newtype TSummaryCtx = exists(ReturnNodeExt ret, Configuration config, AccessPath ap0 | parameterFlow(p, ap, ret.getEnclosingCallable(), config) and flow(ret, true, TAccessPathSome(_), ap0, config) and - flowFwd(ret, true, TAccessPathSome(ap), _, ap0, config) + flowFwd(ret, any(CallContextCall ccc), TAccessPathSome(ap), _, ap0, config) ) } @@ -2352,7 +2373,7 @@ private predicate pathOutOfCallable0( ) { pos = getReturnPosition(mid.getNode()) and innercc = mid.getCallContext() and - not innercc instanceof CallContextCall and + innercc instanceof CallContextNoCall and ap = mid.getAp() and config = mid.getConfiguration() } @@ -2867,7 +2888,7 @@ private module FlowExploration { ) { pos = getReturnPosition(mid.getNode()) and innercc = mid.getCallContext() and - not innercc instanceof CallContextCall and + innercc instanceof CallContextNoCall and ap = mid.getAp() and config = mid.getConfiguration() } diff --git a/java/ql/src/semmle/code/java/dataflow/internal/DataFlowImplCommon.qll b/java/ql/src/semmle/code/java/dataflow/internal/DataFlowImplCommon.qll index 27ab1d01feb..892250f44bb 100644 --- a/java/ql/src/semmle/code/java/dataflow/internal/DataFlowImplCommon.qll +++ b/java/ql/src/semmle/code/java/dataflow/internal/DataFlowImplCommon.qll @@ -512,13 +512,19 @@ abstract class CallContext extends TCallContext { abstract predicate relevantFor(DataFlowCallable callable); } -class CallContextAny extends CallContext, TAnyCallContext { +abstract class CallContextNoCall extends CallContext { } + +class CallContextAny extends CallContextNoCall, TAnyCallContext { override string toString() { result = "CcAny" } override predicate relevantFor(DataFlowCallable callable) { any() } } -abstract class CallContextCall extends CallContext { } +abstract class CallContextCall extends CallContext { + /** Holds if this call context may be `call`. */ + bindingset[call] + abstract predicate matchesCall(DataFlowCall call); +} class CallContextSpecificCall extends CallContextCall, TSpecificCall { override string toString() { @@ -529,6 +535,8 @@ class CallContextSpecificCall extends CallContextCall, TSpecificCall { recordDataFlowCallSite(getCall(), callable) } + override predicate matchesCall(DataFlowCall call) { call = this.getCall() } + DataFlowCall getCall() { this = TSpecificCall(result) } } @@ -538,9 +546,11 @@ class CallContextSomeCall extends CallContextCall, TSomeCall { override predicate relevantFor(DataFlowCallable callable) { exists(ParameterNode p | p.getEnclosingCallable() = callable) } + + override predicate matchesCall(DataFlowCall call) { any() } } -class CallContextReturn extends CallContext, TReturn { +class CallContextReturn extends CallContextNoCall, TReturn { override string toString() { exists(DataFlowCall call | this = TReturn(_, call) | result = "CcReturn(" + call + ")") } diff --git a/java/ql/test/experimental/query-tests/security/CWE-074/JndiInjection.expected b/java/ql/test/experimental/query-tests/security/CWE-074-JndiInjection/JndiInjection.expected similarity index 100% rename from java/ql/test/experimental/query-tests/security/CWE-074/JndiInjection.expected rename to java/ql/test/experimental/query-tests/security/CWE-074-JndiInjection/JndiInjection.expected diff --git a/java/ql/test/experimental/query-tests/security/CWE-074/JndiInjection.java b/java/ql/test/experimental/query-tests/security/CWE-074-JndiInjection/JndiInjection.java similarity index 100% rename from java/ql/test/experimental/query-tests/security/CWE-074/JndiInjection.java rename to java/ql/test/experimental/query-tests/security/CWE-074-JndiInjection/JndiInjection.java diff --git a/java/ql/test/experimental/query-tests/security/CWE-074/JndiInjection.qlref b/java/ql/test/experimental/query-tests/security/CWE-074-JndiInjection/JndiInjection.qlref similarity index 100% rename from java/ql/test/experimental/query-tests/security/CWE-074/JndiInjection.qlref rename to java/ql/test/experimental/query-tests/security/CWE-074-JndiInjection/JndiInjection.qlref diff --git a/java/ql/test/experimental/query-tests/security/CWE-074/options b/java/ql/test/experimental/query-tests/security/CWE-074-JndiInjection/options similarity index 100% rename from java/ql/test/experimental/query-tests/security/CWE-074/options rename to java/ql/test/experimental/query-tests/security/CWE-074-JndiInjection/options diff --git a/javascript/ql/src/Security/CWE-020/IncompleteUrlSubstringSanitization.ql b/javascript/ql/src/Security/CWE-020/IncompleteUrlSubstringSanitization.ql index 14115853640..09aad31c96d 100644 --- a/javascript/ql/src/Security/CWE-020/IncompleteUrlSubstringSanitization.ql +++ b/javascript/ql/src/Security/CWE-020/IncompleteUrlSubstringSanitization.ql @@ -43,6 +43,10 @@ where or // target is a HTTP URL to a domain on any TLD target.regexpMatch("(?i)https?://([a-z0-9-]+\\.)+([a-z]+)(:[0-9]+)?/?") + or + // target is a HTTP URL to a domain on any TLD with path elements, and the check is an includes check + check instanceof StringOps::Includes and + target.regexpMatch("(?i)https?://([a-z0-9-]+\\.)+([a-z]+)(:[0-9]+)?/[a-z0-9/_-]+") ) and ( if check instanceof StringOps::StartsWith diff --git a/javascript/ql/test/query-tests/Security/CWE-020/IncompleteUrlSubstringSanitization.expected b/javascript/ql/test/query-tests/Security/CWE-020/IncompleteUrlSubstringSanitization.expected index efbaad5a672..fa1d5872ecb 100644 --- a/javascript/ql/test/query-tests/Security/CWE-020/IncompleteUrlSubstringSanitization.expected +++ b/javascript/ql/test/query-tests/Security/CWE-020/IncompleteUrlSubstringSanitization.expected @@ -20,3 +20,6 @@ | tst-IncompleteUrlSubstringSanitization.js:63:4:63:33 | x.index ... !== -1 | '$@' can be anywhere in the URL, and arbitrary hosts may come before or after it. | tst-IncompleteUrlSubstringSanitization.js:63:14:63:25 | "secure.com" | secure.com | | tst-IncompleteUrlSubstringSanitization.js:64:3:64:26 | x.inclu ... e.com") | '$@' can be anywhere in the URL, and arbitrary hosts may come before or after it. | tst-IncompleteUrlSubstringSanitization.js:64:14:64:25 | "secure.com" | secure.com | | tst-IncompleteUrlSubstringSanitization.js:66:6:66:29 | x.inclu ... e.com") | '$@' can be anywhere in the URL, and arbitrary hosts may come before or after it. | tst-IncompleteUrlSubstringSanitization.js:66:17:66:28 | "secure.com" | secure.com | +| tst-IncompleteUrlSubstringSanitization.js:73:5:73:48 | x.index ... ") >= 0 | '$@' can be anywhere in the URL, and arbitrary hosts may come before or after it. | tst-IncompleteUrlSubstringSanitization.js:73:15:73:42 | "https: ... oo/bar" | https://secure.com/foo/bar | +| tst-IncompleteUrlSubstringSanitization.js:74:5:74:40 | x.index ... ") >= 0 | '$@' can be anywhere in the URL, and arbitrary hosts may come before or after it. | tst-IncompleteUrlSubstringSanitization.js:74:15:74:34 | "https://secure.com" | https://secure.com | +| tst-IncompleteUrlSubstringSanitization.js:75:5:75:52 | x.index ... ") >= 0 | '$@' can be anywhere in the URL, and arbitrary hosts may come before or after it. | tst-IncompleteUrlSubstringSanitization.js:75:15:75:46 | "https: ... ar-baz" | https://secure.com/foo/bar-baz | diff --git a/javascript/ql/test/query-tests/Security/CWE-020/tst-IncompleteUrlSubstringSanitization.js b/javascript/ql/test/query-tests/Security/CWE-020/tst-IncompleteUrlSubstringSanitization.js index f7246c2a401..efbaaff1986 100644 --- a/javascript/ql/test/query-tests/Security/CWE-020/tst-IncompleteUrlSubstringSanitization.js +++ b/javascript/ql/test/query-tests/Security/CWE-020/tst-IncompleteUrlSubstringSanitization.js @@ -67,5 +67,10 @@ } else { doSomeThingWithTrustedURL(x); - } + } + + x.startsWith("https://secure.com/foo/bar"); // OK - a forward slash after the domain makes prefix checks safe. + x.indexOf("https://secure.com/foo/bar") >= 0 // NOT OK - the url can be anywhere in the string. + x.indexOf("https://secure.com") >= 0 // NOT OK + x.indexOf("https://secure.com/foo/bar-baz") >= 0 // NOT OK - the url can be anywhere in the string. }); diff --git a/python/ql/src/Imports/UnintentionalImport.ql b/python/ql/src/Imports/UnintentionalImport.ql index dfd751fd527..2a3c2b6460b 100644 --- a/python/ql/src/Imports/UnintentionalImport.ql +++ b/python/ql/src/Imports/UnintentionalImport.ql @@ -26,7 +26,7 @@ predicate all_defined(ModuleValue exporter) { } from ImportStar imp, ModuleValue exporter -where import_star(imp, exporter) and not all_defined(exporter) +where import_star(imp, exporter) and not all_defined(exporter) and not exporter.isAbsent() select imp, "Import pollutes the enclosing namespace, as the imported module $@ does not define '__all__'.", exporter, exporter.getName() diff --git a/python/ql/src/experimental/dataflow/internal/DataFlowImpl.qll b/python/ql/src/experimental/dataflow/internal/DataFlowImpl.qll index 5042dce683f..8c210edbe5f 100644 --- a/python/ql/src/experimental/dataflow/internal/DataFlowImpl.qll +++ b/python/ql/src/experimental/dataflow/internal/DataFlowImpl.qll @@ -1066,7 +1066,7 @@ private module LocalFlowBigStep { * Holds if `node` can be the first node in a maximal subsequence of local * flow steps in a dataflow path. */ - private predicate localFlowEntry(Node node, Configuration config) { + predicate localFlowEntry(Node node, Configuration config) { nodeCand2(node, config) and ( config.isSource(node) or @@ -1650,53 +1650,53 @@ private class AccessPathOption extends TAccessPathOption { * Holds if `node` is reachable with access path `ap` from a source in * the configuration `config`. * - * The Boolean `fromArg` records whether the node is reached through an + * 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. */ private predicate flowFwd( - Node node, boolean fromArg, AccessPathOption argAp, AccessPathFront apf, AccessPath ap, + Node node, CallContext cc, AccessPathOption argAp, AccessPathFront apf, AccessPath ap, Configuration config ) { - flowFwd0(node, fromArg, argAp, apf, ap, config) and + flowFwd0(node, cc, argAp, apf, ap, config) and flowCand(node, _, _, apf, config) } private predicate flowFwd0( - Node node, boolean fromArg, AccessPathOption argAp, AccessPathFront apf, AccessPath ap, + Node node, CallContext cc, AccessPathOption argAp, AccessPathFront apf, AccessPath ap, Configuration config ) { flowCand(node, _, _, _, config) and config.isSource(node) and - fromArg = false and + cc instanceof CallContextAny and argAp = TAccessPathNone() and ap = TNil(getNodeType(node)) and apf = ap.(AccessPathNil).getFront() or flowCand(node, _, _, _, unbind(config)) and ( - exists(Node mid | - flowFwd(mid, fromArg, argAp, apf, ap, config) and - localFlowBigStep(mid, node, true, _, config, _) + exists(Node mid, LocalCallContext localCC | + flowFwdLocalEntry(mid, cc, argAp, apf, ap, localCC, config) and + localFlowBigStep(mid, node, true, _, config, localCC) ) or - exists(Node mid, AccessPathNil nil | - flowFwd(mid, fromArg, argAp, _, nil, config) and - localFlowBigStep(mid, node, false, apf, config, _) and + exists(Node mid, AccessPathNil nil, LocalCallContext localCC | + flowFwdLocalEntry(mid, cc, argAp, _, nil, localCC, config) and + localFlowBigStep(mid, node, false, apf, config, localCC) and apf = ap.(AccessPathNil).getFront() ) or exists(Node mid | flowFwd(mid, _, _, apf, ap, config) and jumpStep(mid, node, config) and - fromArg = false and + cc instanceof CallContextAny and argAp = TAccessPathNone() ) or exists(Node mid, AccessPathNil nil | flowFwd(mid, _, _, _, nil, config) and additionalJumpStep(mid, node, config) and - fromArg = false and + cc instanceof CallContextAny and argAp = TAccessPathNone() and ap = TNil(getNodeType(node)) and apf = ap.(AccessPathNil).getFront() @@ -1704,40 +1704,51 @@ private predicate flowFwd0( ) or // store - exists(TypedContent tc | flowFwdStore(node, tc, pop(tc, ap), apf, fromArg, argAp, config)) + exists(TypedContent tc | flowFwdStore(node, tc, pop(tc, ap), apf, cc, argAp, config)) or // read exists(TypedContent tc | - flowFwdRead(node, _, push(tc, ap), apf, fromArg, argAp, config) and + flowFwdRead(node, _, push(tc, ap), apf, cc, argAp, config) and flowFwdConsCand(tc, apf, ap, config) ) or // flow into a callable - flowFwdIn(_, node, _, _, apf, ap, config) and - fromArg = true and + flowFwdIn(_, node, _, cc, _, apf, ap, config) and if flowCand(node, true, _, apf, config) then argAp = TAccessPathSome(ap) else argAp = TAccessPathNone() or // flow out of a callable exists(DataFlowCall call | - flowFwdOut(call, node, fromArg, argAp, apf, ap, config) and - fromArg = false + exists(DataFlowCallable c | + flowFwdOut(call, node, any(CallContextNoCall innercc), c, argAp, apf, ap, config) and + if reducedViableImplInReturn(c, call) then cc = TReturn(c, call) else cc = TAnyCallContext() + ) or exists(AccessPath argAp0 | flowFwdOutFromArg(call, node, argAp0, apf, ap, config) and - flowFwdIsEntered(call, fromArg, argAp, argAp0, config) + flowFwdIsEntered(call, cc, argAp, argAp0, config) ) ) } +pragma[nomagic] +private predicate flowFwdLocalEntry( + Node node, CallContext cc, AccessPathOption argAp, AccessPathFront apf, AccessPath ap, + LocalCallContext localCC, Configuration config +) { + flowFwd(node, cc, argAp, apf, ap, config) and + localFlowEntry(node, config) and + localCC = getLocalCallContext(cc, node.getEnclosingCallable()) +} + pragma[nomagic] private predicate flowFwdStore( - Node node, TypedContent tc, AccessPath ap0, AccessPathFront apf, boolean fromArg, + Node node, TypedContent tc, AccessPath ap0, AccessPathFront apf, CallContext cc, AccessPathOption argAp, Configuration config ) { exists(Node mid, AccessPathFront apf0 | - flowFwd(mid, fromArg, argAp, apf0, ap0, config) and + flowFwd(mid, cc, argAp, apf0, ap0, config) and flowFwdStore0(mid, tc, node, apf0, apf, config) ) } @@ -1764,20 +1775,20 @@ private predicate flowFwdStore0( pragma[nomagic] private predicate flowFwdRead0( - Node node1, TypedContent tc, AccessPathFrontHead apf0, AccessPath ap0, Node node2, - boolean fromArg, AccessPathOption argAp, Configuration config + Node node1, TypedContent tc, AccessPathFrontHead apf0, AccessPath ap0, Node node2, CallContext cc, + AccessPathOption argAp, Configuration config ) { - flowFwd(node1, fromArg, argAp, apf0, ap0, config) and + flowFwd(node1, cc, argAp, apf0, ap0, config) and readCandFwd(node1, tc, apf0, node2, config) } pragma[nomagic] private predicate flowFwdRead( - Node node, AccessPathFrontHead apf0, AccessPath ap0, AccessPathFront apf, boolean fromArg, + Node node, AccessPathFrontHead apf0, AccessPath ap0, AccessPathFront apf, CallContext cc, AccessPathOption argAp, Configuration config ) { exists(Node mid, TypedContent tc | - flowFwdRead0(mid, tc, apf0, ap0, node, fromArg, argAp, config) and + flowFwdRead0(mid, tc, apf0, ap0, node, cc, argAp, config) and flowCand(node, _, _, apf, unbind(config)) and flowCandConsCand(tc, apf, unbind(config)) ) @@ -1795,13 +1806,16 @@ private predicate flowFwdConsCand( pragma[nomagic] private predicate flowFwdIn( - DataFlowCall call, ParameterNode p, boolean fromArg, AccessPathOption argAp, AccessPathFront apf, - AccessPath ap, Configuration config + DataFlowCall call, ParameterNode p, CallContext outercc, CallContext innercc, + AccessPathOption argAp, AccessPathFront apf, AccessPath ap, Configuration config ) { - exists(ArgumentNode arg, boolean allowsFieldFlow | - flowFwd(arg, fromArg, argAp, apf, ap, config) and + exists(ArgumentNode arg, boolean allowsFieldFlow, DataFlowCallable c | + flowFwd(arg, outercc, argAp, apf, ap, config) and flowIntoCallNodeCand2(call, arg, p, allowsFieldFlow, config) and - flowCand(p, _, _, _, unbind(config)) + c = p.getEnclosingCallable() and + c = resolveCall(call, outercc) and + flowCand(p, _, _, _, unbind(config)) and + if recordDataFlowCallSite(call, c) then innercc = TSpecificCall(call) else innercc = TSomeCall() | ap instanceof AccessPathNil or allowsFieldFlow = true ) @@ -1809,13 +1823,19 @@ private predicate flowFwdIn( pragma[nomagic] private predicate flowFwdOut( - DataFlowCall call, Node node, boolean fromArg, AccessPathOption argAp, AccessPathFront apf, - AccessPath ap, Configuration config + DataFlowCall call, Node node, CallContext innercc, DataFlowCallable innerc, + AccessPathOption argAp, AccessPathFront apf, AccessPath ap, Configuration config ) { exists(ReturnNodeExt ret, boolean allowsFieldFlow | - flowFwd(ret, fromArg, argAp, apf, ap, config) and + flowFwd(ret, innercc, argAp, apf, ap, config) and flowOutOfCallNodeCand2(call, ret, node, allowsFieldFlow, config) and - flowCand(node, _, _, _, unbind(config)) + innerc = ret.getEnclosingCallable() and + flowCand(node, _, _, _, unbind(config)) and + ( + resolveReturn(innercc, innerc, call) + or + innercc.(CallContextCall).matchesCall(call) + ) | ap instanceof AccessPathNil or allowsFieldFlow = true ) @@ -1826,7 +1846,7 @@ private predicate flowFwdOutFromArg( DataFlowCall call, Node node, AccessPath argAp, AccessPathFront apf, AccessPath ap, Configuration config ) { - flowFwdOut(call, node, true, TAccessPathSome(argAp), apf, ap, config) + flowFwdOut(call, node, any(CallContextCall ccc), _, TAccessPathSome(argAp), apf, ap, config) } /** @@ -1834,10 +1854,10 @@ private predicate flowFwdOutFromArg( */ pragma[nomagic] private predicate flowFwdIsEntered( - DataFlowCall call, boolean fromArg, AccessPathOption argAp, AccessPath ap, Configuration config + DataFlowCall call, CallContext cc, AccessPathOption argAp, AccessPath ap, Configuration config ) { exists(ParameterNode p, AccessPathFront apf | - flowFwdIn(call, p, fromArg, argAp, apf, ap, config) and + flowFwdIn(call, p, cc, _, argAp, apf, ap, config) and flowCand(p, true, TAccessPathFrontSome(_), apf, config) ) } @@ -1920,7 +1940,7 @@ private predicate flow0( // flow out of a callable flowOut(_, node, _, _, ap, config) and toReturn = true and - if flowFwd(node, true, TAccessPathSome(_), _, ap, config) + if flowFwd(node, any(CallContextCall ccc), TAccessPathSome(_), _, ap, config) then returnAp = TAccessPathSome(ap) else returnAp = TAccessPathNone() } @@ -2006,9 +2026,10 @@ private predicate flowIsReturned( DataFlowCall call, boolean toReturn, AccessPathOption returnAp, AccessPath ap, Configuration config ) { - exists(ReturnNodeExt ret | + exists(ReturnNodeExt ret, CallContextCall ccc | flowOut(call, ret, toReturn, returnAp, ap, config) and - flowFwd(ret, true, TAccessPathSome(_), _, ap, config) + flowFwd(ret, ccc, TAccessPathSome(_), _, ap, config) and + ccc.matchesCall(call) ) } @@ -2031,7 +2052,7 @@ private newtype TSummaryCtx = exists(ReturnNodeExt ret, Configuration config, AccessPath ap0 | parameterFlow(p, ap, ret.getEnclosingCallable(), config) and flow(ret, true, TAccessPathSome(_), ap0, config) and - flowFwd(ret, true, TAccessPathSome(ap), _, ap0, config) + flowFwd(ret, any(CallContextCall ccc), TAccessPathSome(ap), _, ap0, config) ) } @@ -2352,7 +2373,7 @@ private predicate pathOutOfCallable0( ) { pos = getReturnPosition(mid.getNode()) and innercc = mid.getCallContext() and - not innercc instanceof CallContextCall and + innercc instanceof CallContextNoCall and ap = mid.getAp() and config = mid.getConfiguration() } @@ -2867,7 +2888,7 @@ private module FlowExploration { ) { pos = getReturnPosition(mid.getNode()) and innercc = mid.getCallContext() and - not innercc instanceof CallContextCall and + innercc instanceof CallContextNoCall and ap = mid.getAp() and config = mid.getConfiguration() } diff --git a/python/ql/src/experimental/dataflow/internal/DataFlowImpl2.qll b/python/ql/src/experimental/dataflow/internal/DataFlowImpl2.qll index 5042dce683f..8c210edbe5f 100644 --- a/python/ql/src/experimental/dataflow/internal/DataFlowImpl2.qll +++ b/python/ql/src/experimental/dataflow/internal/DataFlowImpl2.qll @@ -1066,7 +1066,7 @@ private module LocalFlowBigStep { * Holds if `node` can be the first node in a maximal subsequence of local * flow steps in a dataflow path. */ - private predicate localFlowEntry(Node node, Configuration config) { + predicate localFlowEntry(Node node, Configuration config) { nodeCand2(node, config) and ( config.isSource(node) or @@ -1650,53 +1650,53 @@ private class AccessPathOption extends TAccessPathOption { * Holds if `node` is reachable with access path `ap` from a source in * the configuration `config`. * - * The Boolean `fromArg` records whether the node is reached through an + * 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. */ private predicate flowFwd( - Node node, boolean fromArg, AccessPathOption argAp, AccessPathFront apf, AccessPath ap, + Node node, CallContext cc, AccessPathOption argAp, AccessPathFront apf, AccessPath ap, Configuration config ) { - flowFwd0(node, fromArg, argAp, apf, ap, config) and + flowFwd0(node, cc, argAp, apf, ap, config) and flowCand(node, _, _, apf, config) } private predicate flowFwd0( - Node node, boolean fromArg, AccessPathOption argAp, AccessPathFront apf, AccessPath ap, + Node node, CallContext cc, AccessPathOption argAp, AccessPathFront apf, AccessPath ap, Configuration config ) { flowCand(node, _, _, _, config) and config.isSource(node) and - fromArg = false and + cc instanceof CallContextAny and argAp = TAccessPathNone() and ap = TNil(getNodeType(node)) and apf = ap.(AccessPathNil).getFront() or flowCand(node, _, _, _, unbind(config)) and ( - exists(Node mid | - flowFwd(mid, fromArg, argAp, apf, ap, config) and - localFlowBigStep(mid, node, true, _, config, _) + exists(Node mid, LocalCallContext localCC | + flowFwdLocalEntry(mid, cc, argAp, apf, ap, localCC, config) and + localFlowBigStep(mid, node, true, _, config, localCC) ) or - exists(Node mid, AccessPathNil nil | - flowFwd(mid, fromArg, argAp, _, nil, config) and - localFlowBigStep(mid, node, false, apf, config, _) and + exists(Node mid, AccessPathNil nil, LocalCallContext localCC | + flowFwdLocalEntry(mid, cc, argAp, _, nil, localCC, config) and + localFlowBigStep(mid, node, false, apf, config, localCC) and apf = ap.(AccessPathNil).getFront() ) or exists(Node mid | flowFwd(mid, _, _, apf, ap, config) and jumpStep(mid, node, config) and - fromArg = false and + cc instanceof CallContextAny and argAp = TAccessPathNone() ) or exists(Node mid, AccessPathNil nil | flowFwd(mid, _, _, _, nil, config) and additionalJumpStep(mid, node, config) and - fromArg = false and + cc instanceof CallContextAny and argAp = TAccessPathNone() and ap = TNil(getNodeType(node)) and apf = ap.(AccessPathNil).getFront() @@ -1704,40 +1704,51 @@ private predicate flowFwd0( ) or // store - exists(TypedContent tc | flowFwdStore(node, tc, pop(tc, ap), apf, fromArg, argAp, config)) + exists(TypedContent tc | flowFwdStore(node, tc, pop(tc, ap), apf, cc, argAp, config)) or // read exists(TypedContent tc | - flowFwdRead(node, _, push(tc, ap), apf, fromArg, argAp, config) and + flowFwdRead(node, _, push(tc, ap), apf, cc, argAp, config) and flowFwdConsCand(tc, apf, ap, config) ) or // flow into a callable - flowFwdIn(_, node, _, _, apf, ap, config) and - fromArg = true and + flowFwdIn(_, node, _, cc, _, apf, ap, config) and if flowCand(node, true, _, apf, config) then argAp = TAccessPathSome(ap) else argAp = TAccessPathNone() or // flow out of a callable exists(DataFlowCall call | - flowFwdOut(call, node, fromArg, argAp, apf, ap, config) and - fromArg = false + exists(DataFlowCallable c | + flowFwdOut(call, node, any(CallContextNoCall innercc), c, argAp, apf, ap, config) and + if reducedViableImplInReturn(c, call) then cc = TReturn(c, call) else cc = TAnyCallContext() + ) or exists(AccessPath argAp0 | flowFwdOutFromArg(call, node, argAp0, apf, ap, config) and - flowFwdIsEntered(call, fromArg, argAp, argAp0, config) + flowFwdIsEntered(call, cc, argAp, argAp0, config) ) ) } +pragma[nomagic] +private predicate flowFwdLocalEntry( + Node node, CallContext cc, AccessPathOption argAp, AccessPathFront apf, AccessPath ap, + LocalCallContext localCC, Configuration config +) { + flowFwd(node, cc, argAp, apf, ap, config) and + localFlowEntry(node, config) and + localCC = getLocalCallContext(cc, node.getEnclosingCallable()) +} + pragma[nomagic] private predicate flowFwdStore( - Node node, TypedContent tc, AccessPath ap0, AccessPathFront apf, boolean fromArg, + Node node, TypedContent tc, AccessPath ap0, AccessPathFront apf, CallContext cc, AccessPathOption argAp, Configuration config ) { exists(Node mid, AccessPathFront apf0 | - flowFwd(mid, fromArg, argAp, apf0, ap0, config) and + flowFwd(mid, cc, argAp, apf0, ap0, config) and flowFwdStore0(mid, tc, node, apf0, apf, config) ) } @@ -1764,20 +1775,20 @@ private predicate flowFwdStore0( pragma[nomagic] private predicate flowFwdRead0( - Node node1, TypedContent tc, AccessPathFrontHead apf0, AccessPath ap0, Node node2, - boolean fromArg, AccessPathOption argAp, Configuration config + Node node1, TypedContent tc, AccessPathFrontHead apf0, AccessPath ap0, Node node2, CallContext cc, + AccessPathOption argAp, Configuration config ) { - flowFwd(node1, fromArg, argAp, apf0, ap0, config) and + flowFwd(node1, cc, argAp, apf0, ap0, config) and readCandFwd(node1, tc, apf0, node2, config) } pragma[nomagic] private predicate flowFwdRead( - Node node, AccessPathFrontHead apf0, AccessPath ap0, AccessPathFront apf, boolean fromArg, + Node node, AccessPathFrontHead apf0, AccessPath ap0, AccessPathFront apf, CallContext cc, AccessPathOption argAp, Configuration config ) { exists(Node mid, TypedContent tc | - flowFwdRead0(mid, tc, apf0, ap0, node, fromArg, argAp, config) and + flowFwdRead0(mid, tc, apf0, ap0, node, cc, argAp, config) and flowCand(node, _, _, apf, unbind(config)) and flowCandConsCand(tc, apf, unbind(config)) ) @@ -1795,13 +1806,16 @@ private predicate flowFwdConsCand( pragma[nomagic] private predicate flowFwdIn( - DataFlowCall call, ParameterNode p, boolean fromArg, AccessPathOption argAp, AccessPathFront apf, - AccessPath ap, Configuration config + DataFlowCall call, ParameterNode p, CallContext outercc, CallContext innercc, + AccessPathOption argAp, AccessPathFront apf, AccessPath ap, Configuration config ) { - exists(ArgumentNode arg, boolean allowsFieldFlow | - flowFwd(arg, fromArg, argAp, apf, ap, config) and + exists(ArgumentNode arg, boolean allowsFieldFlow, DataFlowCallable c | + flowFwd(arg, outercc, argAp, apf, ap, config) and flowIntoCallNodeCand2(call, arg, p, allowsFieldFlow, config) and - flowCand(p, _, _, _, unbind(config)) + c = p.getEnclosingCallable() and + c = resolveCall(call, outercc) and + flowCand(p, _, _, _, unbind(config)) and + if recordDataFlowCallSite(call, c) then innercc = TSpecificCall(call) else innercc = TSomeCall() | ap instanceof AccessPathNil or allowsFieldFlow = true ) @@ -1809,13 +1823,19 @@ private predicate flowFwdIn( pragma[nomagic] private predicate flowFwdOut( - DataFlowCall call, Node node, boolean fromArg, AccessPathOption argAp, AccessPathFront apf, - AccessPath ap, Configuration config + DataFlowCall call, Node node, CallContext innercc, DataFlowCallable innerc, + AccessPathOption argAp, AccessPathFront apf, AccessPath ap, Configuration config ) { exists(ReturnNodeExt ret, boolean allowsFieldFlow | - flowFwd(ret, fromArg, argAp, apf, ap, config) and + flowFwd(ret, innercc, argAp, apf, ap, config) and flowOutOfCallNodeCand2(call, ret, node, allowsFieldFlow, config) and - flowCand(node, _, _, _, unbind(config)) + innerc = ret.getEnclosingCallable() and + flowCand(node, _, _, _, unbind(config)) and + ( + resolveReturn(innercc, innerc, call) + or + innercc.(CallContextCall).matchesCall(call) + ) | ap instanceof AccessPathNil or allowsFieldFlow = true ) @@ -1826,7 +1846,7 @@ private predicate flowFwdOutFromArg( DataFlowCall call, Node node, AccessPath argAp, AccessPathFront apf, AccessPath ap, Configuration config ) { - flowFwdOut(call, node, true, TAccessPathSome(argAp), apf, ap, config) + flowFwdOut(call, node, any(CallContextCall ccc), _, TAccessPathSome(argAp), apf, ap, config) } /** @@ -1834,10 +1854,10 @@ private predicate flowFwdOutFromArg( */ pragma[nomagic] private predicate flowFwdIsEntered( - DataFlowCall call, boolean fromArg, AccessPathOption argAp, AccessPath ap, Configuration config + DataFlowCall call, CallContext cc, AccessPathOption argAp, AccessPath ap, Configuration config ) { exists(ParameterNode p, AccessPathFront apf | - flowFwdIn(call, p, fromArg, argAp, apf, ap, config) and + flowFwdIn(call, p, cc, _, argAp, apf, ap, config) and flowCand(p, true, TAccessPathFrontSome(_), apf, config) ) } @@ -1920,7 +1940,7 @@ private predicate flow0( // flow out of a callable flowOut(_, node, _, _, ap, config) and toReturn = true and - if flowFwd(node, true, TAccessPathSome(_), _, ap, config) + if flowFwd(node, any(CallContextCall ccc), TAccessPathSome(_), _, ap, config) then returnAp = TAccessPathSome(ap) else returnAp = TAccessPathNone() } @@ -2006,9 +2026,10 @@ private predicate flowIsReturned( DataFlowCall call, boolean toReturn, AccessPathOption returnAp, AccessPath ap, Configuration config ) { - exists(ReturnNodeExt ret | + exists(ReturnNodeExt ret, CallContextCall ccc | flowOut(call, ret, toReturn, returnAp, ap, config) and - flowFwd(ret, true, TAccessPathSome(_), _, ap, config) + flowFwd(ret, ccc, TAccessPathSome(_), _, ap, config) and + ccc.matchesCall(call) ) } @@ -2031,7 +2052,7 @@ private newtype TSummaryCtx = exists(ReturnNodeExt ret, Configuration config, AccessPath ap0 | parameterFlow(p, ap, ret.getEnclosingCallable(), config) and flow(ret, true, TAccessPathSome(_), ap0, config) and - flowFwd(ret, true, TAccessPathSome(ap), _, ap0, config) + flowFwd(ret, any(CallContextCall ccc), TAccessPathSome(ap), _, ap0, config) ) } @@ -2352,7 +2373,7 @@ private predicate pathOutOfCallable0( ) { pos = getReturnPosition(mid.getNode()) and innercc = mid.getCallContext() and - not innercc instanceof CallContextCall and + innercc instanceof CallContextNoCall and ap = mid.getAp() and config = mid.getConfiguration() } @@ -2867,7 +2888,7 @@ private module FlowExploration { ) { pos = getReturnPosition(mid.getNode()) and innercc = mid.getCallContext() and - not innercc instanceof CallContextCall and + innercc instanceof CallContextNoCall and ap = mid.getAp() and config = mid.getConfiguration() } diff --git a/python/ql/src/experimental/dataflow/internal/DataFlowImplCommon.qll b/python/ql/src/experimental/dataflow/internal/DataFlowImplCommon.qll index 27ab1d01feb..892250f44bb 100644 --- a/python/ql/src/experimental/dataflow/internal/DataFlowImplCommon.qll +++ b/python/ql/src/experimental/dataflow/internal/DataFlowImplCommon.qll @@ -512,13 +512,19 @@ abstract class CallContext extends TCallContext { abstract predicate relevantFor(DataFlowCallable callable); } -class CallContextAny extends CallContext, TAnyCallContext { +abstract class CallContextNoCall extends CallContext { } + +class CallContextAny extends CallContextNoCall, TAnyCallContext { override string toString() { result = "CcAny" } override predicate relevantFor(DataFlowCallable callable) { any() } } -abstract class CallContextCall extends CallContext { } +abstract class CallContextCall extends CallContext { + /** Holds if this call context may be `call`. */ + bindingset[call] + abstract predicate matchesCall(DataFlowCall call); +} class CallContextSpecificCall extends CallContextCall, TSpecificCall { override string toString() { @@ -529,6 +535,8 @@ class CallContextSpecificCall extends CallContextCall, TSpecificCall { recordDataFlowCallSite(getCall(), callable) } + override predicate matchesCall(DataFlowCall call) { call = this.getCall() } + DataFlowCall getCall() { this = TSpecificCall(result) } } @@ -538,9 +546,11 @@ class CallContextSomeCall extends CallContextCall, TSomeCall { override predicate relevantFor(DataFlowCallable callable) { exists(ParameterNode p | p.getEnclosingCallable() = callable) } + + override predicate matchesCall(DataFlowCall call) { any() } } -class CallContextReturn extends CallContext, TReturn { +class CallContextReturn extends CallContextNoCall, TReturn { override string toString() { exists(DataFlowCall call | this = TReturn(_, call) | result = "CcReturn(" + call + ")") } diff --git a/python/ql/test/query-tests/Imports/general/ImportStarUsed.expected b/python/ql/test/query-tests/Imports/general/ImportStarUsed.expected index 9aa101edbca..d29bc2b9b5f 100644 --- a/python/ql/test/query-tests/Imports/general/ImportStarUsed.expected +++ b/python/ql/test/query-tests/Imports/general/ImportStarUsed.expected @@ -1,2 +1,3 @@ | imports_test.py:21:1:21:20 | from module import * | Using 'from ... import *' pollutes the namespace | | imports_test.py:22:1:22:32 | from module_without_all import * | Using 'from ... import *' pollutes the namespace | +| imports_test.py:65:1:65:40 | from module_that_does_not_exist import * | Using 'from ... import *' pollutes the namespace | diff --git a/python/ql/test/query-tests/Imports/general/imports_test.py b/python/ql/test/query-tests/Imports/general/imports_test.py index 5e5184b78c7..4b51f8254fc 100644 --- a/python/ql/test/query-tests/Imports/general/imports_test.py +++ b/python/ql/test/query-tests/Imports/general/imports_test.py @@ -61,3 +61,5 @@ import module1 as different #Use it different +# FP reported in https://github.com/github/codeql/issues/4003 +from module_that_does_not_exist import *