diff --git a/csharp/ql/lib/semmle/code/csharp/dataflow/internal/DataFlowImplSpecific.qll b/csharp/ql/lib/semmle/code/csharp/dataflow/internal/DataFlowImplSpecific.qll index af104d777b87..ab1e75b3d0fc 100644 --- a/csharp/ql/lib/semmle/code/csharp/dataflow/internal/DataFlowImplSpecific.qll +++ b/csharp/ql/lib/semmle/code/csharp/dataflow/internal/DataFlowImplSpecific.qll @@ -29,4 +29,8 @@ module CsharpDataFlow implements InputSig { predicate neverSkipInPathGraph(Node n) { exists(n.(AssignableDefinitionNode).getDefinition().getTargetAccess()) } + + DataFlowType getSourceContextParameterNodeType(Node p) { + exists(p) and result.isSourceContextParameterType() + } } diff --git a/csharp/ql/lib/semmle/code/csharp/dataflow/internal/DataFlowPrivate.qll b/csharp/ql/lib/semmle/code/csharp/dataflow/internal/DataFlowPrivate.qll index 109c27de67be..9a550a30b737 100644 --- a/csharp/ql/lib/semmle/code/csharp/dataflow/internal/DataFlowPrivate.qll +++ b/csharp/ql/lib/semmle/code/csharp/dataflow/internal/DataFlowPrivate.qll @@ -1179,7 +1179,8 @@ private module Cached { cached newtype TDataFlowType = TGvnDataFlowType(Gvn::GvnType t) or - TDelegateDataFlowType(Callable lambda) { lambdaCreationExpr(_, lambda) } + TDelegateDataFlowType(Callable lambda) { lambdaCreationExpr(_, lambda) } or + TSourceContextParameterType() } import Cached @@ -2394,6 +2395,8 @@ class DataFlowType extends TDataFlowType { Callable asDelegate() { this = TDelegateDataFlowType(result) } + predicate isSourceContextParameterType() { this = TSourceContextParameterType() } + /** * Gets an expression that creates a delegate of this type. * @@ -2412,6 +2415,9 @@ class DataFlowType extends TDataFlowType { result = this.asGvnType().toString() or result = this.asDelegate().toString() + or + this.isSourceContextParameterType() and + result = "" } } @@ -2469,6 +2475,11 @@ private predicate compatibleTypesDelegateLeft(DataFlowType dt1, DataFlowType dt2 ) } +pragma[nomagic] +private predicate compatibleTypesSourceContextParameterTypeLeft(DataFlowType dt1, DataFlowType dt2) { + dt1.isSourceContextParameterType() and not exists(dt2.asDelegate()) +} + /** * Holds if `t1` and `t2` are compatible, that is, whether data can flow from * a node of type `t1` to a node of type `t2`. @@ -2499,6 +2510,10 @@ predicate compatibleTypes(DataFlowType dt1, DataFlowType dt2) { compatibleTypesDelegateLeft(dt2, dt1) or dt1.asDelegate() = dt2.asDelegate() + or + compatibleTypesSourceContextParameterTypeLeft(dt1, dt2) + or + compatibleTypesSourceContextParameterTypeLeft(dt2, dt1) } pragma[nomagic] @@ -2511,6 +2526,8 @@ predicate typeStrongerThan(DataFlowType t1, DataFlowType t2) { uselessTypebound(t2) or compatibleTypesDelegateLeft(t1, t2) + or + compatibleTypesSourceContextParameterTypeLeft(t1, t2) } /** diff --git a/csharp/ql/test/utils/modelgenerator/dataflow/Summaries.cs b/csharp/ql/test/utils/modelgenerator/dataflow/Summaries.cs index b59513504d9d..4c85b397ac1f 100644 --- a/csharp/ql/test/utils/modelgenerator/dataflow/Summaries.cs +++ b/csharp/ql/test/utils/modelgenerator/dataflow/Summaries.cs @@ -536,6 +536,12 @@ public void Apply(Action a, object o) { a(o); } + + private void CallApply() + { + // Test that this call to `Apply` does not interfere with the flow summaries generated for `Apply` + Apply(x => x, null); + } } public static class HigherOrderExtensionMethods diff --git a/rust/ql/lib/codeql/rust/dataflow/internal/DataFlowImpl.qll b/rust/ql/lib/codeql/rust/dataflow/internal/DataFlowImpl.qll index 27773758fc46..6ae18d8ce149 100644 --- a/rust/ql/lib/codeql/rust/dataflow/internal/DataFlowImpl.qll +++ b/rust/ql/lib/codeql/rust/dataflow/internal/DataFlowImpl.qll @@ -193,9 +193,7 @@ module LocalFlow { } pragma[nomagic] - predicate localFlowStepCommon(Node nodeFrom, Node nodeTo) { - nodeFrom.asExpr() = getALastEvalNode(nodeTo.asExpr()) - or + predicate localMustFlowStep(Node nodeFrom, Node nodeTo) { // An edge from the right-hand side of a let statement to the left-hand side. exists(LetStmt s | nodeFrom.asExpr() = s.getInitializer() and @@ -238,6 +236,15 @@ module LocalFlow { nodeTo.asPat() = match.getAnArm().getPat() ) or + nodeFrom.asExpr() = nodeTo.asExpr().(ParenExpr).getExpr() + } + + pragma[nomagic] + predicate localFlowStepCommon(Node nodeFrom, Node nodeTo) { + localMustFlowStep(nodeFrom, nodeTo) + or + nodeFrom.asExpr() = getALastEvalNode(nodeTo.asExpr()) + or nodeFrom.asPat().(OrPat).getAPat() = nodeTo.asPat() or nodeTo.(PostUpdateNode).getPreUpdateNode().asExpr() = @@ -263,10 +270,84 @@ predicate lambdaCallExpr(CallExprImpl::DynamicCallExpr call, LambdaCallKind kind exists(kind) } +// NOTE: We do not yet track type information, except for closures where +// we use the closure itself to represent the unique type. +final class DataFlowType extends TDataFlowType { + Expr asClosureExpr() { this = TClosureExprType(result) } + + predicate isUnknownType() { this = TUnknownType() } + + predicate isSourceContextParameterType() { this = TSourceContextParameterType() } + + string toString() { + exists(this.asClosureExpr()) and + result = "... => .." + or + this.isUnknownType() and + result = "" + or + this.isSourceContextParameterType() and + result = "" + } +} + +pragma[nomagic] +private predicate compatibleTypesSourceContextParameterTypeLeft(DataFlowType t1, DataFlowType t2) { + t1.isSourceContextParameterType() and not exists(t2.asClosureExpr()) +} + +pragma[nomagic] +private predicate compatibleTypesLeft(DataFlowType t1, DataFlowType t2) { + t1.isUnknownType() and exists(t2) + or + t1.asClosureExpr() = t2.asClosureExpr() + or + compatibleTypesSourceContextParameterTypeLeft(t1, t2) +} + +predicate compatibleTypes(DataFlowType t1, DataFlowType t2) { + compatibleTypesLeft(t1, t2) + or + compatibleTypesLeft(t2, t1) +} + +pragma[nomagic] +predicate typeStrongerThan(DataFlowType t1, DataFlowType t2) { + not t1.isUnknownType() and t2.isUnknownType() + or + compatibleTypesSourceContextParameterTypeLeft(t1, t2) +} + +DataFlowType getNodeType(NodePublic node) { + result.asClosureExpr() = node.asExpr() + or + result.asClosureExpr() = node.(ClosureParameterNode).getCfgScope() + or + exists(VariableCapture::Flow::SynthesizedCaptureNode scn | + scn = node.(CaptureNode).getSynthesizedCaptureNode() and + if scn.isInstanceAccess() + then result.asClosureExpr() = scn.getEnclosingCallable() + else result.isUnknownType() + ) + or + not lambdaCreationExpr(node.asExpr()) and + not node instanceof ClosureParameterNode and + not node instanceof CaptureNode and + result.isUnknownType() +} + // Defines a set of aliases needed for the `RustDataFlow` module private module Aliases { class DataFlowCallableAlias = DataFlowCallable; + class DataFlowTypeAlias = DataFlowType; + + predicate compatibleTypesAlias = compatibleTypes/2; + + predicate typeStrongerThanAlias = typeStrongerThan/2; + + predicate getNodeTypeAlias = getNodeType/1; + class ReturnKindAlias = ReturnKind; class DataFlowCallAlias = DataFlowCall; @@ -398,8 +479,6 @@ module RustDataFlowGen implements InputSig result = node.(Node::Node).getEnclosingCallable() } - DataFlowType getNodeType(Node node) { any() } - predicate nodeIsHidden(Node node) { node instanceof SsaNode or node.(FlowSummaryNode).getSummaryNode().isHidden() or @@ -486,15 +565,17 @@ module RustDataFlowGen implements InputSig */ OutNode getAnOutNode(DataFlowCall call, ReturnKind kind) { call = result.getCall(kind) } - // NOTE: For now we use the type `Unit` and do not benefit from type - // information in the data flow analysis. - final class DataFlowType extends Unit { - string toString() { result = "" } - } + class DataFlowType = DataFlowTypeAlias; - predicate compatibleTypes(DataFlowType t1, DataFlowType t2) { any() } + predicate compatibleTypes = compatibleTypesAlias/2; - predicate typeStrongerThan(DataFlowType t1, DataFlowType t2) { none() } + predicate typeStrongerThan = typeStrongerThanAlias/2; + + DataFlowType getSourceContextParameterNodeType(Node p) { + exists(p) and result.isSourceContextParameterType() + } + + predicate getNodeType = getNodeTypeAlias/1; class Content = ContentAlias; @@ -897,6 +978,8 @@ module RustDataFlowGen implements InputSig predicate localMustFlowStep(Node node1, Node node2) { SsaFlow::localMustFlowStep(node1, node2) or + LocalFlow::localMustFlowStep(node1, node2) + or FlowSummaryImpl::Private::Steps::summaryLocalMustFlowStep(node1 .(FlowSummaryNode) .getSummaryNode(), node2.(FlowSummaryNode).getSummaryNode()) @@ -1110,6 +1193,12 @@ private module Cached { TCfgScope(CfgScope scope) or TSummarizedCallable(SummarizedCallable c) + cached + newtype TDataFlowType = + TClosureExprType(Expr e) { lambdaCreationExpr(e) } or + TUnknownType() or + TSourceContextParameterType() + /** This is the local flow predicate that is exposed. */ cached predicate localFlowStepImpl(Node nodeFrom, Node nodeTo) { diff --git a/rust/ql/lib/codeql/rust/dataflow/internal/ModelsAsData.qll b/rust/ql/lib/codeql/rust/dataflow/internal/ModelsAsData.qll index 4d28dd8de812..0a7288a330fa 100644 --- a/rust/ql/lib/codeql/rust/dataflow/internal/ModelsAsData.qll +++ b/rust/ql/lib/codeql/rust/dataflow/internal/ModelsAsData.qll @@ -280,7 +280,7 @@ private module Debug { private import Content private import codeql.rust.dataflow.internal.DataFlowImpl private import codeql.rust.internal.typeinference.TypeMention - private import codeql.rust.internal.typeinference.Type + private import codeql.rust.internal.typeinference.Type as Type private predicate relevantManualModel(SummarizedCallableImpl sc, string can) { exists(Provenance manual | @@ -298,7 +298,7 @@ private module Debug { sc.propagatesFlow(input, _, _, _, _, _) and input.head() = SummaryComponent::argument(pos) and p = pos.getParameterIn(sc.getParamList()) and - tm.getType() instanceof RefType and + tm.getType() instanceof Type::RefType and not input.tail().head() = SummaryComponent::content(TSingletonContentSet(TReferenceContent())) | tm = p.getTypeRepr() @@ -313,7 +313,7 @@ private module Debug { exists(TypeMention tm | relevantManualModel(sc, can) and sc.propagatesFlow(_, output, _, _, _, _) and - tm.getType() instanceof RefType and + tm.getType() instanceof Type::RefType and output.head() = SummaryComponent::return(_) and not output.tail().head() = SummaryComponent::content(TSingletonContentSet(TReferenceContent())) and diff --git a/rust/ql/test/library-tests/dataflow/lambdas/inline-flow.expected b/rust/ql/test/library-tests/dataflow/lambdas/inline-flow.expected index 7c99702aec63..ca4bbe77479a 100644 --- a/rust/ql/test/library-tests/dataflow/lambdas/inline-flow.expected +++ b/rust/ql/test/library-tests/dataflow/lambdas/inline-flow.expected @@ -13,9 +13,9 @@ edges | main.rs:29:13:29:22 | f(...) | main.rs:29:9:29:9 | b | provenance | | | main.rs:29:21:29:21 | a | main.rs:27:20:27:23 | ... | provenance | | | main.rs:29:21:29:21 | a | main.rs:29:13:29:22 | f(...) | provenance | | -| main.rs:37:16:37:25 | source(...) | main.rs:39:5:39:5 | [post] f [captured capt] | provenance | | -| main.rs:39:5:39:5 | [post] f [captured capt] | main.rs:40:10:40:13 | capt | provenance | | -| main.rs:39:5:39:5 | [post] f [captured capt] | main.rs:44:5:44:5 | g [captured capt] | provenance | | +| main.rs:37:16:37:25 | source(...) | main.rs:39:5:39:5 | [post] f : ... => .. [captured capt] | provenance | | +| main.rs:39:5:39:5 | [post] f : ... => .. [captured capt] | main.rs:40:10:40:13 | capt | provenance | | +| main.rs:39:5:39:5 | [post] f : ... => .. [captured capt] | main.rs:44:5:44:5 | g [captured capt] | provenance | | | main.rs:44:5:44:5 | g [captured capt] | main.rs:42:14:42:17 | capt | provenance | | | main.rs:47:29:49:1 | { ... } | main.rs:57:10:57:12 | f(...) | provenance | | | main.rs:48:5:48:14 | source(...) | main.rs:47:29:49:1 | { ... } | provenance | | @@ -30,6 +30,31 @@ edges | main.rs:77:13:77:22 | f(...) | main.rs:77:9:77:9 | b | provenance | | | main.rs:77:21:77:21 | a | main.rs:66:24:66:32 | ...: i64 | provenance | | | main.rs:77:21:77:21 | a | main.rs:77:13:77:22 | f(...) | provenance | | +| main.rs:81:28:81:33 | ...: i64 | main.rs:82:7:82:7 | x | provenance | | +| main.rs:81:28:81:33 | ...: i64 | main.rs:82:7:82:7 | x | provenance | | +| main.rs:81:28:81:33 | ...: i64 | main.rs:82:7:82:7 | x | provenance | | +| main.rs:82:7:82:7 | x | main.rs:87:12:87:12 | ... | provenance | | +| main.rs:82:7:82:7 | x | main.rs:89:12:89:12 | ... | provenance | | +| main.rs:82:7:82:7 | x | main.rs:99:17:99:17 | ... | provenance | | +| main.rs:82:7:82:7 | x | main.rs:101:17:101:17 | ... | provenance | | +| main.rs:86:9:86:9 | a | main.rs:87:24:87:24 | a | provenance | | +| main.rs:86:13:86:22 | source(...) | main.rs:86:9:86:9 | a | provenance | | +| main.rs:87:12:87:12 | ... | main.rs:87:20:87:20 | x | provenance | | +| main.rs:87:24:87:24 | a | main.rs:81:28:81:33 | ...: i64 | provenance | | +| main.rs:88:9:88:9 | b | main.rs:89:24:89:24 | b | provenance | | +| main.rs:88:13:88:22 | source(...) | main.rs:88:9:88:9 | b | provenance | | +| main.rs:89:12:89:12 | ... | main.rs:89:20:89:20 | x | provenance | | +| main.rs:89:24:89:24 | b | main.rs:81:28:81:33 | ...: i64 | provenance | | +| main.rs:93:33:93:38 | ...: i64 | main.rs:94:14:94:14 | x | provenance | | +| main.rs:94:14:94:14 | x | main.rs:81:28:81:33 | ...: i64 | provenance | | +| main.rs:98:9:98:9 | a | main.rs:99:29:99:29 | a | provenance | | +| main.rs:98:13:98:22 | source(...) | main.rs:98:9:98:9 | a | provenance | | +| main.rs:99:17:99:17 | ... | main.rs:99:25:99:25 | x | provenance | | +| main.rs:99:29:99:29 | a | main.rs:93:33:93:38 | ...: i64 | provenance | | +| main.rs:100:9:100:9 | b | main.rs:101:29:101:29 | b | provenance | | +| main.rs:100:13:100:22 | source(...) | main.rs:100:9:100:9 | b | provenance | | +| main.rs:101:17:101:17 | ... | main.rs:101:25:101:25 | x | provenance | | +| main.rs:101:29:101:29 | b | main.rs:93:33:93:38 | ...: i64 | provenance | | nodes | main.rs:10:20:10:52 | if cond {...} else {...} | semmle.label | if cond {...} else {...} | | main.rs:10:30:10:39 | source(...) | semmle.label | source(...) | @@ -48,7 +73,7 @@ nodes | main.rs:29:21:29:21 | a | semmle.label | a | | main.rs:30:10:30:10 | b | semmle.label | b | | main.rs:37:16:37:25 | source(...) | semmle.label | source(...) | -| main.rs:39:5:39:5 | [post] f [captured capt] | semmle.label | [post] f [captured capt] | +| main.rs:39:5:39:5 | [post] f : ... => .. [captured capt] | semmle.label | [post] f : ... => .. [captured capt] | | main.rs:40:10:40:13 | capt | semmle.label | capt | | main.rs:42:14:42:17 | capt | semmle.label | capt | | main.rs:44:5:44:5 | g [captured capt] | semmle.label | g [captured capt] | @@ -68,6 +93,34 @@ nodes | main.rs:77:13:77:22 | f(...) | semmle.label | f(...) | | main.rs:77:21:77:21 | a | semmle.label | a | | main.rs:78:10:78:10 | b | semmle.label | b | +| main.rs:81:28:81:33 | ...: i64 | semmle.label | ...: i64 | +| main.rs:81:28:81:33 | ...: i64 | semmle.label | ...: i64 | +| main.rs:81:28:81:33 | ...: i64 | semmle.label | ...: i64 | +| main.rs:82:7:82:7 | x | semmle.label | x | +| main.rs:82:7:82:7 | x | semmle.label | x | +| main.rs:82:7:82:7 | x | semmle.label | x | +| main.rs:86:9:86:9 | a | semmle.label | a | +| main.rs:86:13:86:22 | source(...) | semmle.label | source(...) | +| main.rs:87:12:87:12 | ... | semmle.label | ... | +| main.rs:87:20:87:20 | x | semmle.label | x | +| main.rs:87:24:87:24 | a | semmle.label | a | +| main.rs:88:9:88:9 | b | semmle.label | b | +| main.rs:88:13:88:22 | source(...) | semmle.label | source(...) | +| main.rs:89:12:89:12 | ... | semmle.label | ... | +| main.rs:89:20:89:20 | x | semmle.label | x | +| main.rs:89:24:89:24 | b | semmle.label | b | +| main.rs:93:33:93:38 | ...: i64 | semmle.label | ...: i64 | +| main.rs:94:14:94:14 | x | semmle.label | x | +| main.rs:98:9:98:9 | a | semmle.label | a | +| main.rs:98:13:98:22 | source(...) | semmle.label | source(...) | +| main.rs:99:17:99:17 | ... | semmle.label | ... | +| main.rs:99:25:99:25 | x | semmle.label | x | +| main.rs:99:29:99:29 | a | semmle.label | a | +| main.rs:100:9:100:9 | b | semmle.label | b | +| main.rs:100:13:100:22 | source(...) | semmle.label | source(...) | +| main.rs:101:17:101:17 | ... | semmle.label | ... | +| main.rs:101:25:101:25 | x | semmle.label | x | +| main.rs:101:29:101:29 | b | semmle.label | b | subpaths | main.rs:29:21:29:21 | a | main.rs:27:20:27:23 | ... | main.rs:27:26:27:52 | if cond {...} else {...} | main.rs:29:13:29:22 | f(...) | | main.rs:77:21:77:21 | a | main.rs:66:24:66:32 | ...: i64 | main.rs:66:42:72:1 | { ... } | main.rs:77:13:77:22 | f(...) | @@ -81,3 +134,9 @@ testFailures | main.rs:52:10:52:13 | data | main.rs:62:13:62:22 | source(...) | main.rs:52:10:52:13 | data | $@ | main.rs:62:13:62:22 | source(...) | source(...) | | main.rs:57:10:57:12 | f(...) | main.rs:48:5:48:14 | source(...) | main.rs:57:10:57:12 | f(...) | $@ | main.rs:48:5:48:14 | source(...) | source(...) | | main.rs:78:10:78:10 | b | main.rs:76:13:76:22 | source(...) | main.rs:78:10:78:10 | b | $@ | main.rs:76:13:76:22 | source(...) | source(...) | +| main.rs:87:20:87:20 | x | main.rs:86:13:86:22 | source(...) | main.rs:87:20:87:20 | x | $@ | main.rs:86:13:86:22 | source(...) | source(...) | +| main.rs:89:20:89:20 | x | main.rs:88:13:88:22 | source(...) | main.rs:89:20:89:20 | x | $@ | main.rs:88:13:88:22 | source(...) | source(...) | +| main.rs:99:25:99:25 | x | main.rs:98:13:98:22 | source(...) | main.rs:99:25:99:25 | x | $@ | main.rs:98:13:98:22 | source(...) | source(...) | +| main.rs:99:25:99:25 | x | main.rs:100:13:100:22 | source(...) | main.rs:99:25:99:25 | x | $@ | main.rs:100:13:100:22 | source(...) | source(...) | +| main.rs:101:25:101:25 | x | main.rs:98:13:98:22 | source(...) | main.rs:101:25:101:25 | x | $@ | main.rs:98:13:98:22 | source(...) | source(...) | +| main.rs:101:25:101:25 | x | main.rs:100:13:100:22 | source(...) | main.rs:101:25:101:25 | x | $@ | main.rs:100:13:100:22 | source(...) | source(...) | diff --git a/rust/ql/test/library-tests/dataflow/lambdas/main.rs b/rust/ql/test/library-tests/dataflow/lambdas/main.rs index 252b132ec744..66295f004a22 100644 --- a/rust/ql/test/library-tests/dataflow/lambdas/main.rs +++ b/rust/ql/test/library-tests/dataflow/lambdas/main.rs @@ -78,6 +78,30 @@ fn function_flows_through() { sink(b); // $ hasValueFlow=56 } +fn apply(f: F, x: i64) { + f(x); +} + +fn test_apply() { + let a = source(77); + apply(|x| sink(x), a); // $ hasValueFlow=77 + let b = source(78); + apply(|x| sink(x), b); // $ hasValueFlow=78 + apply(|x| sink(x), 0); +} + +fn apply_wrap(f: F, x: i64) { + apply(f, x); +} + +fn test_apply_wrap() { + let a = source(79); + apply_wrap(|x| sink(x), a); // $ hasValueFlow=79 $ SPURIOUS: hasValueFlow=80 + let b = source(80); + apply_wrap(|x| sink(x), b); // $ hasValueFlow=80 $ SPURIOUS: hasValueFlow=79 + apply_wrap(|x| sink(x), 0); +} + fn main() { closure_flow_out(); closure_flow_in(); @@ -86,4 +110,6 @@ fn main() { function_flow_in(); function_flow_out(); function_flows_through(); + test_apply(); + test_apply_wrap(); } diff --git a/shared/dataflow/codeql/dataflow/DataFlow.qll b/shared/dataflow/codeql/dataflow/DataFlow.qll index 7f9c0194374b..cacd52cf8396 100644 --- a/shared/dataflow/codeql/dataflow/DataFlow.qll +++ b/shared/dataflow/codeql/dataflow/DataFlow.qll @@ -63,6 +63,35 @@ signature module InputSig { DataFlowType getNodeType(Node node); + /** + * Gets a special type to use for parameter node `p` belonging to callables with a + * source node where a source call context `FlowFeature` is used, if any. + * + * This can be used to prevent lambdas from being resolved, when a concrete call + * context is needed. Example: + * + * ```csharp + * void Foo(Action a) + * { + * var x = Source(); + * a(x); // (1) + * a = s => Sink(s); // (2) + * a(x); // (3) + * } + * + * void Bar() + * { + * Foo(s => Sink(s)); // (4) + * } + * ``` + * + * If a source call context flow feature is used, `a` can be assigned a special + * type that is incompatible with the type of _any_ lambda expression, which will + * prevent the call edge from (1) to (4). Note that the call edge from (3) to (2) + * will still be valid. + */ + default DataFlowType getSourceContextParameterNodeType(Node p) { none() } + predicate nodeIsHidden(Node node); class DataFlowExpr; diff --git a/shared/dataflow/codeql/dataflow/internal/DataFlowImpl.qll b/shared/dataflow/codeql/dataflow/internal/DataFlowImpl.qll index 506774857d8e..ed0412d1cd4d 100644 --- a/shared/dataflow/codeql/dataflow/internal/DataFlowImpl.qll +++ b/shared/dataflow/codeql/dataflow/internal/DataFlowImpl.qll @@ -1103,6 +1103,16 @@ module MakeImpl Lang> { private module FwdTypeFlowInput implements TypeFlowInput { predicate enableTypeFlow = Param::enableTypeFlow/0; + pragma[nomagic] + predicate isParameterNodeInSourceCallContext(ParamNode p) { + hasSourceCallCtx() and + exists(Node source, DataFlowCallable c | + Config::isSource(pragma[only_bind_into](source), _) and + nodeEnclosingCallable(source, c) and + nodeEnclosingCallable(p, c) + ) + } + predicate relevantCallEdgeIn = PrevStage::relevantCallEdgeIn/2; predicate relevantCallEdgeOut = PrevStage::relevantCallEdgeOut/2; @@ -1410,6 +1420,8 @@ module MakeImpl Lang> { private module RevTypeFlowInput implements TypeFlowInput { predicate enableTypeFlow = Param::enableTypeFlow/0; + predicate isParameterNodeInSourceCallContext(ParamNode p) { none() } + predicate relevantCallEdgeIn(Call call, Callable c) { flowOutOfCallAp(call, c, _, _, _, _, _) } diff --git a/shared/dataflow/codeql/dataflow/internal/DataFlowImplCommon.qll b/shared/dataflow/codeql/dataflow/internal/DataFlowImplCommon.qll index 51ebb3f8a730..b2d4d13b07d4 100644 --- a/shared/dataflow/codeql/dataflow/internal/DataFlowImplCommon.qll +++ b/shared/dataflow/codeql/dataflow/internal/DataFlowImplCommon.qll @@ -1893,6 +1893,9 @@ module MakeImplCommon Lang> { signature module TypeFlowInput { predicate enableTypeFlow(); + /** Holds if `p` is a parameter of a callable with a source node that has a call context. */ + predicate isParameterNodeInSourceCallContext(ParamNode p); + /** Holds if the edge is possibly needed in the direction `call` to `c`. */ predicate relevantCallEdgeIn(Call call, Callable c); @@ -1953,6 +1956,9 @@ module MakeImplCommon Lang> { /** * Holds if a sequence of calls may propagate the value of `arg` to some * argument-to-parameter call edge that strengthens the static type. + * + * This predicate is a reverse flow computation, starting at calls that + * strengthen the type and then following relevant call edges backwards. */ pragma[nomagic] private predicate trackedArgTypeCand(ArgNode arg) { @@ -1987,6 +1993,9 @@ module MakeImplCommon Lang> { * Holds if `p` is part of a value-propagating call path where the * end-points have stronger types than the intermediate parameter and * argument nodes. + * + * This predicate is a forward flow computation, intersecting with the + * reverse flow computation done in `trackedArgTypeCand`. */ private predicate trackedParamType(ParamNode p) { exists(Call call1, Callable c1, ArgNode argOut, Call call2, Callable c2, ArgNode argIn | @@ -2013,6 +2022,8 @@ module MakeImplCommon Lang> { typeStrongerThanFilter(at, pt) ) or + Input::isParameterNodeInSourceCallContext(p) + or exists(ArgNode arg | trackedArgType(arg) and relevantCallEdge(_, _, arg, p) and @@ -2106,7 +2117,9 @@ module MakeImplCommon Lang> { private predicate typeFlowParamType(ParamNode p, Type t, boolean cc) { exists(Callable c | Input::dataFlowNonCallEntry(c, cc) and - trackedParamWithType(p, t, c) + if cc = true and exists(getSourceContextParameterNodeType(p)) + then t = getSourceContextParameterNodeType(p) + else trackedParamWithType(p, t, c) ) or exists(Type t1, Type t2 |