From 9f8a9b9cadb8f1811e52f0de523467058629c54c Mon Sep 17 00:00:00 2001 From: Asger Feldthaus Date: Tue, 20 Apr 2021 17:10:09 +0100 Subject: [PATCH 001/168] JS: Add taint source/sink summary queries --- javascript/ql/src/Summary/TaintSinks.ql | 15 +++++++++++++++ javascript/ql/src/Summary/TaintSources.ql | 16 ++++++++++++++++ 2 files changed, 31 insertions(+) create mode 100644 javascript/ql/src/Summary/TaintSinks.ql create mode 100644 javascript/ql/src/Summary/TaintSources.ql diff --git a/javascript/ql/src/Summary/TaintSinks.ql b/javascript/ql/src/Summary/TaintSinks.ql new file mode 100644 index 00000000000..24f6579efec --- /dev/null +++ b/javascript/ql/src/Summary/TaintSinks.ql @@ -0,0 +1,15 @@ +/** + * @name Taint sinks + * @description Expressions that are vulnerable if containing untrusted data. + * @kind problem + * @problem.severity informational + * @id js/summary/taint-sinks + * @tags summary + * @precision medium + */ + +import javascript +import meta.internal.TaintMetrics + +from string kind +select relevantTaintSink(kind), kind + " sink" diff --git a/javascript/ql/src/Summary/TaintSources.ql b/javascript/ql/src/Summary/TaintSources.ql new file mode 100644 index 00000000000..7178a76dde9 --- /dev/null +++ b/javascript/ql/src/Summary/TaintSources.ql @@ -0,0 +1,16 @@ +/** + * @name Taint sources + * @description Sources of untrusted input. + * @kind problem + * @problem.severity informational + * @id js/summary/taint-sources + * @tags summary + * @precision medium + */ + +import javascript +import meta.internal.TaintMetrics + +from RemoteFlowSource node +where node = relevantTaintSource() +select node, node.getSourceType() From 02707f0777d42601511f3ad6cd22927baed41fae Mon Sep 17 00:00:00 2001 From: Asger Feldthaus Date: Tue, 20 Apr 2021 19:51:16 +0100 Subject: [PATCH 002/168] JS: informational -> info --- javascript/ql/src/Summary/TaintSinks.ql | 2 +- javascript/ql/src/Summary/TaintSources.ql | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/javascript/ql/src/Summary/TaintSinks.ql b/javascript/ql/src/Summary/TaintSinks.ql index 24f6579efec..2da65398935 100644 --- a/javascript/ql/src/Summary/TaintSinks.ql +++ b/javascript/ql/src/Summary/TaintSinks.ql @@ -2,7 +2,7 @@ * @name Taint sinks * @description Expressions that are vulnerable if containing untrusted data. * @kind problem - * @problem.severity informational + * @problem.severity info * @id js/summary/taint-sinks * @tags summary * @precision medium diff --git a/javascript/ql/src/Summary/TaintSources.ql b/javascript/ql/src/Summary/TaintSources.ql index 7178a76dde9..78e544f0bd5 100644 --- a/javascript/ql/src/Summary/TaintSources.ql +++ b/javascript/ql/src/Summary/TaintSources.ql @@ -2,7 +2,7 @@ * @name Taint sources * @description Sources of untrusted input. * @kind problem - * @problem.severity informational + * @problem.severity info * @id js/summary/taint-sources * @tags summary * @precision medium From 50c63a88c3036b7ffddd579fe5454fe3ad72cf2f Mon Sep 17 00:00:00 2001 From: ihsinme Date: Sun, 25 Apr 2021 22:34:41 +0300 Subject: [PATCH 003/168] Add files via upload --- ...tionOfVariableWithUnnecessarilyWideScope.c | 14 +++++ ...OfVariableWithUnnecessarilyWideScope.qhelp | 26 ++++++++ ...ionOfVariableWithUnnecessarilyWideScope.ql | 62 +++++++++++++++++++ 3 files changed, 102 insertions(+) create mode 100644 cpp/ql/src/experimental/Security/CWE/CWE-1126/DeclarationOfVariableWithUnnecessarilyWideScope.c create mode 100644 cpp/ql/src/experimental/Security/CWE/CWE-1126/DeclarationOfVariableWithUnnecessarilyWideScope.qhelp create mode 100644 cpp/ql/src/experimental/Security/CWE/CWE-1126/DeclarationOfVariableWithUnnecessarilyWideScope.ql diff --git a/cpp/ql/src/experimental/Security/CWE/CWE-1126/DeclarationOfVariableWithUnnecessarilyWideScope.c b/cpp/ql/src/experimental/Security/CWE/CWE-1126/DeclarationOfVariableWithUnnecessarilyWideScope.c new file mode 100644 index 00000000000..b09971b5328 --- /dev/null +++ b/cpp/ql/src/experimental/Security/CWE/CWE-1126/DeclarationOfVariableWithUnnecessarilyWideScope.c @@ -0,0 +1,14 @@ +while(intIndex > 2) +{ + ... + intIndex--; + ... +} // GOOD: coreten cycle +... +while(intIndex > 2) +{ + ... + int intIndex; + intIndex--; + ... +} // BAD: the variable used in the condition does not change. diff --git a/cpp/ql/src/experimental/Security/CWE/CWE-1126/DeclarationOfVariableWithUnnecessarilyWideScope.qhelp b/cpp/ql/src/experimental/Security/CWE/CWE-1126/DeclarationOfVariableWithUnnecessarilyWideScope.qhelp new file mode 100644 index 00000000000..d84f47f5453 --- /dev/null +++ b/cpp/ql/src/experimental/Security/CWE/CWE-1126/DeclarationOfVariableWithUnnecessarilyWideScope.qhelp @@ -0,0 +1,26 @@ + + + +

Using variables with the same name is dangerous. However, such a situation inside the while loop can lead to a violation of the accessibility of the program. Requires the attention of developers.

+ +
+ +

We recommend not to use local variables inside a loop if their names are the same as the variables in the condition of this loop.

+ +
+ +

The following example demonstrates an erroneous and corrected use of a local variable within a loop.

+ + +
+ + +
  • + CERT C Coding Standard: + DCL01-C. Do not reuse variable names in subscopes. +
  • + +
    +
    diff --git a/cpp/ql/src/experimental/Security/CWE/CWE-1126/DeclarationOfVariableWithUnnecessarilyWideScope.ql b/cpp/ql/src/experimental/Security/CWE/CWE-1126/DeclarationOfVariableWithUnnecessarilyWideScope.ql new file mode 100644 index 00000000000..a253a5e0599 --- /dev/null +++ b/cpp/ql/src/experimental/Security/CWE/CWE-1126/DeclarationOfVariableWithUnnecessarilyWideScope.ql @@ -0,0 +1,62 @@ +/** + * @name Errors When Using Variable Declaration Inside Loop + * @description Using variables with the same name is dangerous. + * However, such a situation inside the while loop can lead to a violation of the accessibility of the program. + * Requires the attention of developers. + * @kind problem + * @id cpp/errors-when-using-variable-declaration-inside-loop + * @problem.severity warning + * @precision medium + * @tags correctness + * security + * external/cwe/cwe-1126 + */ + +import cpp + +/** + * Errors when using a variable declaration inside a loop. + */ +class DangerousWhileLoop extends WhileStmt { + Expr exp; + Declaration dl; + + DangerousWhileLoop() { + this = dl.getParentScope().(BlockStmt).getParent*() and + exp = this.getCondition().getAChild*() and + not exp instanceof PointerFieldAccess and + not exp instanceof ValueFieldAccess and + exp.toString() = dl.getName() and + not exp.getParent*() instanceof CrementOperation and + not exp.getParent*() instanceof Assignment and + not exp.getParent*() instanceof FunctionCall + } + + Declaration getDeclaration() { result = dl } + + /** Holds when there are changes to the variables involved in the condition. */ + predicate isUseThisVariable() { + exists(Variable v | + this.getCondition().getAChild*().(VariableAccess).getTarget() = v and + ( + exists(Assignment aexp | + aexp = this.getStmt().getAChild*() and + ( + aexp.getLValue().(ArrayExpr).getArrayBase().(VariableAccess).getTarget() = v + or + aexp.getLValue().(VariableAccess).getTarget() = v + ) + ) + or + exists(CrementOperation crm | + crm = this.getStmt().getAChild*() and + crm.getOperand().(VariableAccess).getTarget() = v + ) + ) + ) + } +} + +from DangerousWhileLoop lp +where not lp.isUseThisVariable() +select lp.getDeclaration(), "A variable with this name is used in the loop condition." From 98f7f70814efa3dae55beb0fd1cb9f4e095d776e Mon Sep 17 00:00:00 2001 From: ihsinme Date: Sun, 25 Apr 2021 22:35:40 +0300 Subject: [PATCH 004/168] Add files via upload --- ...nOfVariableWithUnnecessarilyWideScope.expected | 1 + ...tionOfVariableWithUnnecessarilyWideScope.qlref | 1 + .../Security/CWE/CWE-1126/semmle/tests/test.c | 15 +++++++++++++++ 3 files changed, 17 insertions(+) create mode 100644 cpp/ql/test/experimental/query-tests/Security/CWE/CWE-1126/semmle/tests/DeclarationOfVariableWithUnnecessarilyWideScope.expected create mode 100644 cpp/ql/test/experimental/query-tests/Security/CWE/CWE-1126/semmle/tests/DeclarationOfVariableWithUnnecessarilyWideScope.qlref create mode 100644 cpp/ql/test/experimental/query-tests/Security/CWE/CWE-1126/semmle/tests/test.c diff --git a/cpp/ql/test/experimental/query-tests/Security/CWE/CWE-1126/semmle/tests/DeclarationOfVariableWithUnnecessarilyWideScope.expected b/cpp/ql/test/experimental/query-tests/Security/CWE/CWE-1126/semmle/tests/DeclarationOfVariableWithUnnecessarilyWideScope.expected new file mode 100644 index 00000000000..7b540a33384 --- /dev/null +++ b/cpp/ql/test/experimental/query-tests/Security/CWE/CWE-1126/semmle/tests/DeclarationOfVariableWithUnnecessarilyWideScope.expected @@ -0,0 +1 @@ +| test.c:12:9:12:16 | intIndex | A variable with this name is used in the loop condition. | diff --git a/cpp/ql/test/experimental/query-tests/Security/CWE/CWE-1126/semmle/tests/DeclarationOfVariableWithUnnecessarilyWideScope.qlref b/cpp/ql/test/experimental/query-tests/Security/CWE/CWE-1126/semmle/tests/DeclarationOfVariableWithUnnecessarilyWideScope.qlref new file mode 100644 index 00000000000..6da5822f7f0 --- /dev/null +++ b/cpp/ql/test/experimental/query-tests/Security/CWE/CWE-1126/semmle/tests/DeclarationOfVariableWithUnnecessarilyWideScope.qlref @@ -0,0 +1 @@ +experimental/Security/CWE/CWE-1126/DeclarationOfVariableWithUnnecessarilyWideScope.ql diff --git a/cpp/ql/test/experimental/query-tests/Security/CWE/CWE-1126/semmle/tests/test.c b/cpp/ql/test/experimental/query-tests/Security/CWE/CWE-1126/semmle/tests/test.c new file mode 100644 index 00000000000..090bed34d45 --- /dev/null +++ b/cpp/ql/test/experimental/query-tests/Security/CWE/CWE-1126/semmle/tests/test.c @@ -0,0 +1,15 @@ +void workFunction_0(char *s) { + int intIndex = 10; + char buf[80]; + while(intIndex > 2) // GOOD + { + buf[intIndex] = 1; + intIndex--; + } + while(intIndex > 2) + { + buf[intIndex] = 1; + int intIndex; // BAD + intIndex--; + } +} From 044c92016bd89438f8ba135a65717f13c6d52d7b Mon Sep 17 00:00:00 2001 From: Tom Hvitved Date: Fri, 23 Apr 2021 11:52:26 +0200 Subject: [PATCH 005/168] Data flow: Cache enclosing callable predicates --- .../dataflow/internal/DataFlowImplCommon.qll | 18 +++++++++++++----- 1 file changed, 13 insertions(+), 5 deletions(-) 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 a51c20c2288..b5cc2438456 100644 --- a/csharp/ql/src/semmle/code/csharp/dataflow/internal/DataFlowImplCommon.qll +++ b/csharp/ql/src/semmle/code/csharp/dataflow/internal/DataFlowImplCommon.qll @@ -242,6 +242,14 @@ private DataFlowCallable viableCallableExt(DataFlowCall call) { cached private module Cached { + cached + predicate nodeEnclosingCallable(Node n, DataFlowCallable c) { c = n.getEnclosingCallable() } + + cached + predicate callEnclosingCallable(DataFlowCall call, DataFlowCallable c) { + c = call.getEnclosingCallable() + } + /** * Gets a viable target for the lambda call `call`. * @@ -553,7 +561,7 @@ private module Cached { private predicate mayBenefitFromCallContextExt(DataFlowCall call, DataFlowCallable callable) { mayBenefitFromCallContext(call, callable) or - callable = call.getEnclosingCallable() and + callEnclosingCallable(call, callable) and exists(viableCallableLambda(call, TDataFlowCallSome(_))) } @@ -611,7 +619,7 @@ private module Cached { mayBenefitFromCallContextExt(call, _) and c = viableCallableExt(call) and ctxtgts = count(DataFlowCall ctx | c = viableImplInCallContextExt(call, ctx)) and - tgts = strictcount(DataFlowCall ctx | viableCallableExt(ctx) = call.getEnclosingCallable()) and + tgts = strictcount(DataFlowCall ctx | callEnclosingCallable(call, viableCallableExt(ctx))) and ctxtgts < tgts ) } @@ -866,7 +874,7 @@ class CallContextReturn extends CallContextNoCall, TReturn { } override predicate relevantFor(DataFlowCallable callable) { - exists(DataFlowCall call | this = TReturn(_, call) and call.getEnclosingCallable() = callable) + exists(DataFlowCall call | this = TReturn(_, call) and callEnclosingCallable(call, callable)) } } @@ -1017,7 +1025,7 @@ pragma[inline] DataFlowCallable getNodeEnclosingCallable(Node n) { exists(Node n0 | pragma[only_bind_into](n0) = n and - pragma[only_bind_into](result) = n0.getEnclosingCallable() + nodeEnclosingCallable(n0, pragma[only_bind_into](result)) ) } @@ -1042,7 +1050,7 @@ predicate resolveReturn(CallContext cc, DataFlowCallable callable, DataFlowCall cc instanceof CallContextAny and callable = viableCallableExt(call) or exists(DataFlowCallable c0, DataFlowCall call0 | - call0.getEnclosingCallable() = callable and + callEnclosingCallable(call0, callable) and cc = TReturn(c0, call0) and c0 = prunedViableImplInCallContextReverse(call0, call) ) From 1a56f0b79ce8edf7f7d577e599e49c2f51cdf32b Mon Sep 17 00:00:00 2001 From: Tom Hvitved Date: Fri, 23 Apr 2021 11:58:00 +0200 Subject: [PATCH 006/168] Data flow: Cache `getNodeType` --- .../csharp/dataflow/internal/DataFlowImpl.qll | 28 ++++++----- .../dataflow/internal/DataFlowImplCommon.qll | 48 +++++++++++-------- 2 files changed, 42 insertions(+), 34 deletions(-) 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 9498e51e7e6..4dbf1b0cdaa 100644 --- a/csharp/ql/src/semmle/code/csharp/dataflow/internal/DataFlowImpl.qll +++ b/csharp/ql/src/semmle/code/csharp/dataflow/internal/DataFlowImpl.qll @@ -1327,11 +1327,11 @@ private module LocalFlowBigStep { ( localFlowStepNodeCand1(node1, node2, config) and preservesValue = true and - t = getNodeType(node1) + t = getNodeDataFlowType(node1) or additionalLocalFlowStepNodeCand2(node1, node2, config) and preservesValue = false and - t = getNodeType(node2) + t = getNodeDataFlowType(node2) ) and node1 != node2 and cc.relevantFor(getNodeEnclosingCallable(node1)) and @@ -1350,7 +1350,7 @@ private module LocalFlowBigStep { additionalLocalFlowStepNodeCand2(mid, node2, config) and not mid instanceof FlowCheckNode and preservesValue = false and - t = getNodeType(node2) and + t = getNodeDataFlowType(node2) and Stage2::revFlow(node2, pragma[only_bind_into](config)) ) ) @@ -1384,7 +1384,7 @@ private module Stage3 { private ApApprox getApprox(Ap ap) { result = ap.toBoolNonEmpty() } private ApNil getApNil(Node node) { - PrevStage::revFlow(node, _) and result = TFrontNil(getNodeType(node)) + PrevStage::revFlow(node, _) and result = TFrontNil(getNodeDataFlowType(node)) } bindingset[tc, tail] @@ -1443,7 +1443,9 @@ private module Stage3 { bindingset[node, ap] private predicate filter(Node node, Ap ap) { not ap.isClearedAt(node) and - if node instanceof CastingNode then compatibleTypes(getNodeType(node), ap.getType()) else any() + if node instanceof CastingNode + then compatibleTypes(getNodeDataFlowType(node), ap.getType()) + else any() } bindingset[ap, contentType] @@ -2088,7 +2090,7 @@ private module Stage4 { private ApApprox getApprox(Ap ap) { result = ap.getFront() } private ApNil getApNil(Node node) { - PrevStage::revFlow(node, _) and result = TNil(getNodeType(node)) + PrevStage::revFlow(node, _) and result = TNil(getNodeDataFlowType(node)) } bindingset[tc, tail] @@ -2758,7 +2760,7 @@ private newtype TPathNode = config.isSource(node) and cc instanceof CallContextAny and sc instanceof SummaryCtxNone and - ap = TAccessPathNil(getNodeType(node)) + ap = TAccessPathNil(getNodeDataFlowType(node)) or // ... or a step from an existing PathNode to another node. exists(PathNodeMid mid | @@ -3148,7 +3150,7 @@ private predicate pathStep(PathNodeMid mid, Node node, CallContext cc, SummaryCt cc instanceof CallContextAny and sc instanceof SummaryCtxNone and mid.getAp() instanceof AccessPathNil and - ap = TAccessPathNil(getNodeType(node)) + ap = TAccessPathNil(getNodeDataFlowType(node)) or exists(TypedContent tc | pathStoreStep(mid, node, ap.pop(tc), tc, cc)) and sc = mid.getSummaryCtx() @@ -3591,7 +3593,7 @@ private module FlowExploration { cc instanceof CallContextAny and sc1 = TSummaryCtx1None() and sc2 = TSummaryCtx2None() and - ap = TPartialNil(getNodeType(node)) and + ap = TPartialNil(getNodeDataFlowType(node)) and not fullBarrier(node, config) and exists(config.explorationLimit()) or @@ -3627,7 +3629,7 @@ private module FlowExploration { not fullBarrier(node, config) and not clearsContent(node, ap.getHead().getContent()) and if node instanceof CastingNode - then compatibleTypes(getNodeType(node), ap.getType()) + then compatibleTypes(getNodeDataFlowType(node), ap.getType()) else any() ) } @@ -3797,7 +3799,7 @@ private module FlowExploration { sc1 = mid.getSummaryCtx1() and sc2 = mid.getSummaryCtx2() and mid.getAp() instanceof PartialAccessPathNil and - ap = TPartialNil(getNodeType(node)) and + ap = TPartialNil(getNodeDataFlowType(node)) and config = mid.getConfiguration() ) or @@ -3813,7 +3815,7 @@ private module FlowExploration { sc1 = TSummaryCtx1None() and sc2 = TSummaryCtx2None() and mid.getAp() instanceof PartialAccessPathNil and - ap = TPartialNil(getNodeType(node)) and + ap = TPartialNil(getNodeDataFlowType(node)) and config = mid.getConfiguration() or partialPathStoreStep(mid, _, _, node, ap) and @@ -3827,7 +3829,7 @@ private module FlowExploration { sc1 = mid.getSummaryCtx1() and sc2 = mid.getSummaryCtx2() and apConsFwd(ap, tc, ap0, config) and - compatibleTypes(ap.getType(), getNodeType(node)) + compatibleTypes(ap.getType(), getNodeDataFlowType(node)) ) or partialPathIntoCallable(mid, node, _, cc, sc1, sc2, _, ap, config) 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 b5cc2438456..a6f04cb8ce9 100644 --- a/csharp/ql/src/semmle/code/csharp/dataflow/internal/DataFlowImplCommon.qll +++ b/csharp/ql/src/semmle/code/csharp/dataflow/internal/DataFlowImplCommon.qll @@ -119,7 +119,7 @@ private module LambdaFlow { ) { revLambdaFlow0(lambdaCall, kind, node, t, toReturn, toJump, lastCall) and if node instanceof CastNode or node instanceof ArgumentNode or node instanceof ReturnNode - then compatibleTypes(t, getNodeType(node)) + then compatibleTypes(t, getNodeDataFlowType(node)) else any() } @@ -129,7 +129,7 @@ private module LambdaFlow { boolean toJump, DataFlowCallOption lastCall ) { lambdaCall(lambdaCall, kind, node) and - t = getNodeType(node) and + t = getNodeDataFlowType(node) and toReturn = false and toJump = false and lastCall = TDataFlowCallNone() @@ -146,7 +146,7 @@ private module LambdaFlow { getNodeEnclosingCallable(node) = getNodeEnclosingCallable(mid) | preservesValue = false and - t = getNodeType(node) + t = getNodeDataFlowType(node) or preservesValue = true and t = t0 @@ -168,7 +168,7 @@ private module LambdaFlow { getNodeEnclosingCallable(node) != getNodeEnclosingCallable(mid) | preservesValue = false and - t = getNodeType(node) + t = getNodeDataFlowType(node) or preservesValue = true and t = t0 @@ -250,6 +250,9 @@ private module Cached { c = call.getEnclosingCallable() } + cached + predicate nodeDataFlowType(Node n, DataFlowType t) { t = getNodeType(n) } + /** * Gets a viable target for the lambda call `call`. * @@ -282,7 +285,7 @@ private module Cached { exists(int i | viableParam(call, i, p) and arg.argumentOf(call, i) and - compatibleTypes(getNodeType(arg), getNodeType(p)) + compatibleTypes(getNodeDataFlowType(arg), getNodeDataFlowType(p)) ) } @@ -430,10 +433,10 @@ private module Cached { then // normal flow through read = TReadStepTypesNone() and - compatibleTypes(getNodeType(p), getNodeType(node)) + compatibleTypes(getNodeDataFlowType(p), getNodeDataFlowType(node)) or // getter - compatibleTypes(read.getContentType(), getNodeType(node)) + compatibleTypes(read.getContentType(), getNodeDataFlowType(node)) else any() } @@ -455,7 +458,7 @@ private module Cached { readStepWithTypes(mid, read.getContainerType(), read.getContent(), node, read.getContentType()) and Cand::parameterValueFlowReturnCand(p, _, true) and - compatibleTypes(getNodeType(p), read.getContainerType()) + compatibleTypes(getNodeDataFlowType(p), read.getContainerType()) ) or parameterValueFlow0_0(TReadStepTypesNone(), p, node, read) @@ -511,11 +514,11 @@ private module Cached { | // normal flow through read = TReadStepTypesNone() and - compatibleTypes(getNodeType(arg), getNodeType(out)) + compatibleTypes(getNodeDataFlowType(arg), getNodeDataFlowType(out)) or // getter - compatibleTypes(getNodeType(arg), read.getContainerType()) and - compatibleTypes(read.getContentType(), getNodeType(out)) + compatibleTypes(getNodeDataFlowType(arg), read.getContainerType()) and + compatibleTypes(read.getContentType(), getNodeDataFlowType(out)) ) } @@ -653,8 +656,8 @@ private module Cached { ) { storeStep(node1, c, node2) and readStep(_, c, _) and - contentType = getNodeType(node1) and - containerType = getNodeType(node2) + contentType = getNodeDataFlowType(node1) and + containerType = getNodeDataFlowType(node2) or exists(Node n1, Node n2 | n1 = node1.(PostUpdateNode).getPreUpdateNode() and @@ -663,8 +666,8 @@ private module Cached { argumentValueFlowsThrough(n2, TReadStepTypesSome(containerType, c, contentType), n1) or readStep(n2, c, n1) and - contentType = getNodeType(n1) and - containerType = getNodeType(n2) + contentType = getNodeDataFlowType(n1) and + containerType = getNodeDataFlowType(n2) ) } @@ -784,8 +787,8 @@ private predicate readStepWithTypes( Node n1, DataFlowType container, Content c, Node n2, DataFlowType content ) { readStep(n1, c, n2) and - container = getNodeType(n1) and - content = getNodeType(n2) + container = getNodeDataFlowType(n1) and + content = getNodeDataFlowType(n2) } private newtype TReadStepTypesOption = @@ -1023,10 +1026,13 @@ class ReturnPosition extends TReturnPosition0 { */ pragma[inline] DataFlowCallable getNodeEnclosingCallable(Node n) { - exists(Node n0 | - pragma[only_bind_into](n0) = n and - nodeEnclosingCallable(n0, pragma[only_bind_into](result)) - ) + nodeEnclosingCallable(pragma[only_bind_out](n), pragma[only_bind_into](result)) +} + +/** Gets the type of `n` used for type pruning. */ +pragma[inline] +DataFlowType getNodeDataFlowType(Node n) { + nodeDataFlowType(pragma[only_bind_out](n), pragma[only_bind_into](result)) } pragma[noinline] From 8bfeae768faac60bcdc8de1cc59664d1982e728a Mon Sep 17 00:00:00 2001 From: Tom Hvitved Date: Fri, 23 Apr 2021 13:16:54 +0200 Subject: [PATCH 007/168] Data flow: Cache `simpleLocalFlowStep` --- .../code/csharp/dataflow/internal/DataFlowImpl.qll | 5 +---- .../csharp/dataflow/internal/DataFlowImplCommon.qll | 11 +++++++++-- 2 files changed, 10 insertions(+), 6 deletions(-) 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 4dbf1b0cdaa..7bf05143cfa 100644 --- a/csharp/ql/src/semmle/code/csharp/dataflow/internal/DataFlowImpl.qll +++ b/csharp/ql/src/semmle/code/csharp/dataflow/internal/DataFlowImpl.qll @@ -211,10 +211,7 @@ private predicate fullBarrier(Node node, Configuration config) { * Holds if data can flow in one local step from `node1` to `node2`. */ private predicate localFlowStep(Node node1, Node node2, Configuration config) { - ( - simpleLocalFlowStep(node1, node2) or - reverseStepThroughInputOutputAlias(node1, node2) - ) and + simpleLocalFlowStepExt(node1, node2) and not outBarrier(node1, config) and not inBarrier(node2, config) and not fullBarrier(node1, config) and 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 a6f04cb8ce9..87034b74fde 100644 --- a/csharp/ql/src/semmle/code/csharp/dataflow/internal/DataFlowImplCommon.qll +++ b/csharp/ql/src/semmle/code/csharp/dataflow/internal/DataFlowImplCommon.qll @@ -689,8 +689,9 @@ private module Cached { * are aliases. A typical example is a function returning `this`, implementing a fluent * interface. */ - cached - predicate reverseStepThroughInputOutputAlias(PostUpdateNode fromNode, PostUpdateNode toNode) { + private predicate reverseStepThroughInputOutputAlias( + PostUpdateNode fromNode, PostUpdateNode toNode + ) { exists(Node fromPre, Node toPre | fromPre = fromNode.getPreUpdateNode() and toPre = toNode.getPreUpdateNode() @@ -707,6 +708,12 @@ private module Cached { ) } + cached + predicate simpleLocalFlowStepExt(Node node1, Node node2) { + simpleLocalFlowStep(node1, node2) or + reverseStepThroughInputOutputAlias(node1, node2) + } + /** * Holds if the call context `call` either improves virtual dispatch in * `callable` or if it allows us to prune unreachable nodes in `callable`. From 96aa1828935984adfdaa6b50abc566c79f94e79f Mon Sep 17 00:00:00 2001 From: Tom Hvitved Date: Fri, 23 Apr 2021 13:22:37 +0200 Subject: [PATCH 008/168] Data flow: Cache `jumpStep` --- .../semmle/code/csharp/dataflow/internal/DataFlowImpl.qll | 2 +- .../code/csharp/dataflow/internal/DataFlowImplCommon.qll | 5 ++++- 2 files changed, 5 insertions(+), 2 deletions(-) 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 7bf05143cfa..938a7b3fe2f 100644 --- a/csharp/ql/src/semmle/code/csharp/dataflow/internal/DataFlowImpl.qll +++ b/csharp/ql/src/semmle/code/csharp/dataflow/internal/DataFlowImpl.qll @@ -234,7 +234,7 @@ private predicate additionalLocalFlowStep(Node node1, Node node2, Configuration * Holds if data can flow from `node1` to `node2` in a way that discards call contexts. */ private predicate jumpStep(Node node1, Node node2, Configuration config) { - jumpStep(node1, node2) and + jumpStepCached(node1, node2) and not outBarrier(node1, config) and not inBarrier(node2, config) and not fullBarrier(node1, config) and 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 87034b74fde..32e0f913015 100644 --- a/csharp/ql/src/semmle/code/csharp/dataflow/internal/DataFlowImplCommon.qll +++ b/csharp/ql/src/semmle/code/csharp/dataflow/internal/DataFlowImplCommon.qll @@ -160,7 +160,7 @@ private module LambdaFlow { toJump = true and lastCall = TDataFlowCallNone() | - jumpStep(node, mid) and + jumpStepCached(node, mid) and t = t0 or exists(boolean preservesValue | @@ -253,6 +253,9 @@ private module Cached { cached predicate nodeDataFlowType(Node n, DataFlowType t) { t = getNodeType(n) } + cached + predicate jumpStepCached(Node node1, Node node2) { jumpStep(node1, node2) } + /** * Gets a viable target for the lambda call `call`. * From 4009c01558472839f32c006507505749bf864a57 Mon Sep 17 00:00:00 2001 From: Tom Hvitved Date: Fri, 23 Apr 2021 15:52:22 +0200 Subject: [PATCH 009/168] Data flow: Cache `readStep` --- .../dataflow/internal/DataFlowImplCommon.qll | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) 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 32e0f913015..3a245cda11a 100644 --- a/csharp/ql/src/semmle/code/csharp/dataflow/internal/DataFlowImplCommon.qll +++ b/csharp/ql/src/semmle/code/csharp/dataflow/internal/DataFlowImplCommon.qll @@ -339,7 +339,7 @@ private module Cached { // read exists(Node mid | parameterValueFlowCand(p, mid, false) and - readStep(mid, _, node) and + read(mid, _, node) and read = true ) or @@ -658,7 +658,7 @@ private module Cached { Node node1, Content c, Node node2, DataFlowType contentType, DataFlowType containerType ) { storeStep(node1, c, node2) and - readStep(_, c, _) and + read(_, c, _) and contentType = getNodeDataFlowType(node1) and containerType = getNodeDataFlowType(node2) or @@ -668,12 +668,15 @@ private module Cached { | argumentValueFlowsThrough(n2, TReadStepTypesSome(containerType, c, contentType), n1) or - readStep(n2, c, n1) and + read(n2, c, n1) and contentType = getNodeDataFlowType(n1) and containerType = getNodeDataFlowType(n2) ) } + cached + predicate read(Node node1, Content c, Node node2) { readStep(node1, c, node2) } + /** * Holds if data can flow from `node1` to `node2` via a direct assignment to * `f`. @@ -789,14 +792,14 @@ class CastingNode extends Node { // For reads, `x.f`, we want to check that the tracked type after the read (which // is obtained by popping the head of the access path stack) is compatible with // the type of `x.f`. - readStep(_, _, this) + read(_, _, this) } } private predicate readStepWithTypes( Node n1, DataFlowType container, Content c, Node n2, DataFlowType content ) { - readStep(n1, c, n2) and + read(n1, c, n2) and container = getNodeDataFlowType(n1) and content = getNodeDataFlowType(n2) } @@ -1087,8 +1090,6 @@ DataFlowCallable resolveCall(DataFlowCall call, CallContext cc) { result = viableCallableExt(call) and cc instanceof CallContextReturn } -predicate read = readStep/3; - /** An optional Boolean value. */ class BooleanOption extends TBooleanOption { string toString() { From 1bf0e01a83c224632864d4825f5d9f67b69866d0 Mon Sep 17 00:00:00 2001 From: Tom Hvitved Date: Fri, 23 Apr 2021 15:55:52 +0200 Subject: [PATCH 010/168] Data flow: Cache `clearsContent` --- .../semmle/code/csharp/dataflow/internal/DataFlowImpl.qll | 6 +++--- .../code/csharp/dataflow/internal/DataFlowImplCommon.qll | 5 ++++- 2 files changed, 7 insertions(+), 4 deletions(-) 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 938a7b3fe2f..4099043f4d8 100644 --- a/csharp/ql/src/semmle/code/csharp/dataflow/internal/DataFlowImpl.qll +++ b/csharp/ql/src/semmle/code/csharp/dataflow/internal/DataFlowImpl.qll @@ -1258,7 +1258,7 @@ private module LocalFlowBigStep { private class FlowCheckNode extends Node { FlowCheckNode() { this instanceof CastNode or - clearsContent(this, _) + clearsContentCached(this, _) } } @@ -3610,7 +3610,7 @@ private module FlowExploration { or exists(PartialPathNodeRev mid | revPartialPathStep(mid, node, sc1, sc2, ap, config) and - not clearsContent(node, ap.getHead()) and + not clearsContentCached(node, ap.getHead()) and not fullBarrier(node, config) and distSink(getNodeEnclosingCallable(node), config) <= config.explorationLimit() ) @@ -3624,7 +3624,7 @@ private module FlowExploration { exists(PartialPathNodeFwd mid | partialPathStep(mid, node, cc, sc1, sc2, ap, config) and not fullBarrier(node, config) and - not clearsContent(node, ap.getHead().getContent()) and + not clearsContentCached(node, ap.getHead().getContent()) and if node instanceof CastingNode then compatibleTypes(getNodeDataFlowType(node), ap.getType()) else any() 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 3a245cda11a..e8260dd3c8c 100644 --- a/csharp/ql/src/semmle/code/csharp/dataflow/internal/DataFlowImplCommon.qll +++ b/csharp/ql/src/semmle/code/csharp/dataflow/internal/DataFlowImplCommon.qll @@ -256,6 +256,9 @@ private module Cached { cached predicate jumpStepCached(Node node1, Node node2) { jumpStep(node1, node2) } + cached + predicate clearsContentCached(Node n, Content c) { clearsContent(n, c) } + /** * Gets a viable target for the lambda call `call`. * @@ -1141,7 +1144,7 @@ abstract class AccessPathFront extends TAccessPathFront { TypedContent getHead() { this = TFrontHead(result) } - predicate isClearedAt(Node n) { clearsContent(n, getHead().getContent()) } + predicate isClearedAt(Node n) { clearsContentCached(n, getHead().getContent()) } } class AccessPathFrontNil extends AccessPathFront, TFrontNil { From 23113c4ff7002828421630e34febca601341a826 Mon Sep 17 00:00:00 2001 From: Tom Hvitved Date: Fri, 23 Apr 2021 16:11:08 +0200 Subject: [PATCH 011/168] Data flow: Cache `isUnreachableInCall` --- .../code/csharp/dataflow/internal/DataFlowImpl.qll | 6 +++--- .../code/csharp/dataflow/internal/DataFlowImplCommon.qll | 9 ++++++--- 2 files changed, 9 insertions(+), 6 deletions(-) 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 4099043f4d8..68fde153549 100644 --- a/csharp/ql/src/semmle/code/csharp/dataflow/internal/DataFlowImpl.qll +++ b/csharp/ql/src/semmle/code/csharp/dataflow/internal/DataFlowImpl.qll @@ -1318,7 +1318,7 @@ private module LocalFlowBigStep { Node node1, Node node2, boolean preservesValue, DataFlowType t, Configuration config, LocalCallContext cc ) { - not isUnreachableInCall(node2, cc.(LocalCallContextSpecificCall).getCall()) and + not isUnreachableInCallCached(node2, cc.(LocalCallContextSpecificCall).getCall()) and ( localFlowEntry(node1, pragma[only_bind_into](config)) and ( @@ -1332,7 +1332,7 @@ private module LocalFlowBigStep { ) and node1 != node2 and cc.relevantFor(getNodeEnclosingCallable(node1)) and - not isUnreachableInCall(node1, cc.(LocalCallContextSpecificCall).getCall()) and + not isUnreachableInCallCached(node1, cc.(LocalCallContextSpecificCall).getCall()) and Stage2::revFlow(node2, pragma[only_bind_into](config)) or exists(Node mid | @@ -3782,7 +3782,7 @@ private module FlowExploration { PartialPathNodeFwd mid, Node node, CallContext cc, TSummaryCtx1 sc1, TSummaryCtx2 sc2, PartialAccessPath ap, Configuration config ) { - not isUnreachableInCall(node, cc.(CallContextSpecificCall).getCall()) and + not isUnreachableInCallCached(node, cc.(CallContextSpecificCall).getCall()) and ( localFlowStep(mid.getNode(), node, config) and cc = mid.getCallContext() and 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 e8260dd3c8c..f3b005ff148 100644 --- a/csharp/ql/src/semmle/code/csharp/dataflow/internal/DataFlowImplCommon.qll +++ b/csharp/ql/src/semmle/code/csharp/dataflow/internal/DataFlowImplCommon.qll @@ -259,6 +259,9 @@ private module Cached { cached predicate clearsContentCached(Node n, Content c) { clearsContent(n, c) } + cached + predicate isUnreachableInCallCached(Node n, DataFlowCall call) { isUnreachableInCall(n, call) } + /** * Gets a viable target for the lambda call `call`. * @@ -731,7 +734,7 @@ private module Cached { predicate recordDataFlowCallSite(DataFlowCall call, DataFlowCallable callable) { reducedViableImplInCallContext(_, callable, call) or - exists(Node n | getNodeEnclosingCallable(n) = callable | isUnreachableInCall(n, call)) + exists(Node n | getNodeEnclosingCallable(n) = callable | isUnreachableInCallCached(n, call)) } cached @@ -753,7 +756,7 @@ private module Cached { cached newtype TLocalFlowCallContext = TAnyLocalCall() or - TSpecificLocalCall(DataFlowCall call) { isUnreachableInCall(_, call) } + TSpecificLocalCall(DataFlowCall call) { isUnreachableInCallCached(_, call) } cached newtype TReturnKindExt = @@ -926,7 +929,7 @@ class LocalCallContextSpecificCall extends LocalCallContext, TSpecificLocalCall } private predicate relevantLocalCCtx(DataFlowCall call, DataFlowCallable callable) { - exists(Node n | getNodeEnclosingCallable(n) = callable and isUnreachableInCall(n, call)) + exists(Node n | getNodeEnclosingCallable(n) = callable and isUnreachableInCallCached(n, call)) } /** From 9738de2cb9487c52580dc595bea0f8efbd3066fd Mon Sep 17 00:00:00 2001 From: Tom Hvitved Date: Mon, 26 Apr 2021 15:28:20 +0200 Subject: [PATCH 012/168] Data flow: Cache `OutNodeExt` --- .../dataflow/internal/DataFlowImplCommon.qll | 36 ++++++++++--------- 1 file changed, 19 insertions(+), 17 deletions(-) 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 f3b005ff148..d6fbadd21f0 100644 --- a/csharp/ql/src/semmle/code/csharp/dataflow/internal/DataFlowImplCommon.qll +++ b/csharp/ql/src/semmle/code/csharp/dataflow/internal/DataFlowImplCommon.qll @@ -262,6 +262,23 @@ private module Cached { cached predicate isUnreachableInCallCached(Node n, DataFlowCall call) { isUnreachableInCall(n, call) } + cached + predicate outNodeExt(Node n) { + n instanceof OutNode + or + n.(PostUpdateNode).getPreUpdateNode() instanceof ArgumentNode + } + + cached + OutNodeExt getAnOutNodeExt(DataFlowCall call, ReturnKindExt k) { + result = getAnOutNode(call, k.(ValueReturnKind).getKind()) + or + exists(ArgumentNode arg | + result.(PostUpdateNode).getPreUpdateNode() = arg and + arg.argumentOf(call, k.(ParamUpdateReturnKind).getPosition()) + ) + } + /** * Gets a viable target for the lambda call `call`. * @@ -970,11 +987,7 @@ class ReturnNodeExt extends Node { * or a post-update node associated with a call argument. */ class OutNodeExt extends Node { - OutNodeExt() { - this instanceof OutNode - or - this.(PostUpdateNode).getPreUpdateNode() instanceof ArgumentNode - } + OutNodeExt() { outNodeExt(this) } } /** @@ -987,7 +1000,7 @@ abstract class ReturnKindExt extends TReturnKindExt { abstract string toString(); /** Gets a node corresponding to data flow out of `call`. */ - abstract OutNodeExt getAnOutNode(DataFlowCall call); + final OutNodeExt getAnOutNode(DataFlowCall call) { result = getAnOutNodeExt(call, this) } } class ValueReturnKind extends ReturnKindExt, TValueReturn { @@ -998,10 +1011,6 @@ class ValueReturnKind extends ReturnKindExt, TValueReturn { ReturnKind getKind() { result = kind } override string toString() { result = kind.toString() } - - override OutNodeExt getAnOutNode(DataFlowCall call) { - result = getAnOutNode(call, this.getKind()) - } } class ParamUpdateReturnKind extends ReturnKindExt, TParamUpdate { @@ -1012,13 +1021,6 @@ class ParamUpdateReturnKind extends ReturnKindExt, TParamUpdate { int getPosition() { result = pos } override string toString() { result = "param update " + pos } - - override OutNodeExt getAnOutNode(DataFlowCall call) { - exists(ArgumentNode arg | - result.(PostUpdateNode).getPreUpdateNode() = arg and - arg.argumentOf(call, this.getPosition()) - ) - } } /** A callable tagged with a relevant return kind. */ From 346af4f97adf1c650ee4168352667c3de867bd69 Mon Sep 17 00:00:00 2001 From: Tom Hvitved Date: Mon, 26 Apr 2021 15:30:01 +0200 Subject: [PATCH 013/168] Data flow: Cache `ReturnNodeExt` --- .../dataflow/internal/DataFlowImplCommon.qll | 29 +++++++++---------- 1 file changed, 14 insertions(+), 15 deletions(-) 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 d6fbadd21f0..c81af6cde57 100644 --- a/csharp/ql/src/semmle/code/csharp/dataflow/internal/DataFlowImplCommon.qll +++ b/csharp/ql/src/semmle/code/csharp/dataflow/internal/DataFlowImplCommon.qll @@ -279,6 +279,17 @@ private module Cached { ) } + cached + predicate returnNodeExt(Node n, ReturnKindExt k) { + k = TValueReturn(n.(ReturnNode).getKind()) + or + exists(ParameterNode p, int pos | + parameterValueFlowsToPreUpdate(p, n) and + p.isParameterOf(_, pos) and + k = TParamUpdate(pos) + ) + } + /** * Gets a viable target for the lambda call `call`. * @@ -672,8 +683,7 @@ private module Cached { * Holds if `p` can flow to the pre-update node associated with post-update * node `n`, in the same callable, using only value-preserving steps. */ - cached - predicate parameterValueFlowsToPreUpdate(ParameterNode p, PostUpdateNode n) { + private predicate parameterValueFlowsToPreUpdate(ParameterNode p, PostUpdateNode n) { parameterValueFlow(p, n.getPreUpdateNode(), TReadStepTypesNone()) } @@ -965,21 +975,10 @@ LocalCallContext getLocalCallContext(CallContext ctx, DataFlowCallable callable) * `ReturnNode` or a `PostUpdateNode` corresponding to the value of a parameter. */ class ReturnNodeExt extends Node { - ReturnNodeExt() { - this instanceof ReturnNode or - parameterValueFlowsToPreUpdate(_, this) - } + ReturnNodeExt() { returnNodeExt(this, _) } /** Gets the kind of this returned value. */ - ReturnKindExt getKind() { - result = TValueReturn(this.(ReturnNode).getKind()) - or - exists(ParameterNode p, int pos | - parameterValueFlowsToPreUpdate(p, this) and - p.isParameterOf(_, pos) and - result = TParamUpdate(pos) - ) - } + ReturnKindExt getKind() { returnNodeExt(this, result) } } /** From ade99c2c2b104d7b5cc5295553a8e82da328bcae Mon Sep 17 00:00:00 2001 From: Tom Hvitved Date: Mon, 26 Apr 2021 15:37:43 +0200 Subject: [PATCH 014/168] Data flow: Cache `Cast(ing)Node` --- .../csharp/dataflow/internal/DataFlowImpl.qll | 2 +- .../dataflow/internal/DataFlowImplCommon.qll | 26 ++++++++++++------- 2 files changed, 17 insertions(+), 11 deletions(-) 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 68fde153549..242d5444d38 100644 --- a/csharp/ql/src/semmle/code/csharp/dataflow/internal/DataFlowImpl.qll +++ b/csharp/ql/src/semmle/code/csharp/dataflow/internal/DataFlowImpl.qll @@ -1257,7 +1257,7 @@ private module LocalFlowBigStep { */ private class FlowCheckNode extends Node { FlowCheckNode() { - this instanceof CastNode or + castNode(this) or clearsContentCached(this, _) } } 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 c81af6cde57..d9e875ee281 100644 --- a/csharp/ql/src/semmle/code/csharp/dataflow/internal/DataFlowImplCommon.qll +++ b/csharp/ql/src/semmle/code/csharp/dataflow/internal/DataFlowImplCommon.qll @@ -118,7 +118,7 @@ private module LambdaFlow { boolean toJump, DataFlowCallOption lastCall ) { revLambdaFlow0(lambdaCall, kind, node, t, toReturn, toJump, lastCall) and - if node instanceof CastNode or node instanceof ArgumentNode or node instanceof ReturnNode + if castNode(node) or node instanceof ArgumentNode or node instanceof ReturnNode then compatibleTypes(t, getNodeDataFlowType(node)) else any() } @@ -290,6 +290,20 @@ private module Cached { ) } + cached + predicate castNode(Node n) { n instanceof CastNode } + + cached + predicate castingNode(Node n) { + castNode(n) or + n instanceof ParameterNode or + n instanceof OutNodeExt or + // For reads, `x.f`, we want to check that the tracked type after the read (which + // is obtained by popping the head of the access path stack) is compatible with + // the type of `x.f`. + read(_, _, n) + } + /** * Gets a viable target for the lambda call `call`. * @@ -818,15 +832,7 @@ private module Cached { * A `Node` at which a cast can occur such that the type should be checked. */ class CastingNode extends Node { - CastingNode() { - this instanceof ParameterNode or - this instanceof CastNode or - this instanceof OutNodeExt or - // For reads, `x.f`, we want to check that the tracked type after the read (which - // is obtained by popping the head of the access path stack) is compatible with - // the type of `x.f`. - read(_, _, this) - } + CastingNode() { castingNode(this) } } private predicate readStepWithTypes( From 7d4feaca2f421acd7241621d93e4d357bc721059 Mon Sep 17 00:00:00 2001 From: Tom Hvitved Date: Mon, 26 Apr 2021 15:44:22 +0200 Subject: [PATCH 015/168] Data flow: Cache `ArgumentNode` --- .../csharp/dataflow/internal/DataFlowImpl.qll | 42 +++++++-------- .../dataflow/internal/DataFlowImplCommon.qll | 51 ++++++++++++------- 2 files changed, 53 insertions(+), 40 deletions(-) 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 242d5444d38..53a8ebe2d54 100644 --- a/csharp/ql/src/semmle/code/csharp/dataflow/internal/DataFlowImpl.qll +++ b/csharp/ql/src/semmle/code/csharp/dataflow/internal/DataFlowImpl.qll @@ -385,7 +385,7 @@ private module Stage1 { */ pragma[nomagic] private predicate fwdFlowIsEntered(DataFlowCall call, Cc cc, Configuration config) { - exists(ArgumentNode arg | + exists(ArgumentNodeExt arg | fwdFlow(arg, cc, config) and viableParamArg(call, _, arg) ) @@ -512,7 +512,7 @@ private module Stage1 { pragma[nomagic] predicate viableParamArgNodeCandFwd1( - DataFlowCall call, ParameterNode p, ArgumentNode arg, Configuration config + DataFlowCall call, ParameterNode p, ArgumentNodeExt arg, Configuration config ) { viableParamArg(call, p, arg) and fwdFlow(arg, config) @@ -520,7 +520,7 @@ private module Stage1 { pragma[nomagic] private predicate revFlowIn( - DataFlowCall call, ArgumentNode arg, boolean toReturn, Configuration config + DataFlowCall call, ArgumentNodeExt arg, boolean toReturn, Configuration config ) { exists(ParameterNode p | revFlow(p, toReturn, config) and @@ -529,7 +529,7 @@ private module Stage1 { } pragma[nomagic] - private predicate revFlowInToReturn(DataFlowCall call, ArgumentNode arg, Configuration config) { + private predicate revFlowInToReturn(DataFlowCall call, ArgumentNodeExt arg, Configuration config) { revFlowIn(call, arg, true, config) } @@ -660,7 +660,7 @@ private predicate flowOutOfCallNodeCand1( pragma[nomagic] private predicate viableParamArgNodeCand1( - DataFlowCall call, ParameterNode p, ArgumentNode arg, Configuration config + DataFlowCall call, ParameterNode p, ArgumentNodeExt arg, Configuration config ) { Stage1::viableParamArgNodeCandFwd1(call, p, arg, config) and Stage1::revFlow(arg, config) @@ -672,7 +672,7 @@ private predicate viableParamArgNodeCand1( */ pragma[nomagic] private predicate flowIntoCallNodeCand1( - DataFlowCall call, ArgumentNode arg, ParameterNode p, Configuration config + DataFlowCall call, ArgumentNodeExt arg, ParameterNode p, Configuration config ) { viableParamArgNodeCand1(call, p, arg, config) and Stage1::revFlow(p, config) and @@ -732,7 +732,7 @@ private predicate flowOutOfCallNodeCand1( */ pragma[nomagic] private predicate flowIntoCallNodeCand1( - DataFlowCall call, ArgumentNode arg, ParameterNode p, boolean allowsFieldFlow, + DataFlowCall call, ArgumentNodeExt arg, ParameterNode p, boolean allowsFieldFlow, Configuration config ) { flowIntoCallNodeCand1(call, arg, p, config) and @@ -944,7 +944,7 @@ private module Stage2 { DataFlowCall call, ParameterNode p, Cc outercc, Cc innercc, ApOption argAp, Ap ap, Configuration config ) { - exists(ArgumentNode arg, boolean allowsFieldFlow | + exists(ArgumentNodeExt arg, boolean allowsFieldFlow | fwdFlow(arg, outercc, argAp, ap, config) and flowIntoCall(call, arg, p, allowsFieldFlow, config) and innercc = getCallContextCall(call, getNodeEnclosingCallable(p), outercc) @@ -1130,7 +1130,7 @@ private module Stage2 { pragma[nomagic] private predicate revFlowIn( - DataFlowCall call, ArgumentNode arg, boolean toReturn, ApOption returnAp, Ap ap, + DataFlowCall call, ArgumentNodeExt arg, boolean toReturn, ApOption returnAp, Ap ap, Configuration config ) { exists(ParameterNode p, boolean allowsFieldFlow | @@ -1143,7 +1143,7 @@ private module Stage2 { pragma[nomagic] private predicate revFlowInToReturn( - DataFlowCall call, ArgumentNode arg, Ap returnAp, Ap ap, Configuration config + DataFlowCall call, ArgumentNodeExt arg, Ap returnAp, Ap ap, Configuration config ) { revFlowIn(call, arg, true, apSome(returnAp), ap, config) } @@ -1242,7 +1242,7 @@ private predicate flowOutOfCallNodeCand2( pragma[nomagic] private predicate flowIntoCallNodeCand2( - DataFlowCall call, ArgumentNode node1, ParameterNode node2, boolean allowsFieldFlow, + DataFlowCall call, ArgumentNodeExt node1, ParameterNode node2, boolean allowsFieldFlow, Configuration config ) { flowIntoCallNodeCand1(call, node1, node2, allowsFieldFlow, config) and @@ -1585,7 +1585,7 @@ private module Stage3 { DataFlowCall call, ParameterNode p, Cc outercc, Cc innercc, ApOption argAp, Ap ap, Configuration config ) { - exists(ArgumentNode arg, boolean allowsFieldFlow | + exists(ArgumentNodeExt arg, boolean allowsFieldFlow | fwdFlow(arg, outercc, argAp, ap, config) and flowIntoCall(call, arg, p, allowsFieldFlow, config) and innercc = getCallContextCall(call, getNodeEnclosingCallable(p), outercc) @@ -1771,7 +1771,7 @@ private module Stage3 { pragma[nomagic] private predicate revFlowIn( - DataFlowCall call, ArgumentNode arg, boolean toReturn, ApOption returnAp, Ap ap, + DataFlowCall call, ArgumentNodeExt arg, boolean toReturn, ApOption returnAp, Ap ap, Configuration config ) { exists(ParameterNode p, boolean allowsFieldFlow | @@ -1784,7 +1784,7 @@ private module Stage3 { pragma[nomagic] private predicate revFlowInToReturn( - DataFlowCall call, ArgumentNode arg, Ap returnAp, Ap ap, Configuration config + DataFlowCall call, ArgumentNodeExt arg, Ap returnAp, Ap ap, Configuration config ) { revFlowIn(call, arg, true, apSome(returnAp), ap, config) } @@ -2154,7 +2154,7 @@ private module Stage4 { pragma[nomagic] private predicate flowIntoCall( - DataFlowCall call, ArgumentNode node1, ParameterNode node2, boolean allowsFieldFlow, + DataFlowCall call, ArgumentNodeExt node1, ParameterNode node2, boolean allowsFieldFlow, Configuration config ) { flowIntoCallNodeCand2(call, node1, node2, allowsFieldFlow, config) and @@ -2302,7 +2302,7 @@ private module Stage4 { DataFlowCall call, ParameterNode p, Cc outercc, Cc innercc, ApOption argAp, Ap ap, Configuration config ) { - exists(ArgumentNode arg, boolean allowsFieldFlow | + exists(ArgumentNodeExt arg, boolean allowsFieldFlow | fwdFlow(arg, outercc, argAp, ap, config) and flowIntoCall(call, arg, p, allowsFieldFlow, config) and innercc = getCallContextCall(call, getNodeEnclosingCallable(p), outercc) @@ -2488,7 +2488,7 @@ private module Stage4 { pragma[nomagic] private predicate revFlowIn( - DataFlowCall call, ArgumentNode arg, boolean toReturn, ApOption returnAp, Ap ap, + DataFlowCall call, ArgumentNodeExt arg, boolean toReturn, ApOption returnAp, Ap ap, Configuration config ) { exists(ParameterNode p, boolean allowsFieldFlow | @@ -2501,7 +2501,7 @@ private module Stage4 { pragma[nomagic] private predicate revFlowInToReturn( - DataFlowCall call, ArgumentNode arg, Ap returnAp, Ap ap, Configuration config + DataFlowCall call, ArgumentNodeExt arg, Ap returnAp, Ap ap, Configuration config ) { revFlowIn(call, arg, true, apSome(returnAp), ap, config) } @@ -3234,7 +3234,7 @@ pragma[noinline] private predicate pathIntoArg( PathNodeMid mid, int i, CallContext cc, DataFlowCall call, AccessPath ap, AccessPathApprox apa ) { - exists(ArgumentNode arg | + exists(ArgumentNodeExt arg | arg = mid.getNode() and cc = mid.getCallContext() and arg.argumentOf(call, i) and @@ -3923,7 +3923,7 @@ private module FlowExploration { PartialPathNodeFwd mid, int i, CallContext cc, DataFlowCall call, PartialAccessPath ap, Configuration config ) { - exists(ArgumentNode arg | + exists(ArgumentNodeExt arg | arg = mid.getNode() and cc = mid.getCallContext() and arg.argumentOf(call, i) and @@ -4137,7 +4137,7 @@ private module FlowExploration { pragma[nomagic] private predicate revPartialPathThroughCallable( - PartialPathNodeRev mid, ArgumentNode node, RevPartialAccessPath ap, Configuration config + PartialPathNodeRev mid, ArgumentNodeExt node, RevPartialAccessPath ap, Configuration config ) { exists(DataFlowCall call, int pos | revPartialPathThroughCallable0(call, mid, pos, ap, config) and 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 d9e875ee281..f079d878dec 100644 --- a/csharp/ql/src/semmle/code/csharp/dataflow/internal/DataFlowImplCommon.qll +++ b/csharp/ql/src/semmle/code/csharp/dataflow/internal/DataFlowImplCommon.qll @@ -43,14 +43,14 @@ private module LambdaFlow { p.isParameterOf(viableCallableLambda(call, _), i) } - private predicate viableParamArgNonLambda(DataFlowCall call, ParameterNode p, ArgumentNode arg) { + private predicate viableParamArgNonLambda(DataFlowCall call, ParameterNode p, ArgumentNodeExt arg) { exists(int i | viableParamNonLambda(call, i, p) and arg.argumentOf(call, i) ) } - private predicate viableParamArgLambda(DataFlowCall call, ParameterNode p, ArgumentNode arg) { + private predicate viableParamArgLambda(DataFlowCall call, ParameterNode p, ArgumentNodeExt arg) { exists(int i | viableParamLambda(call, i, p) and arg.argumentOf(call, i) @@ -118,7 +118,7 @@ private module LambdaFlow { boolean toJump, DataFlowCallOption lastCall ) { revLambdaFlow0(lambdaCall, kind, node, t, toReturn, toJump, lastCall) and - if castNode(node) or node instanceof ArgumentNode or node instanceof ReturnNode + if castNode(node) or node instanceof ArgumentNodeExt or node instanceof ReturnNode then compatibleTypes(t, getNodeDataFlowType(node)) else any() } @@ -266,14 +266,14 @@ private module Cached { predicate outNodeExt(Node n) { n instanceof OutNode or - n.(PostUpdateNode).getPreUpdateNode() instanceof ArgumentNode + n.(PostUpdateNode).getPreUpdateNode() instanceof ArgumentNodeExt } cached OutNodeExt getAnOutNodeExt(DataFlowCall call, ReturnKindExt k) { result = getAnOutNode(call, k.(ValueReturnKind).getKind()) or - exists(ArgumentNode arg | + exists(ArgumentNodeExt arg | result.(PostUpdateNode).getPreUpdateNode() = arg and arg.argumentOf(call, k.(ParamUpdateReturnKind).getPosition()) ) @@ -304,6 +304,11 @@ private module Cached { read(_, _, n) } + cached + predicate argumentNode(Node n, DataFlowCall call, int pos) { + n.(ArgumentNode).argumentOf(call, pos) + } + /** * Gets a viable target for the lambda call `call`. * @@ -332,7 +337,7 @@ private module Cached { * dispatch into account. */ cached - predicate viableParamArg(DataFlowCall call, ParameterNode p, ArgumentNode arg) { + predicate viableParamArg(DataFlowCall call, ParameterNode p, ArgumentNodeExt arg) { exists(int i | viableParam(call, i, p) and arg.argumentOf(call, i) and @@ -392,20 +397,20 @@ private module Cached { ) or // flow through: no prior read - exists(ArgumentNode arg | + exists(ArgumentNodeExt arg | parameterValueFlowArgCand(p, arg, false) and argumentValueFlowsThroughCand(arg, node, read) ) or // flow through: no read inside method - exists(ArgumentNode arg | + exists(ArgumentNodeExt arg | parameterValueFlowArgCand(p, arg, read) and argumentValueFlowsThroughCand(arg, node, false) ) } pragma[nomagic] - private predicate parameterValueFlowArgCand(ParameterNode p, ArgumentNode arg, boolean read) { + private predicate parameterValueFlowArgCand(ParameterNode p, ArgumentNodeExt arg, boolean read) { parameterValueFlowCand(p, arg, read) } @@ -431,7 +436,7 @@ private module Cached { pragma[nomagic] private predicate argumentValueFlowsThroughCand0( - DataFlowCall call, ArgumentNode arg, ReturnKind kind, boolean read + DataFlowCall call, ArgumentNodeExt arg, ReturnKind kind, boolean read ) { exists(ParameterNode param | viableParamArg(call, param, arg) | parameterValueFlowReturnCand(param, kind, read) @@ -444,7 +449,7 @@ private module Cached { * * `read` indicates whether it is contents of `arg` that can flow to `out`. */ - predicate argumentValueFlowsThroughCand(ArgumentNode arg, Node out, boolean read) { + predicate argumentValueFlowsThroughCand(ArgumentNodeExt arg, Node out, boolean read) { exists(DataFlowCall call, ReturnKind kind | argumentValueFlowsThroughCand0(call, arg, kind, read) and out = getAnOutNode(call, kind) @@ -520,13 +525,13 @@ private module Cached { ReadStepTypesOption mustBeNone, ParameterNode p, Node node, ReadStepTypesOption read ) { // flow through: no prior read - exists(ArgumentNode arg | + exists(ArgumentNodeExt arg | parameterValueFlowArg(p, arg, mustBeNone) and argumentValueFlowsThrough(arg, read, node) ) or // flow through: no read inside method - exists(ArgumentNode arg | + exists(ArgumentNodeExt arg | parameterValueFlowArg(p, arg, read) and argumentValueFlowsThrough(arg, mustBeNone, node) ) @@ -534,7 +539,7 @@ private module Cached { pragma[nomagic] private predicate parameterValueFlowArg( - ParameterNode p, ArgumentNode arg, ReadStepTypesOption read + ParameterNode p, ArgumentNodeExt arg, ReadStepTypesOption read ) { parameterValueFlow(p, arg, read) and Cand::argumentValueFlowsThroughCand(arg, _, _) @@ -542,7 +547,7 @@ private module Cached { pragma[nomagic] private predicate argumentValueFlowsThrough0( - DataFlowCall call, ArgumentNode arg, ReturnKind kind, ReadStepTypesOption read + DataFlowCall call, ArgumentNodeExt arg, ReturnKind kind, ReadStepTypesOption read ) { exists(ParameterNode param | viableParamArg(call, param, arg) | parameterValueFlowReturn(param, kind, read) @@ -558,7 +563,7 @@ private module Cached { * container type, and the content type. */ pragma[nomagic] - predicate argumentValueFlowsThrough(ArgumentNode arg, ReadStepTypesOption read, Node out) { + predicate argumentValueFlowsThrough(ArgumentNodeExt arg, ReadStepTypesOption read, Node out) { exists(DataFlowCall call, ReturnKind kind | argumentValueFlowsThrough0(call, arg, kind, read) and out = getAnOutNode(call, kind) @@ -578,7 +583,7 @@ private module Cached { * value-preserving steps and a single read step, not taking call * contexts into account, thus representing a getter-step. */ - predicate getterStep(ArgumentNode arg, Content c, Node out) { + predicate getterStep(ArgumentNodeExt arg, Content c, Node out) { argumentValueFlowsThrough(arg, TReadStepTypesSome(_, c, _), out) } @@ -753,8 +758,8 @@ private module Cached { // Does the language-specific simpleLocalFlowStep already model flow // from function input to output? fromPre = getAnOutNode(c, _) and - toPre.(ArgumentNode).argumentOf(c, _) and - simpleLocalFlowStep(toPre.(ArgumentNode), fromPre) + toPre.(ArgumentNodeExt).argumentOf(c, _) and + simpleLocalFlowStep(toPre.(ArgumentNodeExt), fromPre) ) or argumentValueFlowsThrough(toPre, TReadStepTypesNone(), fromPre) @@ -976,6 +981,14 @@ LocalCallContext getLocalCallContext(CallContext ctx, DataFlowCallable callable) else result instanceof LocalCallContextAny } +/** A data-flow node that represents a call argument. */ +class ArgumentNodeExt extends Node { + ArgumentNodeExt() { argumentNode(this, _, _) } + + /** Holds if this argument occurs at the given position in the given call. */ + final predicate argumentOf(DataFlowCall call, int pos) { argumentNode(this, call, pos) } +} + /** * A node from which flow can return to the caller. This is either a regular * `ReturnNode` or a `PostUpdateNode` corresponding to the value of a parameter. From 1112c0f9942f237d362aabd8afc715a5175b949a Mon Sep 17 00:00:00 2001 From: Tom Hvitved Date: Mon, 26 Apr 2021 15:50:00 +0200 Subject: [PATCH 016/168] Data flow: Cache `ParameterNode` --- .../csharp/dataflow/internal/DataFlowImpl.qll | 74 ++++++++++-------- .../dataflow/internal/DataFlowImplCommon.qll | 75 ++++++++++++------- 2 files changed, 90 insertions(+), 59 deletions(-) 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 53a8ebe2d54..ede9390bace 100644 --- a/csharp/ql/src/semmle/code/csharp/dataflow/internal/DataFlowImpl.qll +++ b/csharp/ql/src/semmle/code/csharp/dataflow/internal/DataFlowImpl.qll @@ -512,7 +512,7 @@ private module Stage1 { pragma[nomagic] predicate viableParamArgNodeCandFwd1( - DataFlowCall call, ParameterNode p, ArgumentNodeExt arg, Configuration config + DataFlowCall call, ParameterNodeExt p, ArgumentNodeExt arg, Configuration config ) { viableParamArg(call, p, arg) and fwdFlow(arg, config) @@ -522,7 +522,7 @@ private module Stage1 { private predicate revFlowIn( DataFlowCall call, ArgumentNodeExt arg, boolean toReturn, Configuration config ) { - exists(ParameterNode p | + exists(ParameterNodeExt p | revFlow(p, toReturn, config) and viableParamArgNodeCandFwd1(call, p, arg, config) ) @@ -594,7 +594,9 @@ private module Stage1 { * Holds if flow may enter through `p` and reach a return node making `p` a * candidate for the origin of a summary. */ - predicate parameterMayFlowThrough(ParameterNode p, DataFlowCallable c, Ap ap, Configuration config) { + predicate parameterMayFlowThrough( + ParameterNodeExt p, DataFlowCallable c, Ap ap, Configuration config + ) { exists(ReturnKindExt kind | throughFlowNodeCand(p, config) and returnFlowCallableNodeCand(c, kind, config) and @@ -660,7 +662,7 @@ private predicate flowOutOfCallNodeCand1( pragma[nomagic] private predicate viableParamArgNodeCand1( - DataFlowCall call, ParameterNode p, ArgumentNodeExt arg, Configuration config + DataFlowCall call, ParameterNodeExt p, ArgumentNodeExt arg, Configuration config ) { Stage1::viableParamArgNodeCandFwd1(call, p, arg, config) and Stage1::revFlow(arg, config) @@ -672,7 +674,7 @@ private predicate viableParamArgNodeCand1( */ pragma[nomagic] private predicate flowIntoCallNodeCand1( - DataFlowCall call, ArgumentNodeExt arg, ParameterNode p, Configuration config + DataFlowCall call, ArgumentNodeExt arg, ParameterNodeExt p, Configuration config ) { viableParamArgNodeCand1(call, p, arg, config) and Stage1::revFlow(p, config) and @@ -732,7 +734,7 @@ private predicate flowOutOfCallNodeCand1( */ pragma[nomagic] private predicate flowIntoCallNodeCand1( - DataFlowCall call, ArgumentNodeExt arg, ParameterNode p, boolean allowsFieldFlow, + DataFlowCall call, ArgumentNodeExt arg, ParameterNodeExt p, boolean allowsFieldFlow, Configuration config ) { flowIntoCallNodeCand1(call, arg, p, config) and @@ -941,7 +943,7 @@ private module Stage2 { pragma[nomagic] private predicate fwdFlowIn( - DataFlowCall call, ParameterNode p, Cc outercc, Cc innercc, ApOption argAp, Ap ap, + DataFlowCall call, ParameterNodeExt p, Cc outercc, Cc innercc, ApOption argAp, Ap ap, Configuration config ) { exists(ArgumentNodeExt arg, boolean allowsFieldFlow | @@ -989,7 +991,7 @@ private module Stage2 { private predicate fwdFlowIsEntered( DataFlowCall call, Cc cc, ApOption argAp, Ap ap, Configuration config ) { - exists(ParameterNode p | + exists(ParameterNodeExt p | fwdFlowIn(call, p, cc, _, argAp, ap, config) and PrevStage::parameterMayFlowThrough(p, _, getApprox(ap), config) ) @@ -1133,7 +1135,7 @@ private module Stage2 { DataFlowCall call, ArgumentNodeExt arg, boolean toReturn, ApOption returnAp, Ap ap, Configuration config ) { - exists(ParameterNode p, boolean allowsFieldFlow | + exists(ParameterNodeExt p, boolean allowsFieldFlow | revFlow(p, toReturn, returnAp, ap, config) and flowIntoCall(call, arg, p, allowsFieldFlow, config) | @@ -1196,13 +1198,15 @@ private module Stage2 { pragma[noinline] private predicate parameterFlow( - ParameterNode p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config + ParameterNodeExt p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config ) { revFlow(p, true, apSome(ap0), ap, config) and c = getNodeEnclosingCallable(p) } - predicate parameterMayFlowThrough(ParameterNode p, DataFlowCallable c, Ap ap, Configuration config) { + predicate parameterMayFlowThrough( + ParameterNodeExt p, DataFlowCallable c, Ap ap, Configuration config + ) { exists(ReturnNodeExt ret, Ap ap0, ReturnKindExt kind, int pos | parameterFlow(p, ap, ap0, c, config) and c = getNodeEnclosingCallable(ret) and @@ -1242,7 +1246,7 @@ private predicate flowOutOfCallNodeCand2( pragma[nomagic] private predicate flowIntoCallNodeCand2( - DataFlowCall call, ArgumentNodeExt node1, ParameterNode node2, boolean allowsFieldFlow, + DataFlowCall call, ArgumentNodeExt node1, ParameterNodeExt node2, boolean allowsFieldFlow, Configuration config ) { flowIntoCallNodeCand1(call, node1, node2, allowsFieldFlow, config) and @@ -1272,7 +1276,7 @@ private module LocalFlowBigStep { config.isSource(node) or jumpStep(_, node, config) or additionalJumpStep(_, node, config) or - node instanceof ParameterNode or + node instanceof ParameterNodeExt or node instanceof OutNodeExt or store(_, _, node, _) or read(_, _, node) or @@ -1582,7 +1586,7 @@ private module Stage3 { pragma[nomagic] private predicate fwdFlowIn( - DataFlowCall call, ParameterNode p, Cc outercc, Cc innercc, ApOption argAp, Ap ap, + DataFlowCall call, ParameterNodeExt p, Cc outercc, Cc innercc, ApOption argAp, Ap ap, Configuration config ) { exists(ArgumentNodeExt arg, boolean allowsFieldFlow | @@ -1630,7 +1634,7 @@ private module Stage3 { private predicate fwdFlowIsEntered( DataFlowCall call, Cc cc, ApOption argAp, Ap ap, Configuration config ) { - exists(ParameterNode p | + exists(ParameterNodeExt p | fwdFlowIn(call, p, cc, _, argAp, ap, config) and PrevStage::parameterMayFlowThrough(p, _, unbindApa(getApprox(ap)), config) ) @@ -1774,7 +1778,7 @@ private module Stage3 { DataFlowCall call, ArgumentNodeExt arg, boolean toReturn, ApOption returnAp, Ap ap, Configuration config ) { - exists(ParameterNode p, boolean allowsFieldFlow | + exists(ParameterNodeExt p, boolean allowsFieldFlow | revFlow(p, toReturn, returnAp, ap, config) and flowIntoCall(call, arg, p, allowsFieldFlow, config) | @@ -1837,13 +1841,15 @@ private module Stage3 { pragma[noinline] private predicate parameterFlow( - ParameterNode p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config + ParameterNodeExt p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config ) { revFlow(p, true, apSome(ap0), ap, config) and c = getNodeEnclosingCallable(p) } - predicate parameterMayFlowThrough(ParameterNode p, DataFlowCallable c, Ap ap, Configuration config) { + predicate parameterMayFlowThrough( + ParameterNodeExt p, DataFlowCallable c, Ap ap, Configuration config + ) { exists(ReturnNodeExt ret, Ap ap0, ReturnKindExt kind, int pos | parameterFlow(p, ap, ap0, c, config) and c = getNodeEnclosingCallable(ret) and @@ -2154,7 +2160,7 @@ private module Stage4 { pragma[nomagic] private predicate flowIntoCall( - DataFlowCall call, ArgumentNodeExt node1, ParameterNode node2, boolean allowsFieldFlow, + DataFlowCall call, ArgumentNodeExt node1, ParameterNodeExt node2, boolean allowsFieldFlow, Configuration config ) { flowIntoCallNodeCand2(call, node1, node2, allowsFieldFlow, config) and @@ -2299,7 +2305,7 @@ private module Stage4 { pragma[nomagic] private predicate fwdFlowIn( - DataFlowCall call, ParameterNode p, Cc outercc, Cc innercc, ApOption argAp, Ap ap, + DataFlowCall call, ParameterNodeExt p, Cc outercc, Cc innercc, ApOption argAp, Ap ap, Configuration config ) { exists(ArgumentNodeExt arg, boolean allowsFieldFlow | @@ -2347,7 +2353,7 @@ private module Stage4 { private predicate fwdFlowIsEntered( DataFlowCall call, Cc cc, ApOption argAp, Ap ap, Configuration config ) { - exists(ParameterNode p | + exists(ParameterNodeExt p | fwdFlowIn(call, p, cc, _, argAp, ap, config) and PrevStage::parameterMayFlowThrough(p, _, unbindApa(getApprox(ap)), config) ) @@ -2491,7 +2497,7 @@ private module Stage4 { DataFlowCall call, ArgumentNodeExt arg, boolean toReturn, ApOption returnAp, Ap ap, Configuration config ) { - exists(ParameterNode p, boolean allowsFieldFlow | + exists(ParameterNodeExt p, boolean allowsFieldFlow | revFlow(p, toReturn, returnAp, ap, config) and flowIntoCall(call, arg, p, allowsFieldFlow, config) | @@ -2554,13 +2560,15 @@ private module Stage4 { pragma[noinline] private predicate parameterFlow( - ParameterNode p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config + ParameterNodeExt p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config ) { revFlow(p, true, apSome(ap0), ap, config) and c = getNodeEnclosingCallable(p) } - predicate parameterMayFlowThrough(ParameterNode p, DataFlowCallable c, Ap ap, Configuration config) { + predicate parameterMayFlowThrough( + ParameterNodeExt p, DataFlowCallable c, Ap ap, Configuration config + ) { exists(ReturnNodeExt ret, Ap ap0, ReturnKindExt kind, int pos | parameterFlow(p, ap, ap0, c, config) and c = getNodeEnclosingCallable(ret) and @@ -2605,7 +2613,7 @@ private predicate nodeMayUseSummary(Node n, AccessPathApprox apa, Configuration private newtype TSummaryCtx = TSummaryCtxNone() or - TSummaryCtxSome(ParameterNode p, AccessPath ap) { + TSummaryCtxSome(ParameterNodeExt p, AccessPath ap) { Stage4::parameterMayFlowThrough(p, _, ap.getApprox(), _) } @@ -2626,7 +2634,7 @@ private class SummaryCtxNone extends SummaryCtx, TSummaryCtxNone { /** A summary context from which a flow summary can be generated. */ private class SummaryCtxSome extends SummaryCtx, TSummaryCtxSome { - private ParameterNode p; + private ParameterNodeExt p; private AccessPath ap; SummaryCtxSome() { this = TSummaryCtxSome(p, ap) } @@ -3247,7 +3255,7 @@ pragma[noinline] private predicate parameterCand( DataFlowCallable callable, int i, AccessPathApprox apa, Configuration config ) { - exists(ParameterNode p | + exists(ParameterNodeExt p | Stage4::revFlow(p, _, _, apa, config) and p.isParameterOf(callable, i) ) @@ -3271,7 +3279,7 @@ private predicate pathIntoCallable0( * respectively. */ private predicate pathIntoCallable( - PathNodeMid mid, ParameterNode p, CallContext outercc, CallContextCall innercc, SummaryCtx sc, + PathNodeMid mid, ParameterNodeExt p, CallContext outercc, CallContextCall innercc, SummaryCtx sc, DataFlowCall call ) { exists(int i, DataFlowCallable callable, AccessPath ap | @@ -3567,7 +3575,7 @@ private module FlowExploration { private newtype TSummaryCtx1 = TSummaryCtx1None() or - TSummaryCtx1Param(ParameterNode p) + TSummaryCtx1Param(ParameterNodeExt p) private newtype TSummaryCtx2 = TSummaryCtx2None() or @@ -3942,7 +3950,7 @@ private module FlowExploration { } private predicate partialPathIntoCallable( - PartialPathNodeFwd mid, ParameterNode p, CallContext outercc, CallContextCall innercc, + PartialPathNodeFwd mid, ParameterNodeExt p, CallContext outercc, CallContextCall innercc, TSummaryCtx1 sc1, TSummaryCtx2 sc2, DataFlowCall call, PartialAccessPath ap, Configuration config ) { @@ -3979,7 +3987,7 @@ private module FlowExploration { DataFlowCall call, PartialPathNodeFwd mid, ReturnKindExt kind, CallContext cc, PartialAccessPath ap, Configuration config ) { - exists(ParameterNode p, CallContext innercc, TSummaryCtx1 sc1, TSummaryCtx2 sc2 | + exists(ParameterNodeExt p, CallContext innercc, TSummaryCtx1 sc1, TSummaryCtx2 sc2 | partialPathIntoCallable(mid, p, cc, innercc, sc1, sc2, call, _, config) and paramFlowsThroughInPartialPath(kind, innercc, sc1, sc2, ap, config) ) @@ -4036,7 +4044,7 @@ private module FlowExploration { apConsRev(ap, c, ap0, config) ) or - exists(ParameterNode p | + exists(ParameterNodeExt p | mid.getNode() = p and viableParamArg(_, p, node) and sc1 = mid.getSummaryCtx1() and @@ -4114,7 +4122,7 @@ private module FlowExploration { int pos, TRevSummaryCtx1Some sc1, TRevSummaryCtx2Some sc2, RevPartialAccessPath ap, Configuration config ) { - exists(PartialPathNodeRev mid, ParameterNode p | + exists(PartialPathNodeRev mid, ParameterNodeExt p | mid.getNode() = p and p.isParameterOf(_, pos) and sc1 = mid.getSummaryCtx1() and 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 f079d878dec..a9a19ed322d 100644 --- a/csharp/ql/src/semmle/code/csharp/dataflow/internal/DataFlowImplCommon.qll +++ b/csharp/ql/src/semmle/code/csharp/dataflow/internal/DataFlowImplCommon.qll @@ -35,22 +35,24 @@ predicate accessPathCostLimits(int apLimit, int tupleLimit) { * calls. For this reason, we cannot reuse the code from `DataFlowImpl.qll` directly. */ private module LambdaFlow { - private predicate viableParamNonLambda(DataFlowCall call, int i, ParameterNode p) { + private predicate viableParamNonLambda(DataFlowCall call, int i, ParameterNodeExt p) { p.isParameterOf(viableCallable(call), i) } - private predicate viableParamLambda(DataFlowCall call, int i, ParameterNode p) { + private predicate viableParamLambda(DataFlowCall call, int i, ParameterNodeExt p) { p.isParameterOf(viableCallableLambda(call, _), i) } - private predicate viableParamArgNonLambda(DataFlowCall call, ParameterNode p, ArgumentNodeExt arg) { + private predicate viableParamArgNonLambda( + DataFlowCall call, ParameterNodeExt p, ArgumentNodeExt arg + ) { exists(int i | viableParamNonLambda(call, i, p) and arg.argumentOf(call, i) ) } - private predicate viableParamArgLambda(DataFlowCall call, ParameterNode p, ArgumentNodeExt arg) { + private predicate viableParamArgLambda(DataFlowCall call, ParameterNodeExt p, ArgumentNodeExt arg) { exists(int i | viableParamLambda(call, i, p) and arg.argumentOf(call, i) @@ -176,7 +178,7 @@ private module LambdaFlow { ) or // flow into a callable - exists(ParameterNode p, DataFlowCallOption lastCall0, DataFlowCall call | + exists(ParameterNodeExt p, DataFlowCallOption lastCall0, DataFlowCall call | revLambdaFlowIn(lambdaCall, kind, p, t, toJump, lastCall0) and ( if lastCall0 = TDataFlowCallNone() and toJump = false @@ -227,8 +229,8 @@ private module LambdaFlow { pragma[nomagic] predicate revLambdaFlowIn( - DataFlowCall lambdaCall, LambdaCallKind kind, ParameterNode p, DataFlowType t, boolean toJump, - DataFlowCallOption lastCall + DataFlowCall lambdaCall, LambdaCallKind kind, ParameterNodeExt p, DataFlowType t, + boolean toJump, DataFlowCallOption lastCall ) { revLambdaFlow(lambdaCall, kind, p, t, false, toJump, lastCall) } @@ -283,7 +285,7 @@ private module Cached { predicate returnNodeExt(Node n, ReturnKindExt k) { k = TValueReturn(n.(ReturnNode).getKind()) or - exists(ParameterNode p, int pos | + exists(ParameterNodeExt p, int pos | parameterValueFlowsToPreUpdate(p, n) and p.isParameterOf(_, pos) and k = TParamUpdate(pos) @@ -296,7 +298,7 @@ private module Cached { cached predicate castingNode(Node n) { castNode(n) or - n instanceof ParameterNode or + n instanceof ParameterNodeExt or n instanceof OutNodeExt or // For reads, `x.f`, we want to check that the tracked type after the read (which // is obtained by popping the head of the access path stack) is compatible with @@ -304,6 +306,11 @@ private module Cached { read(_, _, n) } + cached + predicate parameterNode(Node n, DataFlowCallable c, int i) { + n.(ParameterNode).isParameterOf(c, i) + } + cached predicate argumentNode(Node n, DataFlowCall call, int pos) { n.(ArgumentNode).argumentOf(call, pos) @@ -328,7 +335,7 @@ private module Cached { * The instance parameter is considered to have index `-1`. */ pragma[nomagic] - private predicate viableParam(DataFlowCall call, int i, ParameterNode p) { + private predicate viableParam(DataFlowCall call, int i, ParameterNodeExt p) { p.isParameterOf(viableCallableExt(call), i) } @@ -337,7 +344,7 @@ private module Cached { * dispatch into account. */ cached - predicate viableParamArg(DataFlowCall call, ParameterNode p, ArgumentNodeExt arg) { + predicate viableParamArg(DataFlowCall call, ParameterNodeExt p, ArgumentNodeExt arg) { exists(int i | viableParam(call, i, p) and arg.argumentOf(call, i) and @@ -379,7 +386,7 @@ private module Cached { * `read` indicates whether it is contents of `p` that can flow to `node`. */ pragma[nomagic] - private predicate parameterValueFlowCand(ParameterNode p, Node node, boolean read) { + private predicate parameterValueFlowCand(ParameterNodeExt p, Node node, boolean read) { p = node and read = false or @@ -410,12 +417,14 @@ private module Cached { } pragma[nomagic] - private predicate parameterValueFlowArgCand(ParameterNode p, ArgumentNodeExt arg, boolean read) { + private predicate parameterValueFlowArgCand( + ParameterNodeExt p, ArgumentNodeExt arg, boolean read + ) { parameterValueFlowCand(p, arg, read) } pragma[nomagic] - predicate parameterValueFlowsToPreUpdateCand(ParameterNode p, PostUpdateNode n) { + predicate parameterValueFlowsToPreUpdateCand(ParameterNodeExt p, PostUpdateNode n) { parameterValueFlowCand(p, n.getPreUpdateNode(), false) } @@ -427,7 +436,7 @@ private module Cached { * `read` indicates whether it is contents of `p` that can flow to the return * node. */ - predicate parameterValueFlowReturnCand(ParameterNode p, ReturnKind kind, boolean read) { + predicate parameterValueFlowReturnCand(ParameterNodeExt p, ReturnKind kind, boolean read) { exists(ReturnNode ret | parameterValueFlowCand(p, ret, read) and kind = ret.getKind() @@ -438,7 +447,7 @@ private module Cached { private predicate argumentValueFlowsThroughCand0( DataFlowCall call, ArgumentNodeExt arg, ReturnKind kind, boolean read ) { - exists(ParameterNode param | viableParamArg(call, param, arg) | + exists(ParameterNodeExt param | viableParamArg(call, param, arg) | parameterValueFlowReturnCand(param, kind, read) ) } @@ -456,7 +465,7 @@ private module Cached { ) } - predicate cand(ParameterNode p, Node n) { + predicate cand(ParameterNodeExt p, Node n) { parameterValueFlowCand(p, n, _) and ( parameterValueFlowReturnCand(p, _, _) @@ -483,7 +492,7 @@ private module Cached { * If a read step was taken, then `read` captures the `Content`, the * container type, and the content type. */ - predicate parameterValueFlow(ParameterNode p, Node node, ReadStepTypesOption read) { + predicate parameterValueFlow(ParameterNodeExt p, Node node, ReadStepTypesOption read) { parameterValueFlow0(p, node, read) and if node instanceof CastingNode then @@ -497,7 +506,7 @@ private module Cached { } pragma[nomagic] - private predicate parameterValueFlow0(ParameterNode p, Node node, ReadStepTypesOption read) { + private predicate parameterValueFlow0(ParameterNodeExt p, Node node, ReadStepTypesOption read) { p = node and Cand::cand(p, _) and read = TReadStepTypesNone() @@ -522,7 +531,7 @@ private module Cached { pragma[nomagic] private predicate parameterValueFlow0_0( - ReadStepTypesOption mustBeNone, ParameterNode p, Node node, ReadStepTypesOption read + ReadStepTypesOption mustBeNone, ParameterNodeExt p, Node node, ReadStepTypesOption read ) { // flow through: no prior read exists(ArgumentNodeExt arg | @@ -539,7 +548,7 @@ private module Cached { pragma[nomagic] private predicate parameterValueFlowArg( - ParameterNode p, ArgumentNodeExt arg, ReadStepTypesOption read + ParameterNodeExt p, ArgumentNodeExt arg, ReadStepTypesOption read ) { parameterValueFlow(p, arg, read) and Cand::argumentValueFlowsThroughCand(arg, _, _) @@ -549,7 +558,7 @@ private module Cached { private predicate argumentValueFlowsThrough0( DataFlowCall call, ArgumentNodeExt arg, ReturnKind kind, ReadStepTypesOption read ) { - exists(ParameterNode param | viableParamArg(call, param, arg) | + exists(ParameterNodeExt param | viableParamArg(call, param, arg) | parameterValueFlowReturn(param, kind, read) ) } @@ -596,7 +605,7 @@ private module Cached { * container type, and the content type. */ private predicate parameterValueFlowReturn( - ParameterNode p, ReturnKind kind, ReadStepTypesOption read + ParameterNodeExt p, ReturnKind kind, ReadStepTypesOption read ) { exists(ReturnNode ret | parameterValueFlow(p, ret, read) and @@ -702,7 +711,7 @@ private module Cached { * Holds if `p` can flow to the pre-update node associated with post-update * node `n`, in the same callable, using only value-preserving steps. */ - private predicate parameterValueFlowsToPreUpdate(ParameterNode p, PostUpdateNode n) { + private predicate parameterValueFlowsToPreUpdate(ParameterNodeExt p, PostUpdateNode n) { parameterValueFlow(p, n.getPreUpdateNode(), TReadStepTypesNone()) } @@ -807,7 +816,7 @@ private module Cached { cached newtype TReturnKindExt = TValueReturn(ReturnKind kind) or - TParamUpdate(int pos) { exists(ParameterNode p | p.isParameterOf(_, pos)) } + TParamUpdate(int pos) { exists(ParameterNodeExt p | p.isParameterOf(_, pos)) } cached newtype TBooleanOption = @@ -922,7 +931,7 @@ class CallContextSomeCall extends CallContextCall, TSomeCall { override string toString() { result = "CcSomeCall" } override predicate relevantFor(DataFlowCallable callable) { - exists(ParameterNode p | getNodeEnclosingCallable(p) = callable) + exists(ParameterNodeExt p | getNodeEnclosingCallable(p) = callable) } override predicate matchesCall(DataFlowCall call) { any() } @@ -981,6 +990,20 @@ LocalCallContext getLocalCallContext(CallContext ctx, DataFlowCallable callable) else result instanceof LocalCallContextAny } +/** + * The value of a parameter at function entry, viewed as a node in a data + * flow graph. + */ +class ParameterNodeExt extends Node { + ParameterNodeExt() { parameterNode(this, _, _) } + + /** + * Holds if this node is the parameter of callable `c` at the specified + * (zero-based) position. + */ + predicate isParameterOf(DataFlowCallable c, int i) { parameterNode(this, c, i) } +} + /** A data-flow node that represents a call argument. */ class ArgumentNodeExt extends Node { ArgumentNodeExt() { argumentNode(this, _, _) } From 0c8886967bb53b7ac3c5f81b4650aa22fcc09764 Mon Sep 17 00:00:00 2001 From: Tom Hvitved Date: Mon, 26 Apr 2021 16:38:17 +0200 Subject: [PATCH 017/168] Data flow: Cache `nodeIsHidden` --- .../code/csharp/dataflow/internal/DataFlowImpl.qll | 2 +- .../csharp/dataflow/internal/DataFlowImplCommon.qll | 11 +++++++++++ 2 files changed, 12 insertions(+), 1 deletion(-) 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 ede9390bace..cf83530bcbd 100644 --- a/csharp/ql/src/semmle/code/csharp/dataflow/internal/DataFlowImpl.qll +++ b/csharp/ql/src/semmle/code/csharp/dataflow/internal/DataFlowImpl.qll @@ -2986,7 +2986,7 @@ class PathNode extends TPathNode { Configuration getConfiguration() { none() } private predicate isHidden() { - nodeIsHidden(this.getNode()) and + hiddenNode(this.getNode()) and not this.isSource() and not this instanceof PathNodeSink } 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 a9a19ed322d..b5ceabc605d 100644 --- a/csharp/ql/src/semmle/code/csharp/dataflow/internal/DataFlowImplCommon.qll +++ b/csharp/ql/src/semmle/code/csharp/dataflow/internal/DataFlowImplCommon.qll @@ -244,6 +244,14 @@ private DataFlowCallable viableCallableExt(DataFlowCall call) { cached private module Cached { + /** + * If needed, call this predicate from `DataFlowImplSpecific.qll` in order to + * force a stage-dependency on the `DataFlowImplCommon.qll` stage and therby + * collapsing the two stages. + */ + cached + predicate forceCachingInSameStage() { any() } + cached predicate nodeEnclosingCallable(Node n, DataFlowCallable c) { c = n.getEnclosingCallable() } @@ -271,6 +279,9 @@ private module Cached { n.(PostUpdateNode).getPreUpdateNode() instanceof ArgumentNodeExt } + cached + predicate hiddenNode(Node n) { nodeIsHidden(n) } + cached OutNodeExt getAnOutNodeExt(DataFlowCall call, ReturnKindExt k) { result = getAnOutNode(call, k.(ValueReturnKind).getKind()) From 914184f3dd08969b0709104be32287a6779ad243 Mon Sep 17 00:00:00 2001 From: Tom Hvitved Date: Mon, 26 Apr 2021 17:02:20 +0200 Subject: [PATCH 018/168] Data flow: Sync files --- .../cpp/dataflow/internal/DataFlowImpl.qll | 155 ++++----- .../cpp/dataflow/internal/DataFlowImpl2.qll | 155 ++++----- .../cpp/dataflow/internal/DataFlowImpl3.qll | 155 ++++----- .../cpp/dataflow/internal/DataFlowImpl4.qll | 155 ++++----- .../dataflow/internal/DataFlowImplCommon.qll | 313 +++++++++++------- .../dataflow/internal/DataFlowImplLocal.qll | 155 ++++----- .../cpp/ir/dataflow/internal/DataFlowImpl.qll | 155 ++++----- .../ir/dataflow/internal/DataFlowImpl2.qll | 155 ++++----- .../ir/dataflow/internal/DataFlowImpl3.qll | 155 ++++----- .../ir/dataflow/internal/DataFlowImpl4.qll | 155 ++++----- .../dataflow/internal/DataFlowImplCommon.qll | 313 +++++++++++------- .../dataflow/internal/DataFlowImpl2.qll | 155 ++++----- .../dataflow/internal/DataFlowImpl3.qll | 155 ++++----- .../dataflow/internal/DataFlowImpl4.qll | 155 ++++----- .../dataflow/internal/DataFlowImpl5.qll | 155 ++++----- .../java/dataflow/internal/DataFlowImpl.qll | 155 ++++----- .../java/dataflow/internal/DataFlowImpl2.qll | 155 ++++----- .../java/dataflow/internal/DataFlowImpl3.qll | 155 ++++----- .../java/dataflow/internal/DataFlowImpl4.qll | 155 ++++----- .../java/dataflow/internal/DataFlowImpl5.qll | 155 ++++----- .../java/dataflow/internal/DataFlowImpl6.qll | 155 ++++----- .../dataflow/internal/DataFlowImplCommon.qll | 313 +++++++++++------- .../dataflow/new/internal/DataFlowImpl.qll | 155 ++++----- .../dataflow/new/internal/DataFlowImpl2.qll | 155 ++++----- .../dataflow/new/internal/DataFlowImpl3.qll | 155 ++++----- .../dataflow/new/internal/DataFlowImpl4.qll | 155 ++++----- .../new/internal/DataFlowImplCommon.qll | 313 +++++++++++------- 27 files changed, 2659 insertions(+), 2158 deletions(-) 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 9498e51e7e6..cf83530bcbd 100644 --- a/cpp/ql/src/semmle/code/cpp/dataflow/internal/DataFlowImpl.qll +++ b/cpp/ql/src/semmle/code/cpp/dataflow/internal/DataFlowImpl.qll @@ -211,10 +211,7 @@ private predicate fullBarrier(Node node, Configuration config) { * Holds if data can flow in one local step from `node1` to `node2`. */ private predicate localFlowStep(Node node1, Node node2, Configuration config) { - ( - simpleLocalFlowStep(node1, node2) or - reverseStepThroughInputOutputAlias(node1, node2) - ) and + simpleLocalFlowStepExt(node1, node2) and not outBarrier(node1, config) and not inBarrier(node2, config) and not fullBarrier(node1, config) and @@ -237,7 +234,7 @@ private predicate additionalLocalFlowStep(Node node1, Node node2, Configuration * Holds if data can flow from `node1` to `node2` in a way that discards call contexts. */ private predicate jumpStep(Node node1, Node node2, Configuration config) { - jumpStep(node1, node2) and + jumpStepCached(node1, node2) and not outBarrier(node1, config) and not inBarrier(node2, config) and not fullBarrier(node1, config) and @@ -388,7 +385,7 @@ private module Stage1 { */ pragma[nomagic] private predicate fwdFlowIsEntered(DataFlowCall call, Cc cc, Configuration config) { - exists(ArgumentNode arg | + exists(ArgumentNodeExt arg | fwdFlow(arg, cc, config) and viableParamArg(call, _, arg) ) @@ -515,7 +512,7 @@ private module Stage1 { pragma[nomagic] predicate viableParamArgNodeCandFwd1( - DataFlowCall call, ParameterNode p, ArgumentNode arg, Configuration config + DataFlowCall call, ParameterNodeExt p, ArgumentNodeExt arg, Configuration config ) { viableParamArg(call, p, arg) and fwdFlow(arg, config) @@ -523,16 +520,16 @@ private module Stage1 { pragma[nomagic] private predicate revFlowIn( - DataFlowCall call, ArgumentNode arg, boolean toReturn, Configuration config + DataFlowCall call, ArgumentNodeExt arg, boolean toReturn, Configuration config ) { - exists(ParameterNode p | + exists(ParameterNodeExt p | revFlow(p, toReturn, config) and viableParamArgNodeCandFwd1(call, p, arg, config) ) } pragma[nomagic] - private predicate revFlowInToReturn(DataFlowCall call, ArgumentNode arg, Configuration config) { + private predicate revFlowInToReturn(DataFlowCall call, ArgumentNodeExt arg, Configuration config) { revFlowIn(call, arg, true, config) } @@ -597,7 +594,9 @@ private module Stage1 { * Holds if flow may enter through `p` and reach a return node making `p` a * candidate for the origin of a summary. */ - predicate parameterMayFlowThrough(ParameterNode p, DataFlowCallable c, Ap ap, Configuration config) { + predicate parameterMayFlowThrough( + ParameterNodeExt p, DataFlowCallable c, Ap ap, Configuration config + ) { exists(ReturnKindExt kind | throughFlowNodeCand(p, config) and returnFlowCallableNodeCand(c, kind, config) and @@ -663,7 +662,7 @@ private predicate flowOutOfCallNodeCand1( pragma[nomagic] private predicate viableParamArgNodeCand1( - DataFlowCall call, ParameterNode p, ArgumentNode arg, Configuration config + DataFlowCall call, ParameterNodeExt p, ArgumentNodeExt arg, Configuration config ) { Stage1::viableParamArgNodeCandFwd1(call, p, arg, config) and Stage1::revFlow(arg, config) @@ -675,7 +674,7 @@ private predicate viableParamArgNodeCand1( */ pragma[nomagic] private predicate flowIntoCallNodeCand1( - DataFlowCall call, ArgumentNode arg, ParameterNode p, Configuration config + DataFlowCall call, ArgumentNodeExt arg, ParameterNodeExt p, Configuration config ) { viableParamArgNodeCand1(call, p, arg, config) and Stage1::revFlow(p, config) and @@ -735,7 +734,7 @@ private predicate flowOutOfCallNodeCand1( */ pragma[nomagic] private predicate flowIntoCallNodeCand1( - DataFlowCall call, ArgumentNode arg, ParameterNode p, boolean allowsFieldFlow, + DataFlowCall call, ArgumentNodeExt arg, ParameterNodeExt p, boolean allowsFieldFlow, Configuration config ) { flowIntoCallNodeCand1(call, arg, p, config) and @@ -944,10 +943,10 @@ private module Stage2 { pragma[nomagic] private predicate fwdFlowIn( - DataFlowCall call, ParameterNode p, Cc outercc, Cc innercc, ApOption argAp, Ap ap, + DataFlowCall call, ParameterNodeExt p, Cc outercc, Cc innercc, ApOption argAp, Ap ap, Configuration config ) { - exists(ArgumentNode arg, boolean allowsFieldFlow | + exists(ArgumentNodeExt arg, boolean allowsFieldFlow | fwdFlow(arg, outercc, argAp, ap, config) and flowIntoCall(call, arg, p, allowsFieldFlow, config) and innercc = getCallContextCall(call, getNodeEnclosingCallable(p), outercc) @@ -992,7 +991,7 @@ private module Stage2 { private predicate fwdFlowIsEntered( DataFlowCall call, Cc cc, ApOption argAp, Ap ap, Configuration config ) { - exists(ParameterNode p | + exists(ParameterNodeExt p | fwdFlowIn(call, p, cc, _, argAp, ap, config) and PrevStage::parameterMayFlowThrough(p, _, getApprox(ap), config) ) @@ -1133,10 +1132,10 @@ private module Stage2 { pragma[nomagic] private predicate revFlowIn( - DataFlowCall call, ArgumentNode arg, boolean toReturn, ApOption returnAp, Ap ap, + DataFlowCall call, ArgumentNodeExt arg, boolean toReturn, ApOption returnAp, Ap ap, Configuration config ) { - exists(ParameterNode p, boolean allowsFieldFlow | + exists(ParameterNodeExt p, boolean allowsFieldFlow | revFlow(p, toReturn, returnAp, ap, config) and flowIntoCall(call, arg, p, allowsFieldFlow, config) | @@ -1146,7 +1145,7 @@ private module Stage2 { pragma[nomagic] private predicate revFlowInToReturn( - DataFlowCall call, ArgumentNode arg, Ap returnAp, Ap ap, Configuration config + DataFlowCall call, ArgumentNodeExt arg, Ap returnAp, Ap ap, Configuration config ) { revFlowIn(call, arg, true, apSome(returnAp), ap, config) } @@ -1199,13 +1198,15 @@ private module Stage2 { pragma[noinline] private predicate parameterFlow( - ParameterNode p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config + ParameterNodeExt p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config ) { revFlow(p, true, apSome(ap0), ap, config) and c = getNodeEnclosingCallable(p) } - predicate parameterMayFlowThrough(ParameterNode p, DataFlowCallable c, Ap ap, Configuration config) { + predicate parameterMayFlowThrough( + ParameterNodeExt p, DataFlowCallable c, Ap ap, Configuration config + ) { exists(ReturnNodeExt ret, Ap ap0, ReturnKindExt kind, int pos | parameterFlow(p, ap, ap0, c, config) and c = getNodeEnclosingCallable(ret) and @@ -1245,7 +1246,7 @@ private predicate flowOutOfCallNodeCand2( pragma[nomagic] private predicate flowIntoCallNodeCand2( - DataFlowCall call, ArgumentNode node1, ParameterNode node2, boolean allowsFieldFlow, + DataFlowCall call, ArgumentNodeExt node1, ParameterNodeExt node2, boolean allowsFieldFlow, Configuration config ) { flowIntoCallNodeCand1(call, node1, node2, allowsFieldFlow, config) and @@ -1260,8 +1261,8 @@ private module LocalFlowBigStep { */ private class FlowCheckNode extends Node { FlowCheckNode() { - this instanceof CastNode or - clearsContent(this, _) + castNode(this) or + clearsContentCached(this, _) } } @@ -1275,7 +1276,7 @@ private module LocalFlowBigStep { config.isSource(node) or jumpStep(_, node, config) or additionalJumpStep(_, node, config) or - node instanceof ParameterNode or + node instanceof ParameterNodeExt or node instanceof OutNodeExt or store(_, _, node, _) or read(_, _, node) or @@ -1321,21 +1322,21 @@ private module LocalFlowBigStep { Node node1, Node node2, boolean preservesValue, DataFlowType t, Configuration config, LocalCallContext cc ) { - not isUnreachableInCall(node2, cc.(LocalCallContextSpecificCall).getCall()) and + not isUnreachableInCallCached(node2, cc.(LocalCallContextSpecificCall).getCall()) and ( localFlowEntry(node1, pragma[only_bind_into](config)) and ( localFlowStepNodeCand1(node1, node2, config) and preservesValue = true and - t = getNodeType(node1) + t = getNodeDataFlowType(node1) or additionalLocalFlowStepNodeCand2(node1, node2, config) and preservesValue = false and - t = getNodeType(node2) + t = getNodeDataFlowType(node2) ) and node1 != node2 and cc.relevantFor(getNodeEnclosingCallable(node1)) and - not isUnreachableInCall(node1, cc.(LocalCallContextSpecificCall).getCall()) and + not isUnreachableInCallCached(node1, cc.(LocalCallContextSpecificCall).getCall()) and Stage2::revFlow(node2, pragma[only_bind_into](config)) or exists(Node mid | @@ -1350,7 +1351,7 @@ private module LocalFlowBigStep { additionalLocalFlowStepNodeCand2(mid, node2, config) and not mid instanceof FlowCheckNode and preservesValue = false and - t = getNodeType(node2) and + t = getNodeDataFlowType(node2) and Stage2::revFlow(node2, pragma[only_bind_into](config)) ) ) @@ -1384,7 +1385,7 @@ private module Stage3 { private ApApprox getApprox(Ap ap) { result = ap.toBoolNonEmpty() } private ApNil getApNil(Node node) { - PrevStage::revFlow(node, _) and result = TFrontNil(getNodeType(node)) + PrevStage::revFlow(node, _) and result = TFrontNil(getNodeDataFlowType(node)) } bindingset[tc, tail] @@ -1443,7 +1444,9 @@ private module Stage3 { bindingset[node, ap] private predicate filter(Node node, Ap ap) { not ap.isClearedAt(node) and - if node instanceof CastingNode then compatibleTypes(getNodeType(node), ap.getType()) else any() + if node instanceof CastingNode + then compatibleTypes(getNodeDataFlowType(node), ap.getType()) + else any() } bindingset[ap, contentType] @@ -1583,10 +1586,10 @@ private module Stage3 { pragma[nomagic] private predicate fwdFlowIn( - DataFlowCall call, ParameterNode p, Cc outercc, Cc innercc, ApOption argAp, Ap ap, + DataFlowCall call, ParameterNodeExt p, Cc outercc, Cc innercc, ApOption argAp, Ap ap, Configuration config ) { - exists(ArgumentNode arg, boolean allowsFieldFlow | + exists(ArgumentNodeExt arg, boolean allowsFieldFlow | fwdFlow(arg, outercc, argAp, ap, config) and flowIntoCall(call, arg, p, allowsFieldFlow, config) and innercc = getCallContextCall(call, getNodeEnclosingCallable(p), outercc) @@ -1631,7 +1634,7 @@ private module Stage3 { private predicate fwdFlowIsEntered( DataFlowCall call, Cc cc, ApOption argAp, Ap ap, Configuration config ) { - exists(ParameterNode p | + exists(ParameterNodeExt p | fwdFlowIn(call, p, cc, _, argAp, ap, config) and PrevStage::parameterMayFlowThrough(p, _, unbindApa(getApprox(ap)), config) ) @@ -1772,10 +1775,10 @@ private module Stage3 { pragma[nomagic] private predicate revFlowIn( - DataFlowCall call, ArgumentNode arg, boolean toReturn, ApOption returnAp, Ap ap, + DataFlowCall call, ArgumentNodeExt arg, boolean toReturn, ApOption returnAp, Ap ap, Configuration config ) { - exists(ParameterNode p, boolean allowsFieldFlow | + exists(ParameterNodeExt p, boolean allowsFieldFlow | revFlow(p, toReturn, returnAp, ap, config) and flowIntoCall(call, arg, p, allowsFieldFlow, config) | @@ -1785,7 +1788,7 @@ private module Stage3 { pragma[nomagic] private predicate revFlowInToReturn( - DataFlowCall call, ArgumentNode arg, Ap returnAp, Ap ap, Configuration config + DataFlowCall call, ArgumentNodeExt arg, Ap returnAp, Ap ap, Configuration config ) { revFlowIn(call, arg, true, apSome(returnAp), ap, config) } @@ -1838,13 +1841,15 @@ private module Stage3 { pragma[noinline] private predicate parameterFlow( - ParameterNode p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config + ParameterNodeExt p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config ) { revFlow(p, true, apSome(ap0), ap, config) and c = getNodeEnclosingCallable(p) } - predicate parameterMayFlowThrough(ParameterNode p, DataFlowCallable c, Ap ap, Configuration config) { + predicate parameterMayFlowThrough( + ParameterNodeExt p, DataFlowCallable c, Ap ap, Configuration config + ) { exists(ReturnNodeExt ret, Ap ap0, ReturnKindExt kind, int pos | parameterFlow(p, ap, ap0, c, config) and c = getNodeEnclosingCallable(ret) and @@ -2088,7 +2093,7 @@ private module Stage4 { private ApApprox getApprox(Ap ap) { result = ap.getFront() } private ApNil getApNil(Node node) { - PrevStage::revFlow(node, _) and result = TNil(getNodeType(node)) + PrevStage::revFlow(node, _) and result = TNil(getNodeDataFlowType(node)) } bindingset[tc, tail] @@ -2155,7 +2160,7 @@ private module Stage4 { pragma[nomagic] private predicate flowIntoCall( - DataFlowCall call, ArgumentNode node1, ParameterNode node2, boolean allowsFieldFlow, + DataFlowCall call, ArgumentNodeExt node1, ParameterNodeExt node2, boolean allowsFieldFlow, Configuration config ) { flowIntoCallNodeCand2(call, node1, node2, allowsFieldFlow, config) and @@ -2300,10 +2305,10 @@ private module Stage4 { pragma[nomagic] private predicate fwdFlowIn( - DataFlowCall call, ParameterNode p, Cc outercc, Cc innercc, ApOption argAp, Ap ap, + DataFlowCall call, ParameterNodeExt p, Cc outercc, Cc innercc, ApOption argAp, Ap ap, Configuration config ) { - exists(ArgumentNode arg, boolean allowsFieldFlow | + exists(ArgumentNodeExt arg, boolean allowsFieldFlow | fwdFlow(arg, outercc, argAp, ap, config) and flowIntoCall(call, arg, p, allowsFieldFlow, config) and innercc = getCallContextCall(call, getNodeEnclosingCallable(p), outercc) @@ -2348,7 +2353,7 @@ private module Stage4 { private predicate fwdFlowIsEntered( DataFlowCall call, Cc cc, ApOption argAp, Ap ap, Configuration config ) { - exists(ParameterNode p | + exists(ParameterNodeExt p | fwdFlowIn(call, p, cc, _, argAp, ap, config) and PrevStage::parameterMayFlowThrough(p, _, unbindApa(getApprox(ap)), config) ) @@ -2489,10 +2494,10 @@ private module Stage4 { pragma[nomagic] private predicate revFlowIn( - DataFlowCall call, ArgumentNode arg, boolean toReturn, ApOption returnAp, Ap ap, + DataFlowCall call, ArgumentNodeExt arg, boolean toReturn, ApOption returnAp, Ap ap, Configuration config ) { - exists(ParameterNode p, boolean allowsFieldFlow | + exists(ParameterNodeExt p, boolean allowsFieldFlow | revFlow(p, toReturn, returnAp, ap, config) and flowIntoCall(call, arg, p, allowsFieldFlow, config) | @@ -2502,7 +2507,7 @@ private module Stage4 { pragma[nomagic] private predicate revFlowInToReturn( - DataFlowCall call, ArgumentNode arg, Ap returnAp, Ap ap, Configuration config + DataFlowCall call, ArgumentNodeExt arg, Ap returnAp, Ap ap, Configuration config ) { revFlowIn(call, arg, true, apSome(returnAp), ap, config) } @@ -2555,13 +2560,15 @@ private module Stage4 { pragma[noinline] private predicate parameterFlow( - ParameterNode p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config + ParameterNodeExt p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config ) { revFlow(p, true, apSome(ap0), ap, config) and c = getNodeEnclosingCallable(p) } - predicate parameterMayFlowThrough(ParameterNode p, DataFlowCallable c, Ap ap, Configuration config) { + predicate parameterMayFlowThrough( + ParameterNodeExt p, DataFlowCallable c, Ap ap, Configuration config + ) { exists(ReturnNodeExt ret, Ap ap0, ReturnKindExt kind, int pos | parameterFlow(p, ap, ap0, c, config) and c = getNodeEnclosingCallable(ret) and @@ -2606,7 +2613,7 @@ private predicate nodeMayUseSummary(Node n, AccessPathApprox apa, Configuration private newtype TSummaryCtx = TSummaryCtxNone() or - TSummaryCtxSome(ParameterNode p, AccessPath ap) { + TSummaryCtxSome(ParameterNodeExt p, AccessPath ap) { Stage4::parameterMayFlowThrough(p, _, ap.getApprox(), _) } @@ -2627,7 +2634,7 @@ private class SummaryCtxNone extends SummaryCtx, TSummaryCtxNone { /** A summary context from which a flow summary can be generated. */ private class SummaryCtxSome extends SummaryCtx, TSummaryCtxSome { - private ParameterNode p; + private ParameterNodeExt p; private AccessPath ap; SummaryCtxSome() { this = TSummaryCtxSome(p, ap) } @@ -2758,7 +2765,7 @@ private newtype TPathNode = config.isSource(node) and cc instanceof CallContextAny and sc instanceof SummaryCtxNone and - ap = TAccessPathNil(getNodeType(node)) + ap = TAccessPathNil(getNodeDataFlowType(node)) or // ... or a step from an existing PathNode to another node. exists(PathNodeMid mid | @@ -2979,7 +2986,7 @@ class PathNode extends TPathNode { Configuration getConfiguration() { none() } private predicate isHidden() { - nodeIsHidden(this.getNode()) and + hiddenNode(this.getNode()) and not this.isSource() and not this instanceof PathNodeSink } @@ -3148,7 +3155,7 @@ private predicate pathStep(PathNodeMid mid, Node node, CallContext cc, SummaryCt cc instanceof CallContextAny and sc instanceof SummaryCtxNone and mid.getAp() instanceof AccessPathNil and - ap = TAccessPathNil(getNodeType(node)) + ap = TAccessPathNil(getNodeDataFlowType(node)) or exists(TypedContent tc | pathStoreStep(mid, node, ap.pop(tc), tc, cc)) and sc = mid.getSummaryCtx() @@ -3235,7 +3242,7 @@ pragma[noinline] private predicate pathIntoArg( PathNodeMid mid, int i, CallContext cc, DataFlowCall call, AccessPath ap, AccessPathApprox apa ) { - exists(ArgumentNode arg | + exists(ArgumentNodeExt arg | arg = mid.getNode() and cc = mid.getCallContext() and arg.argumentOf(call, i) and @@ -3248,7 +3255,7 @@ pragma[noinline] private predicate parameterCand( DataFlowCallable callable, int i, AccessPathApprox apa, Configuration config ) { - exists(ParameterNode p | + exists(ParameterNodeExt p | Stage4::revFlow(p, _, _, apa, config) and p.isParameterOf(callable, i) ) @@ -3272,7 +3279,7 @@ private predicate pathIntoCallable0( * respectively. */ private predicate pathIntoCallable( - PathNodeMid mid, ParameterNode p, CallContext outercc, CallContextCall innercc, SummaryCtx sc, + PathNodeMid mid, ParameterNodeExt p, CallContext outercc, CallContextCall innercc, SummaryCtx sc, DataFlowCall call ) { exists(int i, DataFlowCallable callable, AccessPath ap | @@ -3568,7 +3575,7 @@ private module FlowExploration { private newtype TSummaryCtx1 = TSummaryCtx1None() or - TSummaryCtx1Param(ParameterNode p) + TSummaryCtx1Param(ParameterNodeExt p) private newtype TSummaryCtx2 = TSummaryCtx2None() or @@ -3591,7 +3598,7 @@ private module FlowExploration { cc instanceof CallContextAny and sc1 = TSummaryCtx1None() and sc2 = TSummaryCtx2None() and - ap = TPartialNil(getNodeType(node)) and + ap = TPartialNil(getNodeDataFlowType(node)) and not fullBarrier(node, config) and exists(config.explorationLimit()) or @@ -3611,7 +3618,7 @@ private module FlowExploration { or exists(PartialPathNodeRev mid | revPartialPathStep(mid, node, sc1, sc2, ap, config) and - not clearsContent(node, ap.getHead()) and + not clearsContentCached(node, ap.getHead()) and not fullBarrier(node, config) and distSink(getNodeEnclosingCallable(node), config) <= config.explorationLimit() ) @@ -3625,9 +3632,9 @@ private module FlowExploration { exists(PartialPathNodeFwd mid | partialPathStep(mid, node, cc, sc1, sc2, ap, config) and not fullBarrier(node, config) and - not clearsContent(node, ap.getHead().getContent()) and + not clearsContentCached(node, ap.getHead().getContent()) and if node instanceof CastingNode - then compatibleTypes(getNodeType(node), ap.getType()) + then compatibleTypes(getNodeDataFlowType(node), ap.getType()) else any() ) } @@ -3783,7 +3790,7 @@ private module FlowExploration { PartialPathNodeFwd mid, Node node, CallContext cc, TSummaryCtx1 sc1, TSummaryCtx2 sc2, PartialAccessPath ap, Configuration config ) { - not isUnreachableInCall(node, cc.(CallContextSpecificCall).getCall()) and + not isUnreachableInCallCached(node, cc.(CallContextSpecificCall).getCall()) and ( localFlowStep(mid.getNode(), node, config) and cc = mid.getCallContext() and @@ -3797,7 +3804,7 @@ private module FlowExploration { sc1 = mid.getSummaryCtx1() and sc2 = mid.getSummaryCtx2() and mid.getAp() instanceof PartialAccessPathNil and - ap = TPartialNil(getNodeType(node)) and + ap = TPartialNil(getNodeDataFlowType(node)) and config = mid.getConfiguration() ) or @@ -3813,7 +3820,7 @@ private module FlowExploration { sc1 = TSummaryCtx1None() and sc2 = TSummaryCtx2None() and mid.getAp() instanceof PartialAccessPathNil and - ap = TPartialNil(getNodeType(node)) and + ap = TPartialNil(getNodeDataFlowType(node)) and config = mid.getConfiguration() or partialPathStoreStep(mid, _, _, node, ap) and @@ -3827,7 +3834,7 @@ private module FlowExploration { sc1 = mid.getSummaryCtx1() and sc2 = mid.getSummaryCtx2() and apConsFwd(ap, tc, ap0, config) and - compatibleTypes(ap.getType(), getNodeType(node)) + compatibleTypes(ap.getType(), getNodeDataFlowType(node)) ) or partialPathIntoCallable(mid, node, _, cc, sc1, sc2, _, ap, config) @@ -3924,7 +3931,7 @@ private module FlowExploration { PartialPathNodeFwd mid, int i, CallContext cc, DataFlowCall call, PartialAccessPath ap, Configuration config ) { - exists(ArgumentNode arg | + exists(ArgumentNodeExt arg | arg = mid.getNode() and cc = mid.getCallContext() and arg.argumentOf(call, i) and @@ -3943,7 +3950,7 @@ private module FlowExploration { } private predicate partialPathIntoCallable( - PartialPathNodeFwd mid, ParameterNode p, CallContext outercc, CallContextCall innercc, + PartialPathNodeFwd mid, ParameterNodeExt p, CallContext outercc, CallContextCall innercc, TSummaryCtx1 sc1, TSummaryCtx2 sc2, DataFlowCall call, PartialAccessPath ap, Configuration config ) { @@ -3980,7 +3987,7 @@ private module FlowExploration { DataFlowCall call, PartialPathNodeFwd mid, ReturnKindExt kind, CallContext cc, PartialAccessPath ap, Configuration config ) { - exists(ParameterNode p, CallContext innercc, TSummaryCtx1 sc1, TSummaryCtx2 sc2 | + exists(ParameterNodeExt p, CallContext innercc, TSummaryCtx1 sc1, TSummaryCtx2 sc2 | partialPathIntoCallable(mid, p, cc, innercc, sc1, sc2, call, _, config) and paramFlowsThroughInPartialPath(kind, innercc, sc1, sc2, ap, config) ) @@ -4037,7 +4044,7 @@ private module FlowExploration { apConsRev(ap, c, ap0, config) ) or - exists(ParameterNode p | + exists(ParameterNodeExt p | mid.getNode() = p and viableParamArg(_, p, node) and sc1 = mid.getSummaryCtx1() and @@ -4115,7 +4122,7 @@ private module FlowExploration { int pos, TRevSummaryCtx1Some sc1, TRevSummaryCtx2Some sc2, RevPartialAccessPath ap, Configuration config ) { - exists(PartialPathNodeRev mid, ParameterNode p | + exists(PartialPathNodeRev mid, ParameterNodeExt p | mid.getNode() = p and p.isParameterOf(_, pos) and sc1 = mid.getSummaryCtx1() and @@ -4138,7 +4145,7 @@ private module FlowExploration { pragma[nomagic] private predicate revPartialPathThroughCallable( - PartialPathNodeRev mid, ArgumentNode node, RevPartialAccessPath ap, Configuration config + PartialPathNodeRev mid, ArgumentNodeExt node, RevPartialAccessPath ap, Configuration config ) { exists(DataFlowCall call, int pos | revPartialPathThroughCallable0(call, mid, pos, ap, config) and 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 9498e51e7e6..cf83530bcbd 100644 --- a/cpp/ql/src/semmle/code/cpp/dataflow/internal/DataFlowImpl2.qll +++ b/cpp/ql/src/semmle/code/cpp/dataflow/internal/DataFlowImpl2.qll @@ -211,10 +211,7 @@ private predicate fullBarrier(Node node, Configuration config) { * Holds if data can flow in one local step from `node1` to `node2`. */ private predicate localFlowStep(Node node1, Node node2, Configuration config) { - ( - simpleLocalFlowStep(node1, node2) or - reverseStepThroughInputOutputAlias(node1, node2) - ) and + simpleLocalFlowStepExt(node1, node2) and not outBarrier(node1, config) and not inBarrier(node2, config) and not fullBarrier(node1, config) and @@ -237,7 +234,7 @@ private predicate additionalLocalFlowStep(Node node1, Node node2, Configuration * Holds if data can flow from `node1` to `node2` in a way that discards call contexts. */ private predicate jumpStep(Node node1, Node node2, Configuration config) { - jumpStep(node1, node2) and + jumpStepCached(node1, node2) and not outBarrier(node1, config) and not inBarrier(node2, config) and not fullBarrier(node1, config) and @@ -388,7 +385,7 @@ private module Stage1 { */ pragma[nomagic] private predicate fwdFlowIsEntered(DataFlowCall call, Cc cc, Configuration config) { - exists(ArgumentNode arg | + exists(ArgumentNodeExt arg | fwdFlow(arg, cc, config) and viableParamArg(call, _, arg) ) @@ -515,7 +512,7 @@ private module Stage1 { pragma[nomagic] predicate viableParamArgNodeCandFwd1( - DataFlowCall call, ParameterNode p, ArgumentNode arg, Configuration config + DataFlowCall call, ParameterNodeExt p, ArgumentNodeExt arg, Configuration config ) { viableParamArg(call, p, arg) and fwdFlow(arg, config) @@ -523,16 +520,16 @@ private module Stage1 { pragma[nomagic] private predicate revFlowIn( - DataFlowCall call, ArgumentNode arg, boolean toReturn, Configuration config + DataFlowCall call, ArgumentNodeExt arg, boolean toReturn, Configuration config ) { - exists(ParameterNode p | + exists(ParameterNodeExt p | revFlow(p, toReturn, config) and viableParamArgNodeCandFwd1(call, p, arg, config) ) } pragma[nomagic] - private predicate revFlowInToReturn(DataFlowCall call, ArgumentNode arg, Configuration config) { + private predicate revFlowInToReturn(DataFlowCall call, ArgumentNodeExt arg, Configuration config) { revFlowIn(call, arg, true, config) } @@ -597,7 +594,9 @@ private module Stage1 { * Holds if flow may enter through `p` and reach a return node making `p` a * candidate for the origin of a summary. */ - predicate parameterMayFlowThrough(ParameterNode p, DataFlowCallable c, Ap ap, Configuration config) { + predicate parameterMayFlowThrough( + ParameterNodeExt p, DataFlowCallable c, Ap ap, Configuration config + ) { exists(ReturnKindExt kind | throughFlowNodeCand(p, config) and returnFlowCallableNodeCand(c, kind, config) and @@ -663,7 +662,7 @@ private predicate flowOutOfCallNodeCand1( pragma[nomagic] private predicate viableParamArgNodeCand1( - DataFlowCall call, ParameterNode p, ArgumentNode arg, Configuration config + DataFlowCall call, ParameterNodeExt p, ArgumentNodeExt arg, Configuration config ) { Stage1::viableParamArgNodeCandFwd1(call, p, arg, config) and Stage1::revFlow(arg, config) @@ -675,7 +674,7 @@ private predicate viableParamArgNodeCand1( */ pragma[nomagic] private predicate flowIntoCallNodeCand1( - DataFlowCall call, ArgumentNode arg, ParameterNode p, Configuration config + DataFlowCall call, ArgumentNodeExt arg, ParameterNodeExt p, Configuration config ) { viableParamArgNodeCand1(call, p, arg, config) and Stage1::revFlow(p, config) and @@ -735,7 +734,7 @@ private predicate flowOutOfCallNodeCand1( */ pragma[nomagic] private predicate flowIntoCallNodeCand1( - DataFlowCall call, ArgumentNode arg, ParameterNode p, boolean allowsFieldFlow, + DataFlowCall call, ArgumentNodeExt arg, ParameterNodeExt p, boolean allowsFieldFlow, Configuration config ) { flowIntoCallNodeCand1(call, arg, p, config) and @@ -944,10 +943,10 @@ private module Stage2 { pragma[nomagic] private predicate fwdFlowIn( - DataFlowCall call, ParameterNode p, Cc outercc, Cc innercc, ApOption argAp, Ap ap, + DataFlowCall call, ParameterNodeExt p, Cc outercc, Cc innercc, ApOption argAp, Ap ap, Configuration config ) { - exists(ArgumentNode arg, boolean allowsFieldFlow | + exists(ArgumentNodeExt arg, boolean allowsFieldFlow | fwdFlow(arg, outercc, argAp, ap, config) and flowIntoCall(call, arg, p, allowsFieldFlow, config) and innercc = getCallContextCall(call, getNodeEnclosingCallable(p), outercc) @@ -992,7 +991,7 @@ private module Stage2 { private predicate fwdFlowIsEntered( DataFlowCall call, Cc cc, ApOption argAp, Ap ap, Configuration config ) { - exists(ParameterNode p | + exists(ParameterNodeExt p | fwdFlowIn(call, p, cc, _, argAp, ap, config) and PrevStage::parameterMayFlowThrough(p, _, getApprox(ap), config) ) @@ -1133,10 +1132,10 @@ private module Stage2 { pragma[nomagic] private predicate revFlowIn( - DataFlowCall call, ArgumentNode arg, boolean toReturn, ApOption returnAp, Ap ap, + DataFlowCall call, ArgumentNodeExt arg, boolean toReturn, ApOption returnAp, Ap ap, Configuration config ) { - exists(ParameterNode p, boolean allowsFieldFlow | + exists(ParameterNodeExt p, boolean allowsFieldFlow | revFlow(p, toReturn, returnAp, ap, config) and flowIntoCall(call, arg, p, allowsFieldFlow, config) | @@ -1146,7 +1145,7 @@ private module Stage2 { pragma[nomagic] private predicate revFlowInToReturn( - DataFlowCall call, ArgumentNode arg, Ap returnAp, Ap ap, Configuration config + DataFlowCall call, ArgumentNodeExt arg, Ap returnAp, Ap ap, Configuration config ) { revFlowIn(call, arg, true, apSome(returnAp), ap, config) } @@ -1199,13 +1198,15 @@ private module Stage2 { pragma[noinline] private predicate parameterFlow( - ParameterNode p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config + ParameterNodeExt p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config ) { revFlow(p, true, apSome(ap0), ap, config) and c = getNodeEnclosingCallable(p) } - predicate parameterMayFlowThrough(ParameterNode p, DataFlowCallable c, Ap ap, Configuration config) { + predicate parameterMayFlowThrough( + ParameterNodeExt p, DataFlowCallable c, Ap ap, Configuration config + ) { exists(ReturnNodeExt ret, Ap ap0, ReturnKindExt kind, int pos | parameterFlow(p, ap, ap0, c, config) and c = getNodeEnclosingCallable(ret) and @@ -1245,7 +1246,7 @@ private predicate flowOutOfCallNodeCand2( pragma[nomagic] private predicate flowIntoCallNodeCand2( - DataFlowCall call, ArgumentNode node1, ParameterNode node2, boolean allowsFieldFlow, + DataFlowCall call, ArgumentNodeExt node1, ParameterNodeExt node2, boolean allowsFieldFlow, Configuration config ) { flowIntoCallNodeCand1(call, node1, node2, allowsFieldFlow, config) and @@ -1260,8 +1261,8 @@ private module LocalFlowBigStep { */ private class FlowCheckNode extends Node { FlowCheckNode() { - this instanceof CastNode or - clearsContent(this, _) + castNode(this) or + clearsContentCached(this, _) } } @@ -1275,7 +1276,7 @@ private module LocalFlowBigStep { config.isSource(node) or jumpStep(_, node, config) or additionalJumpStep(_, node, config) or - node instanceof ParameterNode or + node instanceof ParameterNodeExt or node instanceof OutNodeExt or store(_, _, node, _) or read(_, _, node) or @@ -1321,21 +1322,21 @@ private module LocalFlowBigStep { Node node1, Node node2, boolean preservesValue, DataFlowType t, Configuration config, LocalCallContext cc ) { - not isUnreachableInCall(node2, cc.(LocalCallContextSpecificCall).getCall()) and + not isUnreachableInCallCached(node2, cc.(LocalCallContextSpecificCall).getCall()) and ( localFlowEntry(node1, pragma[only_bind_into](config)) and ( localFlowStepNodeCand1(node1, node2, config) and preservesValue = true and - t = getNodeType(node1) + t = getNodeDataFlowType(node1) or additionalLocalFlowStepNodeCand2(node1, node2, config) and preservesValue = false and - t = getNodeType(node2) + t = getNodeDataFlowType(node2) ) and node1 != node2 and cc.relevantFor(getNodeEnclosingCallable(node1)) and - not isUnreachableInCall(node1, cc.(LocalCallContextSpecificCall).getCall()) and + not isUnreachableInCallCached(node1, cc.(LocalCallContextSpecificCall).getCall()) and Stage2::revFlow(node2, pragma[only_bind_into](config)) or exists(Node mid | @@ -1350,7 +1351,7 @@ private module LocalFlowBigStep { additionalLocalFlowStepNodeCand2(mid, node2, config) and not mid instanceof FlowCheckNode and preservesValue = false and - t = getNodeType(node2) and + t = getNodeDataFlowType(node2) and Stage2::revFlow(node2, pragma[only_bind_into](config)) ) ) @@ -1384,7 +1385,7 @@ private module Stage3 { private ApApprox getApprox(Ap ap) { result = ap.toBoolNonEmpty() } private ApNil getApNil(Node node) { - PrevStage::revFlow(node, _) and result = TFrontNil(getNodeType(node)) + PrevStage::revFlow(node, _) and result = TFrontNil(getNodeDataFlowType(node)) } bindingset[tc, tail] @@ -1443,7 +1444,9 @@ private module Stage3 { bindingset[node, ap] private predicate filter(Node node, Ap ap) { not ap.isClearedAt(node) and - if node instanceof CastingNode then compatibleTypes(getNodeType(node), ap.getType()) else any() + if node instanceof CastingNode + then compatibleTypes(getNodeDataFlowType(node), ap.getType()) + else any() } bindingset[ap, contentType] @@ -1583,10 +1586,10 @@ private module Stage3 { pragma[nomagic] private predicate fwdFlowIn( - DataFlowCall call, ParameterNode p, Cc outercc, Cc innercc, ApOption argAp, Ap ap, + DataFlowCall call, ParameterNodeExt p, Cc outercc, Cc innercc, ApOption argAp, Ap ap, Configuration config ) { - exists(ArgumentNode arg, boolean allowsFieldFlow | + exists(ArgumentNodeExt arg, boolean allowsFieldFlow | fwdFlow(arg, outercc, argAp, ap, config) and flowIntoCall(call, arg, p, allowsFieldFlow, config) and innercc = getCallContextCall(call, getNodeEnclosingCallable(p), outercc) @@ -1631,7 +1634,7 @@ private module Stage3 { private predicate fwdFlowIsEntered( DataFlowCall call, Cc cc, ApOption argAp, Ap ap, Configuration config ) { - exists(ParameterNode p | + exists(ParameterNodeExt p | fwdFlowIn(call, p, cc, _, argAp, ap, config) and PrevStage::parameterMayFlowThrough(p, _, unbindApa(getApprox(ap)), config) ) @@ -1772,10 +1775,10 @@ private module Stage3 { pragma[nomagic] private predicate revFlowIn( - DataFlowCall call, ArgumentNode arg, boolean toReturn, ApOption returnAp, Ap ap, + DataFlowCall call, ArgumentNodeExt arg, boolean toReturn, ApOption returnAp, Ap ap, Configuration config ) { - exists(ParameterNode p, boolean allowsFieldFlow | + exists(ParameterNodeExt p, boolean allowsFieldFlow | revFlow(p, toReturn, returnAp, ap, config) and flowIntoCall(call, arg, p, allowsFieldFlow, config) | @@ -1785,7 +1788,7 @@ private module Stage3 { pragma[nomagic] private predicate revFlowInToReturn( - DataFlowCall call, ArgumentNode arg, Ap returnAp, Ap ap, Configuration config + DataFlowCall call, ArgumentNodeExt arg, Ap returnAp, Ap ap, Configuration config ) { revFlowIn(call, arg, true, apSome(returnAp), ap, config) } @@ -1838,13 +1841,15 @@ private module Stage3 { pragma[noinline] private predicate parameterFlow( - ParameterNode p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config + ParameterNodeExt p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config ) { revFlow(p, true, apSome(ap0), ap, config) and c = getNodeEnclosingCallable(p) } - predicate parameterMayFlowThrough(ParameterNode p, DataFlowCallable c, Ap ap, Configuration config) { + predicate parameterMayFlowThrough( + ParameterNodeExt p, DataFlowCallable c, Ap ap, Configuration config + ) { exists(ReturnNodeExt ret, Ap ap0, ReturnKindExt kind, int pos | parameterFlow(p, ap, ap0, c, config) and c = getNodeEnclosingCallable(ret) and @@ -2088,7 +2093,7 @@ private module Stage4 { private ApApprox getApprox(Ap ap) { result = ap.getFront() } private ApNil getApNil(Node node) { - PrevStage::revFlow(node, _) and result = TNil(getNodeType(node)) + PrevStage::revFlow(node, _) and result = TNil(getNodeDataFlowType(node)) } bindingset[tc, tail] @@ -2155,7 +2160,7 @@ private module Stage4 { pragma[nomagic] private predicate flowIntoCall( - DataFlowCall call, ArgumentNode node1, ParameterNode node2, boolean allowsFieldFlow, + DataFlowCall call, ArgumentNodeExt node1, ParameterNodeExt node2, boolean allowsFieldFlow, Configuration config ) { flowIntoCallNodeCand2(call, node1, node2, allowsFieldFlow, config) and @@ -2300,10 +2305,10 @@ private module Stage4 { pragma[nomagic] private predicate fwdFlowIn( - DataFlowCall call, ParameterNode p, Cc outercc, Cc innercc, ApOption argAp, Ap ap, + DataFlowCall call, ParameterNodeExt p, Cc outercc, Cc innercc, ApOption argAp, Ap ap, Configuration config ) { - exists(ArgumentNode arg, boolean allowsFieldFlow | + exists(ArgumentNodeExt arg, boolean allowsFieldFlow | fwdFlow(arg, outercc, argAp, ap, config) and flowIntoCall(call, arg, p, allowsFieldFlow, config) and innercc = getCallContextCall(call, getNodeEnclosingCallable(p), outercc) @@ -2348,7 +2353,7 @@ private module Stage4 { private predicate fwdFlowIsEntered( DataFlowCall call, Cc cc, ApOption argAp, Ap ap, Configuration config ) { - exists(ParameterNode p | + exists(ParameterNodeExt p | fwdFlowIn(call, p, cc, _, argAp, ap, config) and PrevStage::parameterMayFlowThrough(p, _, unbindApa(getApprox(ap)), config) ) @@ -2489,10 +2494,10 @@ private module Stage4 { pragma[nomagic] private predicate revFlowIn( - DataFlowCall call, ArgumentNode arg, boolean toReturn, ApOption returnAp, Ap ap, + DataFlowCall call, ArgumentNodeExt arg, boolean toReturn, ApOption returnAp, Ap ap, Configuration config ) { - exists(ParameterNode p, boolean allowsFieldFlow | + exists(ParameterNodeExt p, boolean allowsFieldFlow | revFlow(p, toReturn, returnAp, ap, config) and flowIntoCall(call, arg, p, allowsFieldFlow, config) | @@ -2502,7 +2507,7 @@ private module Stage4 { pragma[nomagic] private predicate revFlowInToReturn( - DataFlowCall call, ArgumentNode arg, Ap returnAp, Ap ap, Configuration config + DataFlowCall call, ArgumentNodeExt arg, Ap returnAp, Ap ap, Configuration config ) { revFlowIn(call, arg, true, apSome(returnAp), ap, config) } @@ -2555,13 +2560,15 @@ private module Stage4 { pragma[noinline] private predicate parameterFlow( - ParameterNode p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config + ParameterNodeExt p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config ) { revFlow(p, true, apSome(ap0), ap, config) and c = getNodeEnclosingCallable(p) } - predicate parameterMayFlowThrough(ParameterNode p, DataFlowCallable c, Ap ap, Configuration config) { + predicate parameterMayFlowThrough( + ParameterNodeExt p, DataFlowCallable c, Ap ap, Configuration config + ) { exists(ReturnNodeExt ret, Ap ap0, ReturnKindExt kind, int pos | parameterFlow(p, ap, ap0, c, config) and c = getNodeEnclosingCallable(ret) and @@ -2606,7 +2613,7 @@ private predicate nodeMayUseSummary(Node n, AccessPathApprox apa, Configuration private newtype TSummaryCtx = TSummaryCtxNone() or - TSummaryCtxSome(ParameterNode p, AccessPath ap) { + TSummaryCtxSome(ParameterNodeExt p, AccessPath ap) { Stage4::parameterMayFlowThrough(p, _, ap.getApprox(), _) } @@ -2627,7 +2634,7 @@ private class SummaryCtxNone extends SummaryCtx, TSummaryCtxNone { /** A summary context from which a flow summary can be generated. */ private class SummaryCtxSome extends SummaryCtx, TSummaryCtxSome { - private ParameterNode p; + private ParameterNodeExt p; private AccessPath ap; SummaryCtxSome() { this = TSummaryCtxSome(p, ap) } @@ -2758,7 +2765,7 @@ private newtype TPathNode = config.isSource(node) and cc instanceof CallContextAny and sc instanceof SummaryCtxNone and - ap = TAccessPathNil(getNodeType(node)) + ap = TAccessPathNil(getNodeDataFlowType(node)) or // ... or a step from an existing PathNode to another node. exists(PathNodeMid mid | @@ -2979,7 +2986,7 @@ class PathNode extends TPathNode { Configuration getConfiguration() { none() } private predicate isHidden() { - nodeIsHidden(this.getNode()) and + hiddenNode(this.getNode()) and not this.isSource() and not this instanceof PathNodeSink } @@ -3148,7 +3155,7 @@ private predicate pathStep(PathNodeMid mid, Node node, CallContext cc, SummaryCt cc instanceof CallContextAny and sc instanceof SummaryCtxNone and mid.getAp() instanceof AccessPathNil and - ap = TAccessPathNil(getNodeType(node)) + ap = TAccessPathNil(getNodeDataFlowType(node)) or exists(TypedContent tc | pathStoreStep(mid, node, ap.pop(tc), tc, cc)) and sc = mid.getSummaryCtx() @@ -3235,7 +3242,7 @@ pragma[noinline] private predicate pathIntoArg( PathNodeMid mid, int i, CallContext cc, DataFlowCall call, AccessPath ap, AccessPathApprox apa ) { - exists(ArgumentNode arg | + exists(ArgumentNodeExt arg | arg = mid.getNode() and cc = mid.getCallContext() and arg.argumentOf(call, i) and @@ -3248,7 +3255,7 @@ pragma[noinline] private predicate parameterCand( DataFlowCallable callable, int i, AccessPathApprox apa, Configuration config ) { - exists(ParameterNode p | + exists(ParameterNodeExt p | Stage4::revFlow(p, _, _, apa, config) and p.isParameterOf(callable, i) ) @@ -3272,7 +3279,7 @@ private predicate pathIntoCallable0( * respectively. */ private predicate pathIntoCallable( - PathNodeMid mid, ParameterNode p, CallContext outercc, CallContextCall innercc, SummaryCtx sc, + PathNodeMid mid, ParameterNodeExt p, CallContext outercc, CallContextCall innercc, SummaryCtx sc, DataFlowCall call ) { exists(int i, DataFlowCallable callable, AccessPath ap | @@ -3568,7 +3575,7 @@ private module FlowExploration { private newtype TSummaryCtx1 = TSummaryCtx1None() or - TSummaryCtx1Param(ParameterNode p) + TSummaryCtx1Param(ParameterNodeExt p) private newtype TSummaryCtx2 = TSummaryCtx2None() or @@ -3591,7 +3598,7 @@ private module FlowExploration { cc instanceof CallContextAny and sc1 = TSummaryCtx1None() and sc2 = TSummaryCtx2None() and - ap = TPartialNil(getNodeType(node)) and + ap = TPartialNil(getNodeDataFlowType(node)) and not fullBarrier(node, config) and exists(config.explorationLimit()) or @@ -3611,7 +3618,7 @@ private module FlowExploration { or exists(PartialPathNodeRev mid | revPartialPathStep(mid, node, sc1, sc2, ap, config) and - not clearsContent(node, ap.getHead()) and + not clearsContentCached(node, ap.getHead()) and not fullBarrier(node, config) and distSink(getNodeEnclosingCallable(node), config) <= config.explorationLimit() ) @@ -3625,9 +3632,9 @@ private module FlowExploration { exists(PartialPathNodeFwd mid | partialPathStep(mid, node, cc, sc1, sc2, ap, config) and not fullBarrier(node, config) and - not clearsContent(node, ap.getHead().getContent()) and + not clearsContentCached(node, ap.getHead().getContent()) and if node instanceof CastingNode - then compatibleTypes(getNodeType(node), ap.getType()) + then compatibleTypes(getNodeDataFlowType(node), ap.getType()) else any() ) } @@ -3783,7 +3790,7 @@ private module FlowExploration { PartialPathNodeFwd mid, Node node, CallContext cc, TSummaryCtx1 sc1, TSummaryCtx2 sc2, PartialAccessPath ap, Configuration config ) { - not isUnreachableInCall(node, cc.(CallContextSpecificCall).getCall()) and + not isUnreachableInCallCached(node, cc.(CallContextSpecificCall).getCall()) and ( localFlowStep(mid.getNode(), node, config) and cc = mid.getCallContext() and @@ -3797,7 +3804,7 @@ private module FlowExploration { sc1 = mid.getSummaryCtx1() and sc2 = mid.getSummaryCtx2() and mid.getAp() instanceof PartialAccessPathNil and - ap = TPartialNil(getNodeType(node)) and + ap = TPartialNil(getNodeDataFlowType(node)) and config = mid.getConfiguration() ) or @@ -3813,7 +3820,7 @@ private module FlowExploration { sc1 = TSummaryCtx1None() and sc2 = TSummaryCtx2None() and mid.getAp() instanceof PartialAccessPathNil and - ap = TPartialNil(getNodeType(node)) and + ap = TPartialNil(getNodeDataFlowType(node)) and config = mid.getConfiguration() or partialPathStoreStep(mid, _, _, node, ap) and @@ -3827,7 +3834,7 @@ private module FlowExploration { sc1 = mid.getSummaryCtx1() and sc2 = mid.getSummaryCtx2() and apConsFwd(ap, tc, ap0, config) and - compatibleTypes(ap.getType(), getNodeType(node)) + compatibleTypes(ap.getType(), getNodeDataFlowType(node)) ) or partialPathIntoCallable(mid, node, _, cc, sc1, sc2, _, ap, config) @@ -3924,7 +3931,7 @@ private module FlowExploration { PartialPathNodeFwd mid, int i, CallContext cc, DataFlowCall call, PartialAccessPath ap, Configuration config ) { - exists(ArgumentNode arg | + exists(ArgumentNodeExt arg | arg = mid.getNode() and cc = mid.getCallContext() and arg.argumentOf(call, i) and @@ -3943,7 +3950,7 @@ private module FlowExploration { } private predicate partialPathIntoCallable( - PartialPathNodeFwd mid, ParameterNode p, CallContext outercc, CallContextCall innercc, + PartialPathNodeFwd mid, ParameterNodeExt p, CallContext outercc, CallContextCall innercc, TSummaryCtx1 sc1, TSummaryCtx2 sc2, DataFlowCall call, PartialAccessPath ap, Configuration config ) { @@ -3980,7 +3987,7 @@ private module FlowExploration { DataFlowCall call, PartialPathNodeFwd mid, ReturnKindExt kind, CallContext cc, PartialAccessPath ap, Configuration config ) { - exists(ParameterNode p, CallContext innercc, TSummaryCtx1 sc1, TSummaryCtx2 sc2 | + exists(ParameterNodeExt p, CallContext innercc, TSummaryCtx1 sc1, TSummaryCtx2 sc2 | partialPathIntoCallable(mid, p, cc, innercc, sc1, sc2, call, _, config) and paramFlowsThroughInPartialPath(kind, innercc, sc1, sc2, ap, config) ) @@ -4037,7 +4044,7 @@ private module FlowExploration { apConsRev(ap, c, ap0, config) ) or - exists(ParameterNode p | + exists(ParameterNodeExt p | mid.getNode() = p and viableParamArg(_, p, node) and sc1 = mid.getSummaryCtx1() and @@ -4115,7 +4122,7 @@ private module FlowExploration { int pos, TRevSummaryCtx1Some sc1, TRevSummaryCtx2Some sc2, RevPartialAccessPath ap, Configuration config ) { - exists(PartialPathNodeRev mid, ParameterNode p | + exists(PartialPathNodeRev mid, ParameterNodeExt p | mid.getNode() = p and p.isParameterOf(_, pos) and sc1 = mid.getSummaryCtx1() and @@ -4138,7 +4145,7 @@ private module FlowExploration { pragma[nomagic] private predicate revPartialPathThroughCallable( - PartialPathNodeRev mid, ArgumentNode node, RevPartialAccessPath ap, Configuration config + PartialPathNodeRev mid, ArgumentNodeExt node, RevPartialAccessPath ap, Configuration config ) { exists(DataFlowCall call, int pos | revPartialPathThroughCallable0(call, mid, pos, ap, config) and 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 9498e51e7e6..cf83530bcbd 100644 --- a/cpp/ql/src/semmle/code/cpp/dataflow/internal/DataFlowImpl3.qll +++ b/cpp/ql/src/semmle/code/cpp/dataflow/internal/DataFlowImpl3.qll @@ -211,10 +211,7 @@ private predicate fullBarrier(Node node, Configuration config) { * Holds if data can flow in one local step from `node1` to `node2`. */ private predicate localFlowStep(Node node1, Node node2, Configuration config) { - ( - simpleLocalFlowStep(node1, node2) or - reverseStepThroughInputOutputAlias(node1, node2) - ) and + simpleLocalFlowStepExt(node1, node2) and not outBarrier(node1, config) and not inBarrier(node2, config) and not fullBarrier(node1, config) and @@ -237,7 +234,7 @@ private predicate additionalLocalFlowStep(Node node1, Node node2, Configuration * Holds if data can flow from `node1` to `node2` in a way that discards call contexts. */ private predicate jumpStep(Node node1, Node node2, Configuration config) { - jumpStep(node1, node2) and + jumpStepCached(node1, node2) and not outBarrier(node1, config) and not inBarrier(node2, config) and not fullBarrier(node1, config) and @@ -388,7 +385,7 @@ private module Stage1 { */ pragma[nomagic] private predicate fwdFlowIsEntered(DataFlowCall call, Cc cc, Configuration config) { - exists(ArgumentNode arg | + exists(ArgumentNodeExt arg | fwdFlow(arg, cc, config) and viableParamArg(call, _, arg) ) @@ -515,7 +512,7 @@ private module Stage1 { pragma[nomagic] predicate viableParamArgNodeCandFwd1( - DataFlowCall call, ParameterNode p, ArgumentNode arg, Configuration config + DataFlowCall call, ParameterNodeExt p, ArgumentNodeExt arg, Configuration config ) { viableParamArg(call, p, arg) and fwdFlow(arg, config) @@ -523,16 +520,16 @@ private module Stage1 { pragma[nomagic] private predicate revFlowIn( - DataFlowCall call, ArgumentNode arg, boolean toReturn, Configuration config + DataFlowCall call, ArgumentNodeExt arg, boolean toReturn, Configuration config ) { - exists(ParameterNode p | + exists(ParameterNodeExt p | revFlow(p, toReturn, config) and viableParamArgNodeCandFwd1(call, p, arg, config) ) } pragma[nomagic] - private predicate revFlowInToReturn(DataFlowCall call, ArgumentNode arg, Configuration config) { + private predicate revFlowInToReturn(DataFlowCall call, ArgumentNodeExt arg, Configuration config) { revFlowIn(call, arg, true, config) } @@ -597,7 +594,9 @@ private module Stage1 { * Holds if flow may enter through `p` and reach a return node making `p` a * candidate for the origin of a summary. */ - predicate parameterMayFlowThrough(ParameterNode p, DataFlowCallable c, Ap ap, Configuration config) { + predicate parameterMayFlowThrough( + ParameterNodeExt p, DataFlowCallable c, Ap ap, Configuration config + ) { exists(ReturnKindExt kind | throughFlowNodeCand(p, config) and returnFlowCallableNodeCand(c, kind, config) and @@ -663,7 +662,7 @@ private predicate flowOutOfCallNodeCand1( pragma[nomagic] private predicate viableParamArgNodeCand1( - DataFlowCall call, ParameterNode p, ArgumentNode arg, Configuration config + DataFlowCall call, ParameterNodeExt p, ArgumentNodeExt arg, Configuration config ) { Stage1::viableParamArgNodeCandFwd1(call, p, arg, config) and Stage1::revFlow(arg, config) @@ -675,7 +674,7 @@ private predicate viableParamArgNodeCand1( */ pragma[nomagic] private predicate flowIntoCallNodeCand1( - DataFlowCall call, ArgumentNode arg, ParameterNode p, Configuration config + DataFlowCall call, ArgumentNodeExt arg, ParameterNodeExt p, Configuration config ) { viableParamArgNodeCand1(call, p, arg, config) and Stage1::revFlow(p, config) and @@ -735,7 +734,7 @@ private predicate flowOutOfCallNodeCand1( */ pragma[nomagic] private predicate flowIntoCallNodeCand1( - DataFlowCall call, ArgumentNode arg, ParameterNode p, boolean allowsFieldFlow, + DataFlowCall call, ArgumentNodeExt arg, ParameterNodeExt p, boolean allowsFieldFlow, Configuration config ) { flowIntoCallNodeCand1(call, arg, p, config) and @@ -944,10 +943,10 @@ private module Stage2 { pragma[nomagic] private predicate fwdFlowIn( - DataFlowCall call, ParameterNode p, Cc outercc, Cc innercc, ApOption argAp, Ap ap, + DataFlowCall call, ParameterNodeExt p, Cc outercc, Cc innercc, ApOption argAp, Ap ap, Configuration config ) { - exists(ArgumentNode arg, boolean allowsFieldFlow | + exists(ArgumentNodeExt arg, boolean allowsFieldFlow | fwdFlow(arg, outercc, argAp, ap, config) and flowIntoCall(call, arg, p, allowsFieldFlow, config) and innercc = getCallContextCall(call, getNodeEnclosingCallable(p), outercc) @@ -992,7 +991,7 @@ private module Stage2 { private predicate fwdFlowIsEntered( DataFlowCall call, Cc cc, ApOption argAp, Ap ap, Configuration config ) { - exists(ParameterNode p | + exists(ParameterNodeExt p | fwdFlowIn(call, p, cc, _, argAp, ap, config) and PrevStage::parameterMayFlowThrough(p, _, getApprox(ap), config) ) @@ -1133,10 +1132,10 @@ private module Stage2 { pragma[nomagic] private predicate revFlowIn( - DataFlowCall call, ArgumentNode arg, boolean toReturn, ApOption returnAp, Ap ap, + DataFlowCall call, ArgumentNodeExt arg, boolean toReturn, ApOption returnAp, Ap ap, Configuration config ) { - exists(ParameterNode p, boolean allowsFieldFlow | + exists(ParameterNodeExt p, boolean allowsFieldFlow | revFlow(p, toReturn, returnAp, ap, config) and flowIntoCall(call, arg, p, allowsFieldFlow, config) | @@ -1146,7 +1145,7 @@ private module Stage2 { pragma[nomagic] private predicate revFlowInToReturn( - DataFlowCall call, ArgumentNode arg, Ap returnAp, Ap ap, Configuration config + DataFlowCall call, ArgumentNodeExt arg, Ap returnAp, Ap ap, Configuration config ) { revFlowIn(call, arg, true, apSome(returnAp), ap, config) } @@ -1199,13 +1198,15 @@ private module Stage2 { pragma[noinline] private predicate parameterFlow( - ParameterNode p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config + ParameterNodeExt p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config ) { revFlow(p, true, apSome(ap0), ap, config) and c = getNodeEnclosingCallable(p) } - predicate parameterMayFlowThrough(ParameterNode p, DataFlowCallable c, Ap ap, Configuration config) { + predicate parameterMayFlowThrough( + ParameterNodeExt p, DataFlowCallable c, Ap ap, Configuration config + ) { exists(ReturnNodeExt ret, Ap ap0, ReturnKindExt kind, int pos | parameterFlow(p, ap, ap0, c, config) and c = getNodeEnclosingCallable(ret) and @@ -1245,7 +1246,7 @@ private predicate flowOutOfCallNodeCand2( pragma[nomagic] private predicate flowIntoCallNodeCand2( - DataFlowCall call, ArgumentNode node1, ParameterNode node2, boolean allowsFieldFlow, + DataFlowCall call, ArgumentNodeExt node1, ParameterNodeExt node2, boolean allowsFieldFlow, Configuration config ) { flowIntoCallNodeCand1(call, node1, node2, allowsFieldFlow, config) and @@ -1260,8 +1261,8 @@ private module LocalFlowBigStep { */ private class FlowCheckNode extends Node { FlowCheckNode() { - this instanceof CastNode or - clearsContent(this, _) + castNode(this) or + clearsContentCached(this, _) } } @@ -1275,7 +1276,7 @@ private module LocalFlowBigStep { config.isSource(node) or jumpStep(_, node, config) or additionalJumpStep(_, node, config) or - node instanceof ParameterNode or + node instanceof ParameterNodeExt or node instanceof OutNodeExt or store(_, _, node, _) or read(_, _, node) or @@ -1321,21 +1322,21 @@ private module LocalFlowBigStep { Node node1, Node node2, boolean preservesValue, DataFlowType t, Configuration config, LocalCallContext cc ) { - not isUnreachableInCall(node2, cc.(LocalCallContextSpecificCall).getCall()) and + not isUnreachableInCallCached(node2, cc.(LocalCallContextSpecificCall).getCall()) and ( localFlowEntry(node1, pragma[only_bind_into](config)) and ( localFlowStepNodeCand1(node1, node2, config) and preservesValue = true and - t = getNodeType(node1) + t = getNodeDataFlowType(node1) or additionalLocalFlowStepNodeCand2(node1, node2, config) and preservesValue = false and - t = getNodeType(node2) + t = getNodeDataFlowType(node2) ) and node1 != node2 and cc.relevantFor(getNodeEnclosingCallable(node1)) and - not isUnreachableInCall(node1, cc.(LocalCallContextSpecificCall).getCall()) and + not isUnreachableInCallCached(node1, cc.(LocalCallContextSpecificCall).getCall()) and Stage2::revFlow(node2, pragma[only_bind_into](config)) or exists(Node mid | @@ -1350,7 +1351,7 @@ private module LocalFlowBigStep { additionalLocalFlowStepNodeCand2(mid, node2, config) and not mid instanceof FlowCheckNode and preservesValue = false and - t = getNodeType(node2) and + t = getNodeDataFlowType(node2) and Stage2::revFlow(node2, pragma[only_bind_into](config)) ) ) @@ -1384,7 +1385,7 @@ private module Stage3 { private ApApprox getApprox(Ap ap) { result = ap.toBoolNonEmpty() } private ApNil getApNil(Node node) { - PrevStage::revFlow(node, _) and result = TFrontNil(getNodeType(node)) + PrevStage::revFlow(node, _) and result = TFrontNil(getNodeDataFlowType(node)) } bindingset[tc, tail] @@ -1443,7 +1444,9 @@ private module Stage3 { bindingset[node, ap] private predicate filter(Node node, Ap ap) { not ap.isClearedAt(node) and - if node instanceof CastingNode then compatibleTypes(getNodeType(node), ap.getType()) else any() + if node instanceof CastingNode + then compatibleTypes(getNodeDataFlowType(node), ap.getType()) + else any() } bindingset[ap, contentType] @@ -1583,10 +1586,10 @@ private module Stage3 { pragma[nomagic] private predicate fwdFlowIn( - DataFlowCall call, ParameterNode p, Cc outercc, Cc innercc, ApOption argAp, Ap ap, + DataFlowCall call, ParameterNodeExt p, Cc outercc, Cc innercc, ApOption argAp, Ap ap, Configuration config ) { - exists(ArgumentNode arg, boolean allowsFieldFlow | + exists(ArgumentNodeExt arg, boolean allowsFieldFlow | fwdFlow(arg, outercc, argAp, ap, config) and flowIntoCall(call, arg, p, allowsFieldFlow, config) and innercc = getCallContextCall(call, getNodeEnclosingCallable(p), outercc) @@ -1631,7 +1634,7 @@ private module Stage3 { private predicate fwdFlowIsEntered( DataFlowCall call, Cc cc, ApOption argAp, Ap ap, Configuration config ) { - exists(ParameterNode p | + exists(ParameterNodeExt p | fwdFlowIn(call, p, cc, _, argAp, ap, config) and PrevStage::parameterMayFlowThrough(p, _, unbindApa(getApprox(ap)), config) ) @@ -1772,10 +1775,10 @@ private module Stage3 { pragma[nomagic] private predicate revFlowIn( - DataFlowCall call, ArgumentNode arg, boolean toReturn, ApOption returnAp, Ap ap, + DataFlowCall call, ArgumentNodeExt arg, boolean toReturn, ApOption returnAp, Ap ap, Configuration config ) { - exists(ParameterNode p, boolean allowsFieldFlow | + exists(ParameterNodeExt p, boolean allowsFieldFlow | revFlow(p, toReturn, returnAp, ap, config) and flowIntoCall(call, arg, p, allowsFieldFlow, config) | @@ -1785,7 +1788,7 @@ private module Stage3 { pragma[nomagic] private predicate revFlowInToReturn( - DataFlowCall call, ArgumentNode arg, Ap returnAp, Ap ap, Configuration config + DataFlowCall call, ArgumentNodeExt arg, Ap returnAp, Ap ap, Configuration config ) { revFlowIn(call, arg, true, apSome(returnAp), ap, config) } @@ -1838,13 +1841,15 @@ private module Stage3 { pragma[noinline] private predicate parameterFlow( - ParameterNode p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config + ParameterNodeExt p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config ) { revFlow(p, true, apSome(ap0), ap, config) and c = getNodeEnclosingCallable(p) } - predicate parameterMayFlowThrough(ParameterNode p, DataFlowCallable c, Ap ap, Configuration config) { + predicate parameterMayFlowThrough( + ParameterNodeExt p, DataFlowCallable c, Ap ap, Configuration config + ) { exists(ReturnNodeExt ret, Ap ap0, ReturnKindExt kind, int pos | parameterFlow(p, ap, ap0, c, config) and c = getNodeEnclosingCallable(ret) and @@ -2088,7 +2093,7 @@ private module Stage4 { private ApApprox getApprox(Ap ap) { result = ap.getFront() } private ApNil getApNil(Node node) { - PrevStage::revFlow(node, _) and result = TNil(getNodeType(node)) + PrevStage::revFlow(node, _) and result = TNil(getNodeDataFlowType(node)) } bindingset[tc, tail] @@ -2155,7 +2160,7 @@ private module Stage4 { pragma[nomagic] private predicate flowIntoCall( - DataFlowCall call, ArgumentNode node1, ParameterNode node2, boolean allowsFieldFlow, + DataFlowCall call, ArgumentNodeExt node1, ParameterNodeExt node2, boolean allowsFieldFlow, Configuration config ) { flowIntoCallNodeCand2(call, node1, node2, allowsFieldFlow, config) and @@ -2300,10 +2305,10 @@ private module Stage4 { pragma[nomagic] private predicate fwdFlowIn( - DataFlowCall call, ParameterNode p, Cc outercc, Cc innercc, ApOption argAp, Ap ap, + DataFlowCall call, ParameterNodeExt p, Cc outercc, Cc innercc, ApOption argAp, Ap ap, Configuration config ) { - exists(ArgumentNode arg, boolean allowsFieldFlow | + exists(ArgumentNodeExt arg, boolean allowsFieldFlow | fwdFlow(arg, outercc, argAp, ap, config) and flowIntoCall(call, arg, p, allowsFieldFlow, config) and innercc = getCallContextCall(call, getNodeEnclosingCallable(p), outercc) @@ -2348,7 +2353,7 @@ private module Stage4 { private predicate fwdFlowIsEntered( DataFlowCall call, Cc cc, ApOption argAp, Ap ap, Configuration config ) { - exists(ParameterNode p | + exists(ParameterNodeExt p | fwdFlowIn(call, p, cc, _, argAp, ap, config) and PrevStage::parameterMayFlowThrough(p, _, unbindApa(getApprox(ap)), config) ) @@ -2489,10 +2494,10 @@ private module Stage4 { pragma[nomagic] private predicate revFlowIn( - DataFlowCall call, ArgumentNode arg, boolean toReturn, ApOption returnAp, Ap ap, + DataFlowCall call, ArgumentNodeExt arg, boolean toReturn, ApOption returnAp, Ap ap, Configuration config ) { - exists(ParameterNode p, boolean allowsFieldFlow | + exists(ParameterNodeExt p, boolean allowsFieldFlow | revFlow(p, toReturn, returnAp, ap, config) and flowIntoCall(call, arg, p, allowsFieldFlow, config) | @@ -2502,7 +2507,7 @@ private module Stage4 { pragma[nomagic] private predicate revFlowInToReturn( - DataFlowCall call, ArgumentNode arg, Ap returnAp, Ap ap, Configuration config + DataFlowCall call, ArgumentNodeExt arg, Ap returnAp, Ap ap, Configuration config ) { revFlowIn(call, arg, true, apSome(returnAp), ap, config) } @@ -2555,13 +2560,15 @@ private module Stage4 { pragma[noinline] private predicate parameterFlow( - ParameterNode p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config + ParameterNodeExt p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config ) { revFlow(p, true, apSome(ap0), ap, config) and c = getNodeEnclosingCallable(p) } - predicate parameterMayFlowThrough(ParameterNode p, DataFlowCallable c, Ap ap, Configuration config) { + predicate parameterMayFlowThrough( + ParameterNodeExt p, DataFlowCallable c, Ap ap, Configuration config + ) { exists(ReturnNodeExt ret, Ap ap0, ReturnKindExt kind, int pos | parameterFlow(p, ap, ap0, c, config) and c = getNodeEnclosingCallable(ret) and @@ -2606,7 +2613,7 @@ private predicate nodeMayUseSummary(Node n, AccessPathApprox apa, Configuration private newtype TSummaryCtx = TSummaryCtxNone() or - TSummaryCtxSome(ParameterNode p, AccessPath ap) { + TSummaryCtxSome(ParameterNodeExt p, AccessPath ap) { Stage4::parameterMayFlowThrough(p, _, ap.getApprox(), _) } @@ -2627,7 +2634,7 @@ private class SummaryCtxNone extends SummaryCtx, TSummaryCtxNone { /** A summary context from which a flow summary can be generated. */ private class SummaryCtxSome extends SummaryCtx, TSummaryCtxSome { - private ParameterNode p; + private ParameterNodeExt p; private AccessPath ap; SummaryCtxSome() { this = TSummaryCtxSome(p, ap) } @@ -2758,7 +2765,7 @@ private newtype TPathNode = config.isSource(node) and cc instanceof CallContextAny and sc instanceof SummaryCtxNone and - ap = TAccessPathNil(getNodeType(node)) + ap = TAccessPathNil(getNodeDataFlowType(node)) or // ... or a step from an existing PathNode to another node. exists(PathNodeMid mid | @@ -2979,7 +2986,7 @@ class PathNode extends TPathNode { Configuration getConfiguration() { none() } private predicate isHidden() { - nodeIsHidden(this.getNode()) and + hiddenNode(this.getNode()) and not this.isSource() and not this instanceof PathNodeSink } @@ -3148,7 +3155,7 @@ private predicate pathStep(PathNodeMid mid, Node node, CallContext cc, SummaryCt cc instanceof CallContextAny and sc instanceof SummaryCtxNone and mid.getAp() instanceof AccessPathNil and - ap = TAccessPathNil(getNodeType(node)) + ap = TAccessPathNil(getNodeDataFlowType(node)) or exists(TypedContent tc | pathStoreStep(mid, node, ap.pop(tc), tc, cc)) and sc = mid.getSummaryCtx() @@ -3235,7 +3242,7 @@ pragma[noinline] private predicate pathIntoArg( PathNodeMid mid, int i, CallContext cc, DataFlowCall call, AccessPath ap, AccessPathApprox apa ) { - exists(ArgumentNode arg | + exists(ArgumentNodeExt arg | arg = mid.getNode() and cc = mid.getCallContext() and arg.argumentOf(call, i) and @@ -3248,7 +3255,7 @@ pragma[noinline] private predicate parameterCand( DataFlowCallable callable, int i, AccessPathApprox apa, Configuration config ) { - exists(ParameterNode p | + exists(ParameterNodeExt p | Stage4::revFlow(p, _, _, apa, config) and p.isParameterOf(callable, i) ) @@ -3272,7 +3279,7 @@ private predicate pathIntoCallable0( * respectively. */ private predicate pathIntoCallable( - PathNodeMid mid, ParameterNode p, CallContext outercc, CallContextCall innercc, SummaryCtx sc, + PathNodeMid mid, ParameterNodeExt p, CallContext outercc, CallContextCall innercc, SummaryCtx sc, DataFlowCall call ) { exists(int i, DataFlowCallable callable, AccessPath ap | @@ -3568,7 +3575,7 @@ private module FlowExploration { private newtype TSummaryCtx1 = TSummaryCtx1None() or - TSummaryCtx1Param(ParameterNode p) + TSummaryCtx1Param(ParameterNodeExt p) private newtype TSummaryCtx2 = TSummaryCtx2None() or @@ -3591,7 +3598,7 @@ private module FlowExploration { cc instanceof CallContextAny and sc1 = TSummaryCtx1None() and sc2 = TSummaryCtx2None() and - ap = TPartialNil(getNodeType(node)) and + ap = TPartialNil(getNodeDataFlowType(node)) and not fullBarrier(node, config) and exists(config.explorationLimit()) or @@ -3611,7 +3618,7 @@ private module FlowExploration { or exists(PartialPathNodeRev mid | revPartialPathStep(mid, node, sc1, sc2, ap, config) and - not clearsContent(node, ap.getHead()) and + not clearsContentCached(node, ap.getHead()) and not fullBarrier(node, config) and distSink(getNodeEnclosingCallable(node), config) <= config.explorationLimit() ) @@ -3625,9 +3632,9 @@ private module FlowExploration { exists(PartialPathNodeFwd mid | partialPathStep(mid, node, cc, sc1, sc2, ap, config) and not fullBarrier(node, config) and - not clearsContent(node, ap.getHead().getContent()) and + not clearsContentCached(node, ap.getHead().getContent()) and if node instanceof CastingNode - then compatibleTypes(getNodeType(node), ap.getType()) + then compatibleTypes(getNodeDataFlowType(node), ap.getType()) else any() ) } @@ -3783,7 +3790,7 @@ private module FlowExploration { PartialPathNodeFwd mid, Node node, CallContext cc, TSummaryCtx1 sc1, TSummaryCtx2 sc2, PartialAccessPath ap, Configuration config ) { - not isUnreachableInCall(node, cc.(CallContextSpecificCall).getCall()) and + not isUnreachableInCallCached(node, cc.(CallContextSpecificCall).getCall()) and ( localFlowStep(mid.getNode(), node, config) and cc = mid.getCallContext() and @@ -3797,7 +3804,7 @@ private module FlowExploration { sc1 = mid.getSummaryCtx1() and sc2 = mid.getSummaryCtx2() and mid.getAp() instanceof PartialAccessPathNil and - ap = TPartialNil(getNodeType(node)) and + ap = TPartialNil(getNodeDataFlowType(node)) and config = mid.getConfiguration() ) or @@ -3813,7 +3820,7 @@ private module FlowExploration { sc1 = TSummaryCtx1None() and sc2 = TSummaryCtx2None() and mid.getAp() instanceof PartialAccessPathNil and - ap = TPartialNil(getNodeType(node)) and + ap = TPartialNil(getNodeDataFlowType(node)) and config = mid.getConfiguration() or partialPathStoreStep(mid, _, _, node, ap) and @@ -3827,7 +3834,7 @@ private module FlowExploration { sc1 = mid.getSummaryCtx1() and sc2 = mid.getSummaryCtx2() and apConsFwd(ap, tc, ap0, config) and - compatibleTypes(ap.getType(), getNodeType(node)) + compatibleTypes(ap.getType(), getNodeDataFlowType(node)) ) or partialPathIntoCallable(mid, node, _, cc, sc1, sc2, _, ap, config) @@ -3924,7 +3931,7 @@ private module FlowExploration { PartialPathNodeFwd mid, int i, CallContext cc, DataFlowCall call, PartialAccessPath ap, Configuration config ) { - exists(ArgumentNode arg | + exists(ArgumentNodeExt arg | arg = mid.getNode() and cc = mid.getCallContext() and arg.argumentOf(call, i) and @@ -3943,7 +3950,7 @@ private module FlowExploration { } private predicate partialPathIntoCallable( - PartialPathNodeFwd mid, ParameterNode p, CallContext outercc, CallContextCall innercc, + PartialPathNodeFwd mid, ParameterNodeExt p, CallContext outercc, CallContextCall innercc, TSummaryCtx1 sc1, TSummaryCtx2 sc2, DataFlowCall call, PartialAccessPath ap, Configuration config ) { @@ -3980,7 +3987,7 @@ private module FlowExploration { DataFlowCall call, PartialPathNodeFwd mid, ReturnKindExt kind, CallContext cc, PartialAccessPath ap, Configuration config ) { - exists(ParameterNode p, CallContext innercc, TSummaryCtx1 sc1, TSummaryCtx2 sc2 | + exists(ParameterNodeExt p, CallContext innercc, TSummaryCtx1 sc1, TSummaryCtx2 sc2 | partialPathIntoCallable(mid, p, cc, innercc, sc1, sc2, call, _, config) and paramFlowsThroughInPartialPath(kind, innercc, sc1, sc2, ap, config) ) @@ -4037,7 +4044,7 @@ private module FlowExploration { apConsRev(ap, c, ap0, config) ) or - exists(ParameterNode p | + exists(ParameterNodeExt p | mid.getNode() = p and viableParamArg(_, p, node) and sc1 = mid.getSummaryCtx1() and @@ -4115,7 +4122,7 @@ private module FlowExploration { int pos, TRevSummaryCtx1Some sc1, TRevSummaryCtx2Some sc2, RevPartialAccessPath ap, Configuration config ) { - exists(PartialPathNodeRev mid, ParameterNode p | + exists(PartialPathNodeRev mid, ParameterNodeExt p | mid.getNode() = p and p.isParameterOf(_, pos) and sc1 = mid.getSummaryCtx1() and @@ -4138,7 +4145,7 @@ private module FlowExploration { pragma[nomagic] private predicate revPartialPathThroughCallable( - PartialPathNodeRev mid, ArgumentNode node, RevPartialAccessPath ap, Configuration config + PartialPathNodeRev mid, ArgumentNodeExt node, RevPartialAccessPath ap, Configuration config ) { exists(DataFlowCall call, int pos | revPartialPathThroughCallable0(call, mid, pos, ap, config) and 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 9498e51e7e6..cf83530bcbd 100644 --- a/cpp/ql/src/semmle/code/cpp/dataflow/internal/DataFlowImpl4.qll +++ b/cpp/ql/src/semmle/code/cpp/dataflow/internal/DataFlowImpl4.qll @@ -211,10 +211,7 @@ private predicate fullBarrier(Node node, Configuration config) { * Holds if data can flow in one local step from `node1` to `node2`. */ private predicate localFlowStep(Node node1, Node node2, Configuration config) { - ( - simpleLocalFlowStep(node1, node2) or - reverseStepThroughInputOutputAlias(node1, node2) - ) and + simpleLocalFlowStepExt(node1, node2) and not outBarrier(node1, config) and not inBarrier(node2, config) and not fullBarrier(node1, config) and @@ -237,7 +234,7 @@ private predicate additionalLocalFlowStep(Node node1, Node node2, Configuration * Holds if data can flow from `node1` to `node2` in a way that discards call contexts. */ private predicate jumpStep(Node node1, Node node2, Configuration config) { - jumpStep(node1, node2) and + jumpStepCached(node1, node2) and not outBarrier(node1, config) and not inBarrier(node2, config) and not fullBarrier(node1, config) and @@ -388,7 +385,7 @@ private module Stage1 { */ pragma[nomagic] private predicate fwdFlowIsEntered(DataFlowCall call, Cc cc, Configuration config) { - exists(ArgumentNode arg | + exists(ArgumentNodeExt arg | fwdFlow(arg, cc, config) and viableParamArg(call, _, arg) ) @@ -515,7 +512,7 @@ private module Stage1 { pragma[nomagic] predicate viableParamArgNodeCandFwd1( - DataFlowCall call, ParameterNode p, ArgumentNode arg, Configuration config + DataFlowCall call, ParameterNodeExt p, ArgumentNodeExt arg, Configuration config ) { viableParamArg(call, p, arg) and fwdFlow(arg, config) @@ -523,16 +520,16 @@ private module Stage1 { pragma[nomagic] private predicate revFlowIn( - DataFlowCall call, ArgumentNode arg, boolean toReturn, Configuration config + DataFlowCall call, ArgumentNodeExt arg, boolean toReturn, Configuration config ) { - exists(ParameterNode p | + exists(ParameterNodeExt p | revFlow(p, toReturn, config) and viableParamArgNodeCandFwd1(call, p, arg, config) ) } pragma[nomagic] - private predicate revFlowInToReturn(DataFlowCall call, ArgumentNode arg, Configuration config) { + private predicate revFlowInToReturn(DataFlowCall call, ArgumentNodeExt arg, Configuration config) { revFlowIn(call, arg, true, config) } @@ -597,7 +594,9 @@ private module Stage1 { * Holds if flow may enter through `p` and reach a return node making `p` a * candidate for the origin of a summary. */ - predicate parameterMayFlowThrough(ParameterNode p, DataFlowCallable c, Ap ap, Configuration config) { + predicate parameterMayFlowThrough( + ParameterNodeExt p, DataFlowCallable c, Ap ap, Configuration config + ) { exists(ReturnKindExt kind | throughFlowNodeCand(p, config) and returnFlowCallableNodeCand(c, kind, config) and @@ -663,7 +662,7 @@ private predicate flowOutOfCallNodeCand1( pragma[nomagic] private predicate viableParamArgNodeCand1( - DataFlowCall call, ParameterNode p, ArgumentNode arg, Configuration config + DataFlowCall call, ParameterNodeExt p, ArgumentNodeExt arg, Configuration config ) { Stage1::viableParamArgNodeCandFwd1(call, p, arg, config) and Stage1::revFlow(arg, config) @@ -675,7 +674,7 @@ private predicate viableParamArgNodeCand1( */ pragma[nomagic] private predicate flowIntoCallNodeCand1( - DataFlowCall call, ArgumentNode arg, ParameterNode p, Configuration config + DataFlowCall call, ArgumentNodeExt arg, ParameterNodeExt p, Configuration config ) { viableParamArgNodeCand1(call, p, arg, config) and Stage1::revFlow(p, config) and @@ -735,7 +734,7 @@ private predicate flowOutOfCallNodeCand1( */ pragma[nomagic] private predicate flowIntoCallNodeCand1( - DataFlowCall call, ArgumentNode arg, ParameterNode p, boolean allowsFieldFlow, + DataFlowCall call, ArgumentNodeExt arg, ParameterNodeExt p, boolean allowsFieldFlow, Configuration config ) { flowIntoCallNodeCand1(call, arg, p, config) and @@ -944,10 +943,10 @@ private module Stage2 { pragma[nomagic] private predicate fwdFlowIn( - DataFlowCall call, ParameterNode p, Cc outercc, Cc innercc, ApOption argAp, Ap ap, + DataFlowCall call, ParameterNodeExt p, Cc outercc, Cc innercc, ApOption argAp, Ap ap, Configuration config ) { - exists(ArgumentNode arg, boolean allowsFieldFlow | + exists(ArgumentNodeExt arg, boolean allowsFieldFlow | fwdFlow(arg, outercc, argAp, ap, config) and flowIntoCall(call, arg, p, allowsFieldFlow, config) and innercc = getCallContextCall(call, getNodeEnclosingCallable(p), outercc) @@ -992,7 +991,7 @@ private module Stage2 { private predicate fwdFlowIsEntered( DataFlowCall call, Cc cc, ApOption argAp, Ap ap, Configuration config ) { - exists(ParameterNode p | + exists(ParameterNodeExt p | fwdFlowIn(call, p, cc, _, argAp, ap, config) and PrevStage::parameterMayFlowThrough(p, _, getApprox(ap), config) ) @@ -1133,10 +1132,10 @@ private module Stage2 { pragma[nomagic] private predicate revFlowIn( - DataFlowCall call, ArgumentNode arg, boolean toReturn, ApOption returnAp, Ap ap, + DataFlowCall call, ArgumentNodeExt arg, boolean toReturn, ApOption returnAp, Ap ap, Configuration config ) { - exists(ParameterNode p, boolean allowsFieldFlow | + exists(ParameterNodeExt p, boolean allowsFieldFlow | revFlow(p, toReturn, returnAp, ap, config) and flowIntoCall(call, arg, p, allowsFieldFlow, config) | @@ -1146,7 +1145,7 @@ private module Stage2 { pragma[nomagic] private predicate revFlowInToReturn( - DataFlowCall call, ArgumentNode arg, Ap returnAp, Ap ap, Configuration config + DataFlowCall call, ArgumentNodeExt arg, Ap returnAp, Ap ap, Configuration config ) { revFlowIn(call, arg, true, apSome(returnAp), ap, config) } @@ -1199,13 +1198,15 @@ private module Stage2 { pragma[noinline] private predicate parameterFlow( - ParameterNode p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config + ParameterNodeExt p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config ) { revFlow(p, true, apSome(ap0), ap, config) and c = getNodeEnclosingCallable(p) } - predicate parameterMayFlowThrough(ParameterNode p, DataFlowCallable c, Ap ap, Configuration config) { + predicate parameterMayFlowThrough( + ParameterNodeExt p, DataFlowCallable c, Ap ap, Configuration config + ) { exists(ReturnNodeExt ret, Ap ap0, ReturnKindExt kind, int pos | parameterFlow(p, ap, ap0, c, config) and c = getNodeEnclosingCallable(ret) and @@ -1245,7 +1246,7 @@ private predicate flowOutOfCallNodeCand2( pragma[nomagic] private predicate flowIntoCallNodeCand2( - DataFlowCall call, ArgumentNode node1, ParameterNode node2, boolean allowsFieldFlow, + DataFlowCall call, ArgumentNodeExt node1, ParameterNodeExt node2, boolean allowsFieldFlow, Configuration config ) { flowIntoCallNodeCand1(call, node1, node2, allowsFieldFlow, config) and @@ -1260,8 +1261,8 @@ private module LocalFlowBigStep { */ private class FlowCheckNode extends Node { FlowCheckNode() { - this instanceof CastNode or - clearsContent(this, _) + castNode(this) or + clearsContentCached(this, _) } } @@ -1275,7 +1276,7 @@ private module LocalFlowBigStep { config.isSource(node) or jumpStep(_, node, config) or additionalJumpStep(_, node, config) or - node instanceof ParameterNode or + node instanceof ParameterNodeExt or node instanceof OutNodeExt or store(_, _, node, _) or read(_, _, node) or @@ -1321,21 +1322,21 @@ private module LocalFlowBigStep { Node node1, Node node2, boolean preservesValue, DataFlowType t, Configuration config, LocalCallContext cc ) { - not isUnreachableInCall(node2, cc.(LocalCallContextSpecificCall).getCall()) and + not isUnreachableInCallCached(node2, cc.(LocalCallContextSpecificCall).getCall()) and ( localFlowEntry(node1, pragma[only_bind_into](config)) and ( localFlowStepNodeCand1(node1, node2, config) and preservesValue = true and - t = getNodeType(node1) + t = getNodeDataFlowType(node1) or additionalLocalFlowStepNodeCand2(node1, node2, config) and preservesValue = false and - t = getNodeType(node2) + t = getNodeDataFlowType(node2) ) and node1 != node2 and cc.relevantFor(getNodeEnclosingCallable(node1)) and - not isUnreachableInCall(node1, cc.(LocalCallContextSpecificCall).getCall()) and + not isUnreachableInCallCached(node1, cc.(LocalCallContextSpecificCall).getCall()) and Stage2::revFlow(node2, pragma[only_bind_into](config)) or exists(Node mid | @@ -1350,7 +1351,7 @@ private module LocalFlowBigStep { additionalLocalFlowStepNodeCand2(mid, node2, config) and not mid instanceof FlowCheckNode and preservesValue = false and - t = getNodeType(node2) and + t = getNodeDataFlowType(node2) and Stage2::revFlow(node2, pragma[only_bind_into](config)) ) ) @@ -1384,7 +1385,7 @@ private module Stage3 { private ApApprox getApprox(Ap ap) { result = ap.toBoolNonEmpty() } private ApNil getApNil(Node node) { - PrevStage::revFlow(node, _) and result = TFrontNil(getNodeType(node)) + PrevStage::revFlow(node, _) and result = TFrontNil(getNodeDataFlowType(node)) } bindingset[tc, tail] @@ -1443,7 +1444,9 @@ private module Stage3 { bindingset[node, ap] private predicate filter(Node node, Ap ap) { not ap.isClearedAt(node) and - if node instanceof CastingNode then compatibleTypes(getNodeType(node), ap.getType()) else any() + if node instanceof CastingNode + then compatibleTypes(getNodeDataFlowType(node), ap.getType()) + else any() } bindingset[ap, contentType] @@ -1583,10 +1586,10 @@ private module Stage3 { pragma[nomagic] private predicate fwdFlowIn( - DataFlowCall call, ParameterNode p, Cc outercc, Cc innercc, ApOption argAp, Ap ap, + DataFlowCall call, ParameterNodeExt p, Cc outercc, Cc innercc, ApOption argAp, Ap ap, Configuration config ) { - exists(ArgumentNode arg, boolean allowsFieldFlow | + exists(ArgumentNodeExt arg, boolean allowsFieldFlow | fwdFlow(arg, outercc, argAp, ap, config) and flowIntoCall(call, arg, p, allowsFieldFlow, config) and innercc = getCallContextCall(call, getNodeEnclosingCallable(p), outercc) @@ -1631,7 +1634,7 @@ private module Stage3 { private predicate fwdFlowIsEntered( DataFlowCall call, Cc cc, ApOption argAp, Ap ap, Configuration config ) { - exists(ParameterNode p | + exists(ParameterNodeExt p | fwdFlowIn(call, p, cc, _, argAp, ap, config) and PrevStage::parameterMayFlowThrough(p, _, unbindApa(getApprox(ap)), config) ) @@ -1772,10 +1775,10 @@ private module Stage3 { pragma[nomagic] private predicate revFlowIn( - DataFlowCall call, ArgumentNode arg, boolean toReturn, ApOption returnAp, Ap ap, + DataFlowCall call, ArgumentNodeExt arg, boolean toReturn, ApOption returnAp, Ap ap, Configuration config ) { - exists(ParameterNode p, boolean allowsFieldFlow | + exists(ParameterNodeExt p, boolean allowsFieldFlow | revFlow(p, toReturn, returnAp, ap, config) and flowIntoCall(call, arg, p, allowsFieldFlow, config) | @@ -1785,7 +1788,7 @@ private module Stage3 { pragma[nomagic] private predicate revFlowInToReturn( - DataFlowCall call, ArgumentNode arg, Ap returnAp, Ap ap, Configuration config + DataFlowCall call, ArgumentNodeExt arg, Ap returnAp, Ap ap, Configuration config ) { revFlowIn(call, arg, true, apSome(returnAp), ap, config) } @@ -1838,13 +1841,15 @@ private module Stage3 { pragma[noinline] private predicate parameterFlow( - ParameterNode p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config + ParameterNodeExt p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config ) { revFlow(p, true, apSome(ap0), ap, config) and c = getNodeEnclosingCallable(p) } - predicate parameterMayFlowThrough(ParameterNode p, DataFlowCallable c, Ap ap, Configuration config) { + predicate parameterMayFlowThrough( + ParameterNodeExt p, DataFlowCallable c, Ap ap, Configuration config + ) { exists(ReturnNodeExt ret, Ap ap0, ReturnKindExt kind, int pos | parameterFlow(p, ap, ap0, c, config) and c = getNodeEnclosingCallable(ret) and @@ -2088,7 +2093,7 @@ private module Stage4 { private ApApprox getApprox(Ap ap) { result = ap.getFront() } private ApNil getApNil(Node node) { - PrevStage::revFlow(node, _) and result = TNil(getNodeType(node)) + PrevStage::revFlow(node, _) and result = TNil(getNodeDataFlowType(node)) } bindingset[tc, tail] @@ -2155,7 +2160,7 @@ private module Stage4 { pragma[nomagic] private predicate flowIntoCall( - DataFlowCall call, ArgumentNode node1, ParameterNode node2, boolean allowsFieldFlow, + DataFlowCall call, ArgumentNodeExt node1, ParameterNodeExt node2, boolean allowsFieldFlow, Configuration config ) { flowIntoCallNodeCand2(call, node1, node2, allowsFieldFlow, config) and @@ -2300,10 +2305,10 @@ private module Stage4 { pragma[nomagic] private predicate fwdFlowIn( - DataFlowCall call, ParameterNode p, Cc outercc, Cc innercc, ApOption argAp, Ap ap, + DataFlowCall call, ParameterNodeExt p, Cc outercc, Cc innercc, ApOption argAp, Ap ap, Configuration config ) { - exists(ArgumentNode arg, boolean allowsFieldFlow | + exists(ArgumentNodeExt arg, boolean allowsFieldFlow | fwdFlow(arg, outercc, argAp, ap, config) and flowIntoCall(call, arg, p, allowsFieldFlow, config) and innercc = getCallContextCall(call, getNodeEnclosingCallable(p), outercc) @@ -2348,7 +2353,7 @@ private module Stage4 { private predicate fwdFlowIsEntered( DataFlowCall call, Cc cc, ApOption argAp, Ap ap, Configuration config ) { - exists(ParameterNode p | + exists(ParameterNodeExt p | fwdFlowIn(call, p, cc, _, argAp, ap, config) and PrevStage::parameterMayFlowThrough(p, _, unbindApa(getApprox(ap)), config) ) @@ -2489,10 +2494,10 @@ private module Stage4 { pragma[nomagic] private predicate revFlowIn( - DataFlowCall call, ArgumentNode arg, boolean toReturn, ApOption returnAp, Ap ap, + DataFlowCall call, ArgumentNodeExt arg, boolean toReturn, ApOption returnAp, Ap ap, Configuration config ) { - exists(ParameterNode p, boolean allowsFieldFlow | + exists(ParameterNodeExt p, boolean allowsFieldFlow | revFlow(p, toReturn, returnAp, ap, config) and flowIntoCall(call, arg, p, allowsFieldFlow, config) | @@ -2502,7 +2507,7 @@ private module Stage4 { pragma[nomagic] private predicate revFlowInToReturn( - DataFlowCall call, ArgumentNode arg, Ap returnAp, Ap ap, Configuration config + DataFlowCall call, ArgumentNodeExt arg, Ap returnAp, Ap ap, Configuration config ) { revFlowIn(call, arg, true, apSome(returnAp), ap, config) } @@ -2555,13 +2560,15 @@ private module Stage4 { pragma[noinline] private predicate parameterFlow( - ParameterNode p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config + ParameterNodeExt p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config ) { revFlow(p, true, apSome(ap0), ap, config) and c = getNodeEnclosingCallable(p) } - predicate parameterMayFlowThrough(ParameterNode p, DataFlowCallable c, Ap ap, Configuration config) { + predicate parameterMayFlowThrough( + ParameterNodeExt p, DataFlowCallable c, Ap ap, Configuration config + ) { exists(ReturnNodeExt ret, Ap ap0, ReturnKindExt kind, int pos | parameterFlow(p, ap, ap0, c, config) and c = getNodeEnclosingCallable(ret) and @@ -2606,7 +2613,7 @@ private predicate nodeMayUseSummary(Node n, AccessPathApprox apa, Configuration private newtype TSummaryCtx = TSummaryCtxNone() or - TSummaryCtxSome(ParameterNode p, AccessPath ap) { + TSummaryCtxSome(ParameterNodeExt p, AccessPath ap) { Stage4::parameterMayFlowThrough(p, _, ap.getApprox(), _) } @@ -2627,7 +2634,7 @@ private class SummaryCtxNone extends SummaryCtx, TSummaryCtxNone { /** A summary context from which a flow summary can be generated. */ private class SummaryCtxSome extends SummaryCtx, TSummaryCtxSome { - private ParameterNode p; + private ParameterNodeExt p; private AccessPath ap; SummaryCtxSome() { this = TSummaryCtxSome(p, ap) } @@ -2758,7 +2765,7 @@ private newtype TPathNode = config.isSource(node) and cc instanceof CallContextAny and sc instanceof SummaryCtxNone and - ap = TAccessPathNil(getNodeType(node)) + ap = TAccessPathNil(getNodeDataFlowType(node)) or // ... or a step from an existing PathNode to another node. exists(PathNodeMid mid | @@ -2979,7 +2986,7 @@ class PathNode extends TPathNode { Configuration getConfiguration() { none() } private predicate isHidden() { - nodeIsHidden(this.getNode()) and + hiddenNode(this.getNode()) and not this.isSource() and not this instanceof PathNodeSink } @@ -3148,7 +3155,7 @@ private predicate pathStep(PathNodeMid mid, Node node, CallContext cc, SummaryCt cc instanceof CallContextAny and sc instanceof SummaryCtxNone and mid.getAp() instanceof AccessPathNil and - ap = TAccessPathNil(getNodeType(node)) + ap = TAccessPathNil(getNodeDataFlowType(node)) or exists(TypedContent tc | pathStoreStep(mid, node, ap.pop(tc), tc, cc)) and sc = mid.getSummaryCtx() @@ -3235,7 +3242,7 @@ pragma[noinline] private predicate pathIntoArg( PathNodeMid mid, int i, CallContext cc, DataFlowCall call, AccessPath ap, AccessPathApprox apa ) { - exists(ArgumentNode arg | + exists(ArgumentNodeExt arg | arg = mid.getNode() and cc = mid.getCallContext() and arg.argumentOf(call, i) and @@ -3248,7 +3255,7 @@ pragma[noinline] private predicate parameterCand( DataFlowCallable callable, int i, AccessPathApprox apa, Configuration config ) { - exists(ParameterNode p | + exists(ParameterNodeExt p | Stage4::revFlow(p, _, _, apa, config) and p.isParameterOf(callable, i) ) @@ -3272,7 +3279,7 @@ private predicate pathIntoCallable0( * respectively. */ private predicate pathIntoCallable( - PathNodeMid mid, ParameterNode p, CallContext outercc, CallContextCall innercc, SummaryCtx sc, + PathNodeMid mid, ParameterNodeExt p, CallContext outercc, CallContextCall innercc, SummaryCtx sc, DataFlowCall call ) { exists(int i, DataFlowCallable callable, AccessPath ap | @@ -3568,7 +3575,7 @@ private module FlowExploration { private newtype TSummaryCtx1 = TSummaryCtx1None() or - TSummaryCtx1Param(ParameterNode p) + TSummaryCtx1Param(ParameterNodeExt p) private newtype TSummaryCtx2 = TSummaryCtx2None() or @@ -3591,7 +3598,7 @@ private module FlowExploration { cc instanceof CallContextAny and sc1 = TSummaryCtx1None() and sc2 = TSummaryCtx2None() and - ap = TPartialNil(getNodeType(node)) and + ap = TPartialNil(getNodeDataFlowType(node)) and not fullBarrier(node, config) and exists(config.explorationLimit()) or @@ -3611,7 +3618,7 @@ private module FlowExploration { or exists(PartialPathNodeRev mid | revPartialPathStep(mid, node, sc1, sc2, ap, config) and - not clearsContent(node, ap.getHead()) and + not clearsContentCached(node, ap.getHead()) and not fullBarrier(node, config) and distSink(getNodeEnclosingCallable(node), config) <= config.explorationLimit() ) @@ -3625,9 +3632,9 @@ private module FlowExploration { exists(PartialPathNodeFwd mid | partialPathStep(mid, node, cc, sc1, sc2, ap, config) and not fullBarrier(node, config) and - not clearsContent(node, ap.getHead().getContent()) and + not clearsContentCached(node, ap.getHead().getContent()) and if node instanceof CastingNode - then compatibleTypes(getNodeType(node), ap.getType()) + then compatibleTypes(getNodeDataFlowType(node), ap.getType()) else any() ) } @@ -3783,7 +3790,7 @@ private module FlowExploration { PartialPathNodeFwd mid, Node node, CallContext cc, TSummaryCtx1 sc1, TSummaryCtx2 sc2, PartialAccessPath ap, Configuration config ) { - not isUnreachableInCall(node, cc.(CallContextSpecificCall).getCall()) and + not isUnreachableInCallCached(node, cc.(CallContextSpecificCall).getCall()) and ( localFlowStep(mid.getNode(), node, config) and cc = mid.getCallContext() and @@ -3797,7 +3804,7 @@ private module FlowExploration { sc1 = mid.getSummaryCtx1() and sc2 = mid.getSummaryCtx2() and mid.getAp() instanceof PartialAccessPathNil and - ap = TPartialNil(getNodeType(node)) and + ap = TPartialNil(getNodeDataFlowType(node)) and config = mid.getConfiguration() ) or @@ -3813,7 +3820,7 @@ private module FlowExploration { sc1 = TSummaryCtx1None() and sc2 = TSummaryCtx2None() and mid.getAp() instanceof PartialAccessPathNil and - ap = TPartialNil(getNodeType(node)) and + ap = TPartialNil(getNodeDataFlowType(node)) and config = mid.getConfiguration() or partialPathStoreStep(mid, _, _, node, ap) and @@ -3827,7 +3834,7 @@ private module FlowExploration { sc1 = mid.getSummaryCtx1() and sc2 = mid.getSummaryCtx2() and apConsFwd(ap, tc, ap0, config) and - compatibleTypes(ap.getType(), getNodeType(node)) + compatibleTypes(ap.getType(), getNodeDataFlowType(node)) ) or partialPathIntoCallable(mid, node, _, cc, sc1, sc2, _, ap, config) @@ -3924,7 +3931,7 @@ private module FlowExploration { PartialPathNodeFwd mid, int i, CallContext cc, DataFlowCall call, PartialAccessPath ap, Configuration config ) { - exists(ArgumentNode arg | + exists(ArgumentNodeExt arg | arg = mid.getNode() and cc = mid.getCallContext() and arg.argumentOf(call, i) and @@ -3943,7 +3950,7 @@ private module FlowExploration { } private predicate partialPathIntoCallable( - PartialPathNodeFwd mid, ParameterNode p, CallContext outercc, CallContextCall innercc, + PartialPathNodeFwd mid, ParameterNodeExt p, CallContext outercc, CallContextCall innercc, TSummaryCtx1 sc1, TSummaryCtx2 sc2, DataFlowCall call, PartialAccessPath ap, Configuration config ) { @@ -3980,7 +3987,7 @@ private module FlowExploration { DataFlowCall call, PartialPathNodeFwd mid, ReturnKindExt kind, CallContext cc, PartialAccessPath ap, Configuration config ) { - exists(ParameterNode p, CallContext innercc, TSummaryCtx1 sc1, TSummaryCtx2 sc2 | + exists(ParameterNodeExt p, CallContext innercc, TSummaryCtx1 sc1, TSummaryCtx2 sc2 | partialPathIntoCallable(mid, p, cc, innercc, sc1, sc2, call, _, config) and paramFlowsThroughInPartialPath(kind, innercc, sc1, sc2, ap, config) ) @@ -4037,7 +4044,7 @@ private module FlowExploration { apConsRev(ap, c, ap0, config) ) or - exists(ParameterNode p | + exists(ParameterNodeExt p | mid.getNode() = p and viableParamArg(_, p, node) and sc1 = mid.getSummaryCtx1() and @@ -4115,7 +4122,7 @@ private module FlowExploration { int pos, TRevSummaryCtx1Some sc1, TRevSummaryCtx2Some sc2, RevPartialAccessPath ap, Configuration config ) { - exists(PartialPathNodeRev mid, ParameterNode p | + exists(PartialPathNodeRev mid, ParameterNodeExt p | mid.getNode() = p and p.isParameterOf(_, pos) and sc1 = mid.getSummaryCtx1() and @@ -4138,7 +4145,7 @@ private module FlowExploration { pragma[nomagic] private predicate revPartialPathThroughCallable( - PartialPathNodeRev mid, ArgumentNode node, RevPartialAccessPath ap, Configuration config + PartialPathNodeRev mid, ArgumentNodeExt node, RevPartialAccessPath ap, Configuration config ) { exists(DataFlowCall call, int pos | revPartialPathThroughCallable0(call, mid, pos, ap, config) and 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 a51c20c2288..b5ceabc605d 100644 --- a/cpp/ql/src/semmle/code/cpp/dataflow/internal/DataFlowImplCommon.qll +++ b/cpp/ql/src/semmle/code/cpp/dataflow/internal/DataFlowImplCommon.qll @@ -35,22 +35,24 @@ predicate accessPathCostLimits(int apLimit, int tupleLimit) { * calls. For this reason, we cannot reuse the code from `DataFlowImpl.qll` directly. */ private module LambdaFlow { - private predicate viableParamNonLambda(DataFlowCall call, int i, ParameterNode p) { + private predicate viableParamNonLambda(DataFlowCall call, int i, ParameterNodeExt p) { p.isParameterOf(viableCallable(call), i) } - private predicate viableParamLambda(DataFlowCall call, int i, ParameterNode p) { + private predicate viableParamLambda(DataFlowCall call, int i, ParameterNodeExt p) { p.isParameterOf(viableCallableLambda(call, _), i) } - private predicate viableParamArgNonLambda(DataFlowCall call, ParameterNode p, ArgumentNode arg) { + private predicate viableParamArgNonLambda( + DataFlowCall call, ParameterNodeExt p, ArgumentNodeExt arg + ) { exists(int i | viableParamNonLambda(call, i, p) and arg.argumentOf(call, i) ) } - private predicate viableParamArgLambda(DataFlowCall call, ParameterNode p, ArgumentNode arg) { + private predicate viableParamArgLambda(DataFlowCall call, ParameterNodeExt p, ArgumentNodeExt arg) { exists(int i | viableParamLambda(call, i, p) and arg.argumentOf(call, i) @@ -118,8 +120,8 @@ private module LambdaFlow { boolean toJump, DataFlowCallOption lastCall ) { revLambdaFlow0(lambdaCall, kind, node, t, toReturn, toJump, lastCall) and - if node instanceof CastNode or node instanceof ArgumentNode or node instanceof ReturnNode - then compatibleTypes(t, getNodeType(node)) + if castNode(node) or node instanceof ArgumentNodeExt or node instanceof ReturnNode + then compatibleTypes(t, getNodeDataFlowType(node)) else any() } @@ -129,7 +131,7 @@ private module LambdaFlow { boolean toJump, DataFlowCallOption lastCall ) { lambdaCall(lambdaCall, kind, node) and - t = getNodeType(node) and + t = getNodeDataFlowType(node) and toReturn = false and toJump = false and lastCall = TDataFlowCallNone() @@ -146,7 +148,7 @@ private module LambdaFlow { getNodeEnclosingCallable(node) = getNodeEnclosingCallable(mid) | preservesValue = false and - t = getNodeType(node) + t = getNodeDataFlowType(node) or preservesValue = true and t = t0 @@ -160,7 +162,7 @@ private module LambdaFlow { toJump = true and lastCall = TDataFlowCallNone() | - jumpStep(node, mid) and + jumpStepCached(node, mid) and t = t0 or exists(boolean preservesValue | @@ -168,7 +170,7 @@ private module LambdaFlow { getNodeEnclosingCallable(node) != getNodeEnclosingCallable(mid) | preservesValue = false and - t = getNodeType(node) + t = getNodeDataFlowType(node) or preservesValue = true and t = t0 @@ -176,7 +178,7 @@ private module LambdaFlow { ) or // flow into a callable - exists(ParameterNode p, DataFlowCallOption lastCall0, DataFlowCall call | + exists(ParameterNodeExt p, DataFlowCallOption lastCall0, DataFlowCall call | revLambdaFlowIn(lambdaCall, kind, p, t, toJump, lastCall0) and ( if lastCall0 = TDataFlowCallNone() and toJump = false @@ -227,8 +229,8 @@ private module LambdaFlow { pragma[nomagic] predicate revLambdaFlowIn( - DataFlowCall lambdaCall, LambdaCallKind kind, ParameterNode p, DataFlowType t, boolean toJump, - DataFlowCallOption lastCall + DataFlowCall lambdaCall, LambdaCallKind kind, ParameterNodeExt p, DataFlowType t, + boolean toJump, DataFlowCallOption lastCall ) { revLambdaFlow(lambdaCall, kind, p, t, false, toJump, lastCall) } @@ -242,6 +244,89 @@ private DataFlowCallable viableCallableExt(DataFlowCall call) { cached private module Cached { + /** + * If needed, call this predicate from `DataFlowImplSpecific.qll` in order to + * force a stage-dependency on the `DataFlowImplCommon.qll` stage and therby + * collapsing the two stages. + */ + cached + predicate forceCachingInSameStage() { any() } + + cached + predicate nodeEnclosingCallable(Node n, DataFlowCallable c) { c = n.getEnclosingCallable() } + + cached + predicate callEnclosingCallable(DataFlowCall call, DataFlowCallable c) { + c = call.getEnclosingCallable() + } + + cached + predicate nodeDataFlowType(Node n, DataFlowType t) { t = getNodeType(n) } + + cached + predicate jumpStepCached(Node node1, Node node2) { jumpStep(node1, node2) } + + cached + predicate clearsContentCached(Node n, Content c) { clearsContent(n, c) } + + cached + predicate isUnreachableInCallCached(Node n, DataFlowCall call) { isUnreachableInCall(n, call) } + + cached + predicate outNodeExt(Node n) { + n instanceof OutNode + or + n.(PostUpdateNode).getPreUpdateNode() instanceof ArgumentNodeExt + } + + cached + predicate hiddenNode(Node n) { nodeIsHidden(n) } + + cached + OutNodeExt getAnOutNodeExt(DataFlowCall call, ReturnKindExt k) { + result = getAnOutNode(call, k.(ValueReturnKind).getKind()) + or + exists(ArgumentNodeExt arg | + result.(PostUpdateNode).getPreUpdateNode() = arg and + arg.argumentOf(call, k.(ParamUpdateReturnKind).getPosition()) + ) + } + + cached + predicate returnNodeExt(Node n, ReturnKindExt k) { + k = TValueReturn(n.(ReturnNode).getKind()) + or + exists(ParameterNodeExt p, int pos | + parameterValueFlowsToPreUpdate(p, n) and + p.isParameterOf(_, pos) and + k = TParamUpdate(pos) + ) + } + + cached + predicate castNode(Node n) { n instanceof CastNode } + + cached + predicate castingNode(Node n) { + castNode(n) or + n instanceof ParameterNodeExt or + n instanceof OutNodeExt or + // For reads, `x.f`, we want to check that the tracked type after the read (which + // is obtained by popping the head of the access path stack) is compatible with + // the type of `x.f`. + read(_, _, n) + } + + cached + predicate parameterNode(Node n, DataFlowCallable c, int i) { + n.(ParameterNode).isParameterOf(c, i) + } + + cached + predicate argumentNode(Node n, DataFlowCall call, int pos) { + n.(ArgumentNode).argumentOf(call, pos) + } + /** * Gets a viable target for the lambda call `call`. * @@ -261,7 +346,7 @@ private module Cached { * The instance parameter is considered to have index `-1`. */ pragma[nomagic] - private predicate viableParam(DataFlowCall call, int i, ParameterNode p) { + private predicate viableParam(DataFlowCall call, int i, ParameterNodeExt p) { p.isParameterOf(viableCallableExt(call), i) } @@ -270,11 +355,11 @@ private module Cached { * dispatch into account. */ cached - predicate viableParamArg(DataFlowCall call, ParameterNode p, ArgumentNode arg) { + predicate viableParamArg(DataFlowCall call, ParameterNodeExt p, ArgumentNodeExt arg) { exists(int i | viableParam(call, i, p) and arg.argumentOf(call, i) and - compatibleTypes(getNodeType(arg), getNodeType(p)) + compatibleTypes(getNodeDataFlowType(arg), getNodeDataFlowType(p)) ) } @@ -312,7 +397,7 @@ private module Cached { * `read` indicates whether it is contents of `p` that can flow to `node`. */ pragma[nomagic] - private predicate parameterValueFlowCand(ParameterNode p, Node node, boolean read) { + private predicate parameterValueFlowCand(ParameterNodeExt p, Node node, boolean read) { p = node and read = false or @@ -325,30 +410,32 @@ private module Cached { // read exists(Node mid | parameterValueFlowCand(p, mid, false) and - readStep(mid, _, node) and + read(mid, _, node) and read = true ) or // flow through: no prior read - exists(ArgumentNode arg | + exists(ArgumentNodeExt arg | parameterValueFlowArgCand(p, arg, false) and argumentValueFlowsThroughCand(arg, node, read) ) or // flow through: no read inside method - exists(ArgumentNode arg | + exists(ArgumentNodeExt arg | parameterValueFlowArgCand(p, arg, read) and argumentValueFlowsThroughCand(arg, node, false) ) } pragma[nomagic] - private predicate parameterValueFlowArgCand(ParameterNode p, ArgumentNode arg, boolean read) { + private predicate parameterValueFlowArgCand( + ParameterNodeExt p, ArgumentNodeExt arg, boolean read + ) { parameterValueFlowCand(p, arg, read) } pragma[nomagic] - predicate parameterValueFlowsToPreUpdateCand(ParameterNode p, PostUpdateNode n) { + predicate parameterValueFlowsToPreUpdateCand(ParameterNodeExt p, PostUpdateNode n) { parameterValueFlowCand(p, n.getPreUpdateNode(), false) } @@ -360,7 +447,7 @@ private module Cached { * `read` indicates whether it is contents of `p` that can flow to the return * node. */ - predicate parameterValueFlowReturnCand(ParameterNode p, ReturnKind kind, boolean read) { + predicate parameterValueFlowReturnCand(ParameterNodeExt p, ReturnKind kind, boolean read) { exists(ReturnNode ret | parameterValueFlowCand(p, ret, read) and kind = ret.getKind() @@ -369,9 +456,9 @@ private module Cached { pragma[nomagic] private predicate argumentValueFlowsThroughCand0( - DataFlowCall call, ArgumentNode arg, ReturnKind kind, boolean read + DataFlowCall call, ArgumentNodeExt arg, ReturnKind kind, boolean read ) { - exists(ParameterNode param | viableParamArg(call, param, arg) | + exists(ParameterNodeExt param | viableParamArg(call, param, arg) | parameterValueFlowReturnCand(param, kind, read) ) } @@ -382,14 +469,14 @@ private module Cached { * * `read` indicates whether it is contents of `arg` that can flow to `out`. */ - predicate argumentValueFlowsThroughCand(ArgumentNode arg, Node out, boolean read) { + predicate argumentValueFlowsThroughCand(ArgumentNodeExt arg, Node out, boolean read) { exists(DataFlowCall call, ReturnKind kind | argumentValueFlowsThroughCand0(call, arg, kind, read) and out = getAnOutNode(call, kind) ) } - predicate cand(ParameterNode p, Node n) { + predicate cand(ParameterNodeExt p, Node n) { parameterValueFlowCand(p, n, _) and ( parameterValueFlowReturnCand(p, _, _) @@ -416,21 +503,21 @@ private module Cached { * If a read step was taken, then `read` captures the `Content`, the * container type, and the content type. */ - predicate parameterValueFlow(ParameterNode p, Node node, ReadStepTypesOption read) { + predicate parameterValueFlow(ParameterNodeExt p, Node node, ReadStepTypesOption read) { parameterValueFlow0(p, node, read) and if node instanceof CastingNode then // normal flow through read = TReadStepTypesNone() and - compatibleTypes(getNodeType(p), getNodeType(node)) + compatibleTypes(getNodeDataFlowType(p), getNodeDataFlowType(node)) or // getter - compatibleTypes(read.getContentType(), getNodeType(node)) + compatibleTypes(read.getContentType(), getNodeDataFlowType(node)) else any() } pragma[nomagic] - private predicate parameterValueFlow0(ParameterNode p, Node node, ReadStepTypesOption read) { + private predicate parameterValueFlow0(ParameterNodeExt p, Node node, ReadStepTypesOption read) { p = node and Cand::cand(p, _) and read = TReadStepTypesNone() @@ -447,7 +534,7 @@ private module Cached { readStepWithTypes(mid, read.getContainerType(), read.getContent(), node, read.getContentType()) and Cand::parameterValueFlowReturnCand(p, _, true) and - compatibleTypes(getNodeType(p), read.getContainerType()) + compatibleTypes(getNodeDataFlowType(p), read.getContainerType()) ) or parameterValueFlow0_0(TReadStepTypesNone(), p, node, read) @@ -455,16 +542,16 @@ private module Cached { pragma[nomagic] private predicate parameterValueFlow0_0( - ReadStepTypesOption mustBeNone, ParameterNode p, Node node, ReadStepTypesOption read + ReadStepTypesOption mustBeNone, ParameterNodeExt p, Node node, ReadStepTypesOption read ) { // flow through: no prior read - exists(ArgumentNode arg | + exists(ArgumentNodeExt arg | parameterValueFlowArg(p, arg, mustBeNone) and argumentValueFlowsThrough(arg, read, node) ) or // flow through: no read inside method - exists(ArgumentNode arg | + exists(ArgumentNodeExt arg | parameterValueFlowArg(p, arg, read) and argumentValueFlowsThrough(arg, mustBeNone, node) ) @@ -472,7 +559,7 @@ private module Cached { pragma[nomagic] private predicate parameterValueFlowArg( - ParameterNode p, ArgumentNode arg, ReadStepTypesOption read + ParameterNodeExt p, ArgumentNodeExt arg, ReadStepTypesOption read ) { parameterValueFlow(p, arg, read) and Cand::argumentValueFlowsThroughCand(arg, _, _) @@ -480,9 +567,9 @@ private module Cached { pragma[nomagic] private predicate argumentValueFlowsThrough0( - DataFlowCall call, ArgumentNode arg, ReturnKind kind, ReadStepTypesOption read + DataFlowCall call, ArgumentNodeExt arg, ReturnKind kind, ReadStepTypesOption read ) { - exists(ParameterNode param | viableParamArg(call, param, arg) | + exists(ParameterNodeExt param | viableParamArg(call, param, arg) | parameterValueFlowReturn(param, kind, read) ) } @@ -496,18 +583,18 @@ private module Cached { * container type, and the content type. */ pragma[nomagic] - predicate argumentValueFlowsThrough(ArgumentNode arg, ReadStepTypesOption read, Node out) { + predicate argumentValueFlowsThrough(ArgumentNodeExt arg, ReadStepTypesOption read, Node out) { exists(DataFlowCall call, ReturnKind kind | argumentValueFlowsThrough0(call, arg, kind, read) and out = getAnOutNode(call, kind) | // normal flow through read = TReadStepTypesNone() and - compatibleTypes(getNodeType(arg), getNodeType(out)) + compatibleTypes(getNodeDataFlowType(arg), getNodeDataFlowType(out)) or // getter - compatibleTypes(getNodeType(arg), read.getContainerType()) and - compatibleTypes(read.getContentType(), getNodeType(out)) + compatibleTypes(getNodeDataFlowType(arg), read.getContainerType()) and + compatibleTypes(read.getContentType(), getNodeDataFlowType(out)) ) } @@ -516,7 +603,7 @@ private module Cached { * value-preserving steps and a single read step, not taking call * contexts into account, thus representing a getter-step. */ - predicate getterStep(ArgumentNode arg, Content c, Node out) { + predicate getterStep(ArgumentNodeExt arg, Content c, Node out) { argumentValueFlowsThrough(arg, TReadStepTypesSome(_, c, _), out) } @@ -529,7 +616,7 @@ private module Cached { * container type, and the content type. */ private predicate parameterValueFlowReturn( - ParameterNode p, ReturnKind kind, ReadStepTypesOption read + ParameterNodeExt p, ReturnKind kind, ReadStepTypesOption read ) { exists(ReturnNode ret | parameterValueFlow(p, ret, read) and @@ -553,7 +640,7 @@ private module Cached { private predicate mayBenefitFromCallContextExt(DataFlowCall call, DataFlowCallable callable) { mayBenefitFromCallContext(call, callable) or - callable = call.getEnclosingCallable() and + callEnclosingCallable(call, callable) and exists(viableCallableLambda(call, TDataFlowCallSome(_))) } @@ -611,7 +698,7 @@ private module Cached { mayBenefitFromCallContextExt(call, _) and c = viableCallableExt(call) and ctxtgts = count(DataFlowCall ctx | c = viableImplInCallContextExt(call, ctx)) and - tgts = strictcount(DataFlowCall ctx | viableCallableExt(ctx) = call.getEnclosingCallable()) and + tgts = strictcount(DataFlowCall ctx | callEnclosingCallable(call, viableCallableExt(ctx))) and ctxtgts < tgts ) } @@ -635,8 +722,7 @@ private module Cached { * Holds if `p` can flow to the pre-update node associated with post-update * node `n`, in the same callable, using only value-preserving steps. */ - cached - predicate parameterValueFlowsToPreUpdate(ParameterNode p, PostUpdateNode n) { + private predicate parameterValueFlowsToPreUpdate(ParameterNodeExt p, PostUpdateNode n) { parameterValueFlow(p, n.getPreUpdateNode(), TReadStepTypesNone()) } @@ -644,9 +730,9 @@ private module Cached { Node node1, Content c, Node node2, DataFlowType contentType, DataFlowType containerType ) { storeStep(node1, c, node2) and - readStep(_, c, _) and - contentType = getNodeType(node1) and - containerType = getNodeType(node2) + read(_, c, _) and + contentType = getNodeDataFlowType(node1) and + containerType = getNodeDataFlowType(node2) or exists(Node n1, Node n2 | n1 = node1.(PostUpdateNode).getPreUpdateNode() and @@ -654,12 +740,15 @@ private module Cached { | argumentValueFlowsThrough(n2, TReadStepTypesSome(containerType, c, contentType), n1) or - readStep(n2, c, n1) and - contentType = getNodeType(n1) and - containerType = getNodeType(n2) + read(n2, c, n1) and + contentType = getNodeDataFlowType(n1) and + containerType = getNodeDataFlowType(n2) ) } + cached + predicate read(Node node1, Content c, Node node2) { readStep(node1, c, node2) } + /** * Holds if data can flow from `node1` to `node2` via a direct assignment to * `f`. @@ -678,8 +767,9 @@ private module Cached { * are aliases. A typical example is a function returning `this`, implementing a fluent * interface. */ - cached - predicate reverseStepThroughInputOutputAlias(PostUpdateNode fromNode, PostUpdateNode toNode) { + private predicate reverseStepThroughInputOutputAlias( + PostUpdateNode fromNode, PostUpdateNode toNode + ) { exists(Node fromPre, Node toPre | fromPre = fromNode.getPreUpdateNode() and toPre = toNode.getPreUpdateNode() @@ -688,14 +778,20 @@ private module Cached { // Does the language-specific simpleLocalFlowStep already model flow // from function input to output? fromPre = getAnOutNode(c, _) and - toPre.(ArgumentNode).argumentOf(c, _) and - simpleLocalFlowStep(toPre.(ArgumentNode), fromPre) + toPre.(ArgumentNodeExt).argumentOf(c, _) and + simpleLocalFlowStep(toPre.(ArgumentNodeExt), fromPre) ) or argumentValueFlowsThrough(toPre, TReadStepTypesNone(), fromPre) ) } + cached + predicate simpleLocalFlowStepExt(Node node1, Node node2) { + simpleLocalFlowStep(node1, node2) or + reverseStepThroughInputOutputAlias(node1, node2) + } + /** * Holds if the call context `call` either improves virtual dispatch in * `callable` or if it allows us to prune unreachable nodes in `callable`. @@ -704,7 +800,7 @@ private module Cached { predicate recordDataFlowCallSite(DataFlowCall call, DataFlowCallable callable) { reducedViableImplInCallContext(_, callable, call) or - exists(Node n | getNodeEnclosingCallable(n) = callable | isUnreachableInCall(n, call)) + exists(Node n | getNodeEnclosingCallable(n) = callable | isUnreachableInCallCached(n, call)) } cached @@ -726,12 +822,12 @@ private module Cached { cached newtype TLocalFlowCallContext = TAnyLocalCall() or - TSpecificLocalCall(DataFlowCall call) { isUnreachableInCall(_, call) } + TSpecificLocalCall(DataFlowCall call) { isUnreachableInCallCached(_, call) } cached newtype TReturnKindExt = TValueReturn(ReturnKind kind) or - TParamUpdate(int pos) { exists(ParameterNode p | p.isParameterOf(_, pos)) } + TParamUpdate(int pos) { exists(ParameterNodeExt p | p.isParameterOf(_, pos)) } cached newtype TBooleanOption = @@ -761,23 +857,15 @@ private module Cached { * A `Node` at which a cast can occur such that the type should be checked. */ class CastingNode extends Node { - CastingNode() { - this instanceof ParameterNode or - this instanceof CastNode or - this instanceof OutNodeExt or - // For reads, `x.f`, we want to check that the tracked type after the read (which - // is obtained by popping the head of the access path stack) is compatible with - // the type of `x.f`. - readStep(_, _, this) - } + CastingNode() { castingNode(this) } } private predicate readStepWithTypes( Node n1, DataFlowType container, Content c, Node n2, DataFlowType content ) { - readStep(n1, c, n2) and - container = getNodeType(n1) and - content = getNodeType(n2) + read(n1, c, n2) and + container = getNodeDataFlowType(n1) and + content = getNodeDataFlowType(n2) } private newtype TReadStepTypesOption = @@ -854,7 +942,7 @@ class CallContextSomeCall extends CallContextCall, TSomeCall { override string toString() { result = "CcSomeCall" } override predicate relevantFor(DataFlowCallable callable) { - exists(ParameterNode p | getNodeEnclosingCallable(p) = callable) + exists(ParameterNodeExt p | getNodeEnclosingCallable(p) = callable) } override predicate matchesCall(DataFlowCall call) { any() } @@ -866,7 +954,7 @@ class CallContextReturn extends CallContextNoCall, TReturn { } override predicate relevantFor(DataFlowCallable callable) { - exists(DataFlowCall call | this = TReturn(_, call) and call.getEnclosingCallable() = callable) + exists(DataFlowCall call | this = TReturn(_, call) and callEnclosingCallable(call, callable)) } } @@ -899,7 +987,7 @@ class LocalCallContextSpecificCall extends LocalCallContext, TSpecificLocalCall } private predicate relevantLocalCCtx(DataFlowCall call, DataFlowCallable callable) { - exists(Node n | getNodeEnclosingCallable(n) = callable and isUnreachableInCall(n, call)) + exists(Node n | getNodeEnclosingCallable(n) = callable and isUnreachableInCallCached(n, call)) } /** @@ -913,26 +1001,37 @@ LocalCallContext getLocalCallContext(CallContext ctx, DataFlowCallable callable) else result instanceof LocalCallContextAny } +/** + * The value of a parameter at function entry, viewed as a node in a data + * flow graph. + */ +class ParameterNodeExt extends Node { + ParameterNodeExt() { parameterNode(this, _, _) } + + /** + * Holds if this node is the parameter of callable `c` at the specified + * (zero-based) position. + */ + predicate isParameterOf(DataFlowCallable c, int i) { parameterNode(this, c, i) } +} + +/** A data-flow node that represents a call argument. */ +class ArgumentNodeExt extends Node { + ArgumentNodeExt() { argumentNode(this, _, _) } + + /** Holds if this argument occurs at the given position in the given call. */ + final predicate argumentOf(DataFlowCall call, int pos) { argumentNode(this, call, pos) } +} + /** * A node from which flow can return to the caller. This is either a regular * `ReturnNode` or a `PostUpdateNode` corresponding to the value of a parameter. */ class ReturnNodeExt extends Node { - ReturnNodeExt() { - this instanceof ReturnNode or - parameterValueFlowsToPreUpdate(_, this) - } + ReturnNodeExt() { returnNodeExt(this, _) } /** Gets the kind of this returned value. */ - ReturnKindExt getKind() { - result = TValueReturn(this.(ReturnNode).getKind()) - or - exists(ParameterNode p, int pos | - parameterValueFlowsToPreUpdate(p, this) and - p.isParameterOf(_, pos) and - result = TParamUpdate(pos) - ) - } + ReturnKindExt getKind() { returnNodeExt(this, result) } } /** @@ -940,11 +1039,7 @@ class ReturnNodeExt extends Node { * or a post-update node associated with a call argument. */ class OutNodeExt extends Node { - OutNodeExt() { - this instanceof OutNode - or - this.(PostUpdateNode).getPreUpdateNode() instanceof ArgumentNode - } + OutNodeExt() { outNodeExt(this) } } /** @@ -957,7 +1052,7 @@ abstract class ReturnKindExt extends TReturnKindExt { abstract string toString(); /** Gets a node corresponding to data flow out of `call`. */ - abstract OutNodeExt getAnOutNode(DataFlowCall call); + final OutNodeExt getAnOutNode(DataFlowCall call) { result = getAnOutNodeExt(call, this) } } class ValueReturnKind extends ReturnKindExt, TValueReturn { @@ -968,10 +1063,6 @@ class ValueReturnKind extends ReturnKindExt, TValueReturn { ReturnKind getKind() { result = kind } override string toString() { result = kind.toString() } - - override OutNodeExt getAnOutNode(DataFlowCall call) { - result = getAnOutNode(call, this.getKind()) - } } class ParamUpdateReturnKind extends ReturnKindExt, TParamUpdate { @@ -982,13 +1073,6 @@ class ParamUpdateReturnKind extends ReturnKindExt, TParamUpdate { int getPosition() { result = pos } override string toString() { result = "param update " + pos } - - override OutNodeExt getAnOutNode(DataFlowCall call) { - exists(ArgumentNode arg | - result.(PostUpdateNode).getPreUpdateNode() = arg and - arg.argumentOf(call, this.getPosition()) - ) - } } /** A callable tagged with a relevant return kind. */ @@ -1015,10 +1099,13 @@ class ReturnPosition extends TReturnPosition0 { */ pragma[inline] DataFlowCallable getNodeEnclosingCallable(Node n) { - exists(Node n0 | - pragma[only_bind_into](n0) = n and - pragma[only_bind_into](result) = n0.getEnclosingCallable() - ) + nodeEnclosingCallable(pragma[only_bind_out](n), pragma[only_bind_into](result)) +} + +/** Gets the type of `n` used for type pruning. */ +pragma[inline] +DataFlowType getNodeDataFlowType(Node n) { + nodeDataFlowType(pragma[only_bind_out](n), pragma[only_bind_into](result)) } pragma[noinline] @@ -1042,7 +1129,7 @@ predicate resolveReturn(CallContext cc, DataFlowCallable callable, DataFlowCall cc instanceof CallContextAny and callable = viableCallableExt(call) or exists(DataFlowCallable c0, DataFlowCall call0 | - call0.getEnclosingCallable() = callable and + callEnclosingCallable(call0, callable) and cc = TReturn(c0, call0) and c0 = prunedViableImplInCallContextReverse(call0, call) ) @@ -1063,8 +1150,6 @@ DataFlowCallable resolveCall(DataFlowCall call, CallContext cc) { result = viableCallableExt(call) and cc instanceof CallContextReturn } -predicate read = readStep/3; - /** An optional Boolean value. */ class BooleanOption extends TBooleanOption { string toString() { @@ -1116,7 +1201,7 @@ abstract class AccessPathFront extends TAccessPathFront { TypedContent getHead() { this = TFrontHead(result) } - predicate isClearedAt(Node n) { clearsContent(n, getHead().getContent()) } + predicate isClearedAt(Node n) { clearsContentCached(n, getHead().getContent()) } } class AccessPathFrontNil extends AccessPathFront, TFrontNil { 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 9498e51e7e6..cf83530bcbd 100644 --- a/cpp/ql/src/semmle/code/cpp/dataflow/internal/DataFlowImplLocal.qll +++ b/cpp/ql/src/semmle/code/cpp/dataflow/internal/DataFlowImplLocal.qll @@ -211,10 +211,7 @@ private predicate fullBarrier(Node node, Configuration config) { * Holds if data can flow in one local step from `node1` to `node2`. */ private predicate localFlowStep(Node node1, Node node2, Configuration config) { - ( - simpleLocalFlowStep(node1, node2) or - reverseStepThroughInputOutputAlias(node1, node2) - ) and + simpleLocalFlowStepExt(node1, node2) and not outBarrier(node1, config) and not inBarrier(node2, config) and not fullBarrier(node1, config) and @@ -237,7 +234,7 @@ private predicate additionalLocalFlowStep(Node node1, Node node2, Configuration * Holds if data can flow from `node1` to `node2` in a way that discards call contexts. */ private predicate jumpStep(Node node1, Node node2, Configuration config) { - jumpStep(node1, node2) and + jumpStepCached(node1, node2) and not outBarrier(node1, config) and not inBarrier(node2, config) and not fullBarrier(node1, config) and @@ -388,7 +385,7 @@ private module Stage1 { */ pragma[nomagic] private predicate fwdFlowIsEntered(DataFlowCall call, Cc cc, Configuration config) { - exists(ArgumentNode arg | + exists(ArgumentNodeExt arg | fwdFlow(arg, cc, config) and viableParamArg(call, _, arg) ) @@ -515,7 +512,7 @@ private module Stage1 { pragma[nomagic] predicate viableParamArgNodeCandFwd1( - DataFlowCall call, ParameterNode p, ArgumentNode arg, Configuration config + DataFlowCall call, ParameterNodeExt p, ArgumentNodeExt arg, Configuration config ) { viableParamArg(call, p, arg) and fwdFlow(arg, config) @@ -523,16 +520,16 @@ private module Stage1 { pragma[nomagic] private predicate revFlowIn( - DataFlowCall call, ArgumentNode arg, boolean toReturn, Configuration config + DataFlowCall call, ArgumentNodeExt arg, boolean toReturn, Configuration config ) { - exists(ParameterNode p | + exists(ParameterNodeExt p | revFlow(p, toReturn, config) and viableParamArgNodeCandFwd1(call, p, arg, config) ) } pragma[nomagic] - private predicate revFlowInToReturn(DataFlowCall call, ArgumentNode arg, Configuration config) { + private predicate revFlowInToReturn(DataFlowCall call, ArgumentNodeExt arg, Configuration config) { revFlowIn(call, arg, true, config) } @@ -597,7 +594,9 @@ private module Stage1 { * Holds if flow may enter through `p` and reach a return node making `p` a * candidate for the origin of a summary. */ - predicate parameterMayFlowThrough(ParameterNode p, DataFlowCallable c, Ap ap, Configuration config) { + predicate parameterMayFlowThrough( + ParameterNodeExt p, DataFlowCallable c, Ap ap, Configuration config + ) { exists(ReturnKindExt kind | throughFlowNodeCand(p, config) and returnFlowCallableNodeCand(c, kind, config) and @@ -663,7 +662,7 @@ private predicate flowOutOfCallNodeCand1( pragma[nomagic] private predicate viableParamArgNodeCand1( - DataFlowCall call, ParameterNode p, ArgumentNode arg, Configuration config + DataFlowCall call, ParameterNodeExt p, ArgumentNodeExt arg, Configuration config ) { Stage1::viableParamArgNodeCandFwd1(call, p, arg, config) and Stage1::revFlow(arg, config) @@ -675,7 +674,7 @@ private predicate viableParamArgNodeCand1( */ pragma[nomagic] private predicate flowIntoCallNodeCand1( - DataFlowCall call, ArgumentNode arg, ParameterNode p, Configuration config + DataFlowCall call, ArgumentNodeExt arg, ParameterNodeExt p, Configuration config ) { viableParamArgNodeCand1(call, p, arg, config) and Stage1::revFlow(p, config) and @@ -735,7 +734,7 @@ private predicate flowOutOfCallNodeCand1( */ pragma[nomagic] private predicate flowIntoCallNodeCand1( - DataFlowCall call, ArgumentNode arg, ParameterNode p, boolean allowsFieldFlow, + DataFlowCall call, ArgumentNodeExt arg, ParameterNodeExt p, boolean allowsFieldFlow, Configuration config ) { flowIntoCallNodeCand1(call, arg, p, config) and @@ -944,10 +943,10 @@ private module Stage2 { pragma[nomagic] private predicate fwdFlowIn( - DataFlowCall call, ParameterNode p, Cc outercc, Cc innercc, ApOption argAp, Ap ap, + DataFlowCall call, ParameterNodeExt p, Cc outercc, Cc innercc, ApOption argAp, Ap ap, Configuration config ) { - exists(ArgumentNode arg, boolean allowsFieldFlow | + exists(ArgumentNodeExt arg, boolean allowsFieldFlow | fwdFlow(arg, outercc, argAp, ap, config) and flowIntoCall(call, arg, p, allowsFieldFlow, config) and innercc = getCallContextCall(call, getNodeEnclosingCallable(p), outercc) @@ -992,7 +991,7 @@ private module Stage2 { private predicate fwdFlowIsEntered( DataFlowCall call, Cc cc, ApOption argAp, Ap ap, Configuration config ) { - exists(ParameterNode p | + exists(ParameterNodeExt p | fwdFlowIn(call, p, cc, _, argAp, ap, config) and PrevStage::parameterMayFlowThrough(p, _, getApprox(ap), config) ) @@ -1133,10 +1132,10 @@ private module Stage2 { pragma[nomagic] private predicate revFlowIn( - DataFlowCall call, ArgumentNode arg, boolean toReturn, ApOption returnAp, Ap ap, + DataFlowCall call, ArgumentNodeExt arg, boolean toReturn, ApOption returnAp, Ap ap, Configuration config ) { - exists(ParameterNode p, boolean allowsFieldFlow | + exists(ParameterNodeExt p, boolean allowsFieldFlow | revFlow(p, toReturn, returnAp, ap, config) and flowIntoCall(call, arg, p, allowsFieldFlow, config) | @@ -1146,7 +1145,7 @@ private module Stage2 { pragma[nomagic] private predicate revFlowInToReturn( - DataFlowCall call, ArgumentNode arg, Ap returnAp, Ap ap, Configuration config + DataFlowCall call, ArgumentNodeExt arg, Ap returnAp, Ap ap, Configuration config ) { revFlowIn(call, arg, true, apSome(returnAp), ap, config) } @@ -1199,13 +1198,15 @@ private module Stage2 { pragma[noinline] private predicate parameterFlow( - ParameterNode p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config + ParameterNodeExt p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config ) { revFlow(p, true, apSome(ap0), ap, config) and c = getNodeEnclosingCallable(p) } - predicate parameterMayFlowThrough(ParameterNode p, DataFlowCallable c, Ap ap, Configuration config) { + predicate parameterMayFlowThrough( + ParameterNodeExt p, DataFlowCallable c, Ap ap, Configuration config + ) { exists(ReturnNodeExt ret, Ap ap0, ReturnKindExt kind, int pos | parameterFlow(p, ap, ap0, c, config) and c = getNodeEnclosingCallable(ret) and @@ -1245,7 +1246,7 @@ private predicate flowOutOfCallNodeCand2( pragma[nomagic] private predicate flowIntoCallNodeCand2( - DataFlowCall call, ArgumentNode node1, ParameterNode node2, boolean allowsFieldFlow, + DataFlowCall call, ArgumentNodeExt node1, ParameterNodeExt node2, boolean allowsFieldFlow, Configuration config ) { flowIntoCallNodeCand1(call, node1, node2, allowsFieldFlow, config) and @@ -1260,8 +1261,8 @@ private module LocalFlowBigStep { */ private class FlowCheckNode extends Node { FlowCheckNode() { - this instanceof CastNode or - clearsContent(this, _) + castNode(this) or + clearsContentCached(this, _) } } @@ -1275,7 +1276,7 @@ private module LocalFlowBigStep { config.isSource(node) or jumpStep(_, node, config) or additionalJumpStep(_, node, config) or - node instanceof ParameterNode or + node instanceof ParameterNodeExt or node instanceof OutNodeExt or store(_, _, node, _) or read(_, _, node) or @@ -1321,21 +1322,21 @@ private module LocalFlowBigStep { Node node1, Node node2, boolean preservesValue, DataFlowType t, Configuration config, LocalCallContext cc ) { - not isUnreachableInCall(node2, cc.(LocalCallContextSpecificCall).getCall()) and + not isUnreachableInCallCached(node2, cc.(LocalCallContextSpecificCall).getCall()) and ( localFlowEntry(node1, pragma[only_bind_into](config)) and ( localFlowStepNodeCand1(node1, node2, config) and preservesValue = true and - t = getNodeType(node1) + t = getNodeDataFlowType(node1) or additionalLocalFlowStepNodeCand2(node1, node2, config) and preservesValue = false and - t = getNodeType(node2) + t = getNodeDataFlowType(node2) ) and node1 != node2 and cc.relevantFor(getNodeEnclosingCallable(node1)) and - not isUnreachableInCall(node1, cc.(LocalCallContextSpecificCall).getCall()) and + not isUnreachableInCallCached(node1, cc.(LocalCallContextSpecificCall).getCall()) and Stage2::revFlow(node2, pragma[only_bind_into](config)) or exists(Node mid | @@ -1350,7 +1351,7 @@ private module LocalFlowBigStep { additionalLocalFlowStepNodeCand2(mid, node2, config) and not mid instanceof FlowCheckNode and preservesValue = false and - t = getNodeType(node2) and + t = getNodeDataFlowType(node2) and Stage2::revFlow(node2, pragma[only_bind_into](config)) ) ) @@ -1384,7 +1385,7 @@ private module Stage3 { private ApApprox getApprox(Ap ap) { result = ap.toBoolNonEmpty() } private ApNil getApNil(Node node) { - PrevStage::revFlow(node, _) and result = TFrontNil(getNodeType(node)) + PrevStage::revFlow(node, _) and result = TFrontNil(getNodeDataFlowType(node)) } bindingset[tc, tail] @@ -1443,7 +1444,9 @@ private module Stage3 { bindingset[node, ap] private predicate filter(Node node, Ap ap) { not ap.isClearedAt(node) and - if node instanceof CastingNode then compatibleTypes(getNodeType(node), ap.getType()) else any() + if node instanceof CastingNode + then compatibleTypes(getNodeDataFlowType(node), ap.getType()) + else any() } bindingset[ap, contentType] @@ -1583,10 +1586,10 @@ private module Stage3 { pragma[nomagic] private predicate fwdFlowIn( - DataFlowCall call, ParameterNode p, Cc outercc, Cc innercc, ApOption argAp, Ap ap, + DataFlowCall call, ParameterNodeExt p, Cc outercc, Cc innercc, ApOption argAp, Ap ap, Configuration config ) { - exists(ArgumentNode arg, boolean allowsFieldFlow | + exists(ArgumentNodeExt arg, boolean allowsFieldFlow | fwdFlow(arg, outercc, argAp, ap, config) and flowIntoCall(call, arg, p, allowsFieldFlow, config) and innercc = getCallContextCall(call, getNodeEnclosingCallable(p), outercc) @@ -1631,7 +1634,7 @@ private module Stage3 { private predicate fwdFlowIsEntered( DataFlowCall call, Cc cc, ApOption argAp, Ap ap, Configuration config ) { - exists(ParameterNode p | + exists(ParameterNodeExt p | fwdFlowIn(call, p, cc, _, argAp, ap, config) and PrevStage::parameterMayFlowThrough(p, _, unbindApa(getApprox(ap)), config) ) @@ -1772,10 +1775,10 @@ private module Stage3 { pragma[nomagic] private predicate revFlowIn( - DataFlowCall call, ArgumentNode arg, boolean toReturn, ApOption returnAp, Ap ap, + DataFlowCall call, ArgumentNodeExt arg, boolean toReturn, ApOption returnAp, Ap ap, Configuration config ) { - exists(ParameterNode p, boolean allowsFieldFlow | + exists(ParameterNodeExt p, boolean allowsFieldFlow | revFlow(p, toReturn, returnAp, ap, config) and flowIntoCall(call, arg, p, allowsFieldFlow, config) | @@ -1785,7 +1788,7 @@ private module Stage3 { pragma[nomagic] private predicate revFlowInToReturn( - DataFlowCall call, ArgumentNode arg, Ap returnAp, Ap ap, Configuration config + DataFlowCall call, ArgumentNodeExt arg, Ap returnAp, Ap ap, Configuration config ) { revFlowIn(call, arg, true, apSome(returnAp), ap, config) } @@ -1838,13 +1841,15 @@ private module Stage3 { pragma[noinline] private predicate parameterFlow( - ParameterNode p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config + ParameterNodeExt p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config ) { revFlow(p, true, apSome(ap0), ap, config) and c = getNodeEnclosingCallable(p) } - predicate parameterMayFlowThrough(ParameterNode p, DataFlowCallable c, Ap ap, Configuration config) { + predicate parameterMayFlowThrough( + ParameterNodeExt p, DataFlowCallable c, Ap ap, Configuration config + ) { exists(ReturnNodeExt ret, Ap ap0, ReturnKindExt kind, int pos | parameterFlow(p, ap, ap0, c, config) and c = getNodeEnclosingCallable(ret) and @@ -2088,7 +2093,7 @@ private module Stage4 { private ApApprox getApprox(Ap ap) { result = ap.getFront() } private ApNil getApNil(Node node) { - PrevStage::revFlow(node, _) and result = TNil(getNodeType(node)) + PrevStage::revFlow(node, _) and result = TNil(getNodeDataFlowType(node)) } bindingset[tc, tail] @@ -2155,7 +2160,7 @@ private module Stage4 { pragma[nomagic] private predicate flowIntoCall( - DataFlowCall call, ArgumentNode node1, ParameterNode node2, boolean allowsFieldFlow, + DataFlowCall call, ArgumentNodeExt node1, ParameterNodeExt node2, boolean allowsFieldFlow, Configuration config ) { flowIntoCallNodeCand2(call, node1, node2, allowsFieldFlow, config) and @@ -2300,10 +2305,10 @@ private module Stage4 { pragma[nomagic] private predicate fwdFlowIn( - DataFlowCall call, ParameterNode p, Cc outercc, Cc innercc, ApOption argAp, Ap ap, + DataFlowCall call, ParameterNodeExt p, Cc outercc, Cc innercc, ApOption argAp, Ap ap, Configuration config ) { - exists(ArgumentNode arg, boolean allowsFieldFlow | + exists(ArgumentNodeExt arg, boolean allowsFieldFlow | fwdFlow(arg, outercc, argAp, ap, config) and flowIntoCall(call, arg, p, allowsFieldFlow, config) and innercc = getCallContextCall(call, getNodeEnclosingCallable(p), outercc) @@ -2348,7 +2353,7 @@ private module Stage4 { private predicate fwdFlowIsEntered( DataFlowCall call, Cc cc, ApOption argAp, Ap ap, Configuration config ) { - exists(ParameterNode p | + exists(ParameterNodeExt p | fwdFlowIn(call, p, cc, _, argAp, ap, config) and PrevStage::parameterMayFlowThrough(p, _, unbindApa(getApprox(ap)), config) ) @@ -2489,10 +2494,10 @@ private module Stage4 { pragma[nomagic] private predicate revFlowIn( - DataFlowCall call, ArgumentNode arg, boolean toReturn, ApOption returnAp, Ap ap, + DataFlowCall call, ArgumentNodeExt arg, boolean toReturn, ApOption returnAp, Ap ap, Configuration config ) { - exists(ParameterNode p, boolean allowsFieldFlow | + exists(ParameterNodeExt p, boolean allowsFieldFlow | revFlow(p, toReturn, returnAp, ap, config) and flowIntoCall(call, arg, p, allowsFieldFlow, config) | @@ -2502,7 +2507,7 @@ private module Stage4 { pragma[nomagic] private predicate revFlowInToReturn( - DataFlowCall call, ArgumentNode arg, Ap returnAp, Ap ap, Configuration config + DataFlowCall call, ArgumentNodeExt arg, Ap returnAp, Ap ap, Configuration config ) { revFlowIn(call, arg, true, apSome(returnAp), ap, config) } @@ -2555,13 +2560,15 @@ private module Stage4 { pragma[noinline] private predicate parameterFlow( - ParameterNode p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config + ParameterNodeExt p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config ) { revFlow(p, true, apSome(ap0), ap, config) and c = getNodeEnclosingCallable(p) } - predicate parameterMayFlowThrough(ParameterNode p, DataFlowCallable c, Ap ap, Configuration config) { + predicate parameterMayFlowThrough( + ParameterNodeExt p, DataFlowCallable c, Ap ap, Configuration config + ) { exists(ReturnNodeExt ret, Ap ap0, ReturnKindExt kind, int pos | parameterFlow(p, ap, ap0, c, config) and c = getNodeEnclosingCallable(ret) and @@ -2606,7 +2613,7 @@ private predicate nodeMayUseSummary(Node n, AccessPathApprox apa, Configuration private newtype TSummaryCtx = TSummaryCtxNone() or - TSummaryCtxSome(ParameterNode p, AccessPath ap) { + TSummaryCtxSome(ParameterNodeExt p, AccessPath ap) { Stage4::parameterMayFlowThrough(p, _, ap.getApprox(), _) } @@ -2627,7 +2634,7 @@ private class SummaryCtxNone extends SummaryCtx, TSummaryCtxNone { /** A summary context from which a flow summary can be generated. */ private class SummaryCtxSome extends SummaryCtx, TSummaryCtxSome { - private ParameterNode p; + private ParameterNodeExt p; private AccessPath ap; SummaryCtxSome() { this = TSummaryCtxSome(p, ap) } @@ -2758,7 +2765,7 @@ private newtype TPathNode = config.isSource(node) and cc instanceof CallContextAny and sc instanceof SummaryCtxNone and - ap = TAccessPathNil(getNodeType(node)) + ap = TAccessPathNil(getNodeDataFlowType(node)) or // ... or a step from an existing PathNode to another node. exists(PathNodeMid mid | @@ -2979,7 +2986,7 @@ class PathNode extends TPathNode { Configuration getConfiguration() { none() } private predicate isHidden() { - nodeIsHidden(this.getNode()) and + hiddenNode(this.getNode()) and not this.isSource() and not this instanceof PathNodeSink } @@ -3148,7 +3155,7 @@ private predicate pathStep(PathNodeMid mid, Node node, CallContext cc, SummaryCt cc instanceof CallContextAny and sc instanceof SummaryCtxNone and mid.getAp() instanceof AccessPathNil and - ap = TAccessPathNil(getNodeType(node)) + ap = TAccessPathNil(getNodeDataFlowType(node)) or exists(TypedContent tc | pathStoreStep(mid, node, ap.pop(tc), tc, cc)) and sc = mid.getSummaryCtx() @@ -3235,7 +3242,7 @@ pragma[noinline] private predicate pathIntoArg( PathNodeMid mid, int i, CallContext cc, DataFlowCall call, AccessPath ap, AccessPathApprox apa ) { - exists(ArgumentNode arg | + exists(ArgumentNodeExt arg | arg = mid.getNode() and cc = mid.getCallContext() and arg.argumentOf(call, i) and @@ -3248,7 +3255,7 @@ pragma[noinline] private predicate parameterCand( DataFlowCallable callable, int i, AccessPathApprox apa, Configuration config ) { - exists(ParameterNode p | + exists(ParameterNodeExt p | Stage4::revFlow(p, _, _, apa, config) and p.isParameterOf(callable, i) ) @@ -3272,7 +3279,7 @@ private predicate pathIntoCallable0( * respectively. */ private predicate pathIntoCallable( - PathNodeMid mid, ParameterNode p, CallContext outercc, CallContextCall innercc, SummaryCtx sc, + PathNodeMid mid, ParameterNodeExt p, CallContext outercc, CallContextCall innercc, SummaryCtx sc, DataFlowCall call ) { exists(int i, DataFlowCallable callable, AccessPath ap | @@ -3568,7 +3575,7 @@ private module FlowExploration { private newtype TSummaryCtx1 = TSummaryCtx1None() or - TSummaryCtx1Param(ParameterNode p) + TSummaryCtx1Param(ParameterNodeExt p) private newtype TSummaryCtx2 = TSummaryCtx2None() or @@ -3591,7 +3598,7 @@ private module FlowExploration { cc instanceof CallContextAny and sc1 = TSummaryCtx1None() and sc2 = TSummaryCtx2None() and - ap = TPartialNil(getNodeType(node)) and + ap = TPartialNil(getNodeDataFlowType(node)) and not fullBarrier(node, config) and exists(config.explorationLimit()) or @@ -3611,7 +3618,7 @@ private module FlowExploration { or exists(PartialPathNodeRev mid | revPartialPathStep(mid, node, sc1, sc2, ap, config) and - not clearsContent(node, ap.getHead()) and + not clearsContentCached(node, ap.getHead()) and not fullBarrier(node, config) and distSink(getNodeEnclosingCallable(node), config) <= config.explorationLimit() ) @@ -3625,9 +3632,9 @@ private module FlowExploration { exists(PartialPathNodeFwd mid | partialPathStep(mid, node, cc, sc1, sc2, ap, config) and not fullBarrier(node, config) and - not clearsContent(node, ap.getHead().getContent()) and + not clearsContentCached(node, ap.getHead().getContent()) and if node instanceof CastingNode - then compatibleTypes(getNodeType(node), ap.getType()) + then compatibleTypes(getNodeDataFlowType(node), ap.getType()) else any() ) } @@ -3783,7 +3790,7 @@ private module FlowExploration { PartialPathNodeFwd mid, Node node, CallContext cc, TSummaryCtx1 sc1, TSummaryCtx2 sc2, PartialAccessPath ap, Configuration config ) { - not isUnreachableInCall(node, cc.(CallContextSpecificCall).getCall()) and + not isUnreachableInCallCached(node, cc.(CallContextSpecificCall).getCall()) and ( localFlowStep(mid.getNode(), node, config) and cc = mid.getCallContext() and @@ -3797,7 +3804,7 @@ private module FlowExploration { sc1 = mid.getSummaryCtx1() and sc2 = mid.getSummaryCtx2() and mid.getAp() instanceof PartialAccessPathNil and - ap = TPartialNil(getNodeType(node)) and + ap = TPartialNil(getNodeDataFlowType(node)) and config = mid.getConfiguration() ) or @@ -3813,7 +3820,7 @@ private module FlowExploration { sc1 = TSummaryCtx1None() and sc2 = TSummaryCtx2None() and mid.getAp() instanceof PartialAccessPathNil and - ap = TPartialNil(getNodeType(node)) and + ap = TPartialNil(getNodeDataFlowType(node)) and config = mid.getConfiguration() or partialPathStoreStep(mid, _, _, node, ap) and @@ -3827,7 +3834,7 @@ private module FlowExploration { sc1 = mid.getSummaryCtx1() and sc2 = mid.getSummaryCtx2() and apConsFwd(ap, tc, ap0, config) and - compatibleTypes(ap.getType(), getNodeType(node)) + compatibleTypes(ap.getType(), getNodeDataFlowType(node)) ) or partialPathIntoCallable(mid, node, _, cc, sc1, sc2, _, ap, config) @@ -3924,7 +3931,7 @@ private module FlowExploration { PartialPathNodeFwd mid, int i, CallContext cc, DataFlowCall call, PartialAccessPath ap, Configuration config ) { - exists(ArgumentNode arg | + exists(ArgumentNodeExt arg | arg = mid.getNode() and cc = mid.getCallContext() and arg.argumentOf(call, i) and @@ -3943,7 +3950,7 @@ private module FlowExploration { } private predicate partialPathIntoCallable( - PartialPathNodeFwd mid, ParameterNode p, CallContext outercc, CallContextCall innercc, + PartialPathNodeFwd mid, ParameterNodeExt p, CallContext outercc, CallContextCall innercc, TSummaryCtx1 sc1, TSummaryCtx2 sc2, DataFlowCall call, PartialAccessPath ap, Configuration config ) { @@ -3980,7 +3987,7 @@ private module FlowExploration { DataFlowCall call, PartialPathNodeFwd mid, ReturnKindExt kind, CallContext cc, PartialAccessPath ap, Configuration config ) { - exists(ParameterNode p, CallContext innercc, TSummaryCtx1 sc1, TSummaryCtx2 sc2 | + exists(ParameterNodeExt p, CallContext innercc, TSummaryCtx1 sc1, TSummaryCtx2 sc2 | partialPathIntoCallable(mid, p, cc, innercc, sc1, sc2, call, _, config) and paramFlowsThroughInPartialPath(kind, innercc, sc1, sc2, ap, config) ) @@ -4037,7 +4044,7 @@ private module FlowExploration { apConsRev(ap, c, ap0, config) ) or - exists(ParameterNode p | + exists(ParameterNodeExt p | mid.getNode() = p and viableParamArg(_, p, node) and sc1 = mid.getSummaryCtx1() and @@ -4115,7 +4122,7 @@ private module FlowExploration { int pos, TRevSummaryCtx1Some sc1, TRevSummaryCtx2Some sc2, RevPartialAccessPath ap, Configuration config ) { - exists(PartialPathNodeRev mid, ParameterNode p | + exists(PartialPathNodeRev mid, ParameterNodeExt p | mid.getNode() = p and p.isParameterOf(_, pos) and sc1 = mid.getSummaryCtx1() and @@ -4138,7 +4145,7 @@ private module FlowExploration { pragma[nomagic] private predicate revPartialPathThroughCallable( - PartialPathNodeRev mid, ArgumentNode node, RevPartialAccessPath ap, Configuration config + PartialPathNodeRev mid, ArgumentNodeExt node, RevPartialAccessPath ap, Configuration config ) { exists(DataFlowCall call, int pos | revPartialPathThroughCallable0(call, mid, pos, ap, config) and 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 9498e51e7e6..cf83530bcbd 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 @@ -211,10 +211,7 @@ private predicate fullBarrier(Node node, Configuration config) { * Holds if data can flow in one local step from `node1` to `node2`. */ private predicate localFlowStep(Node node1, Node node2, Configuration config) { - ( - simpleLocalFlowStep(node1, node2) or - reverseStepThroughInputOutputAlias(node1, node2) - ) and + simpleLocalFlowStepExt(node1, node2) and not outBarrier(node1, config) and not inBarrier(node2, config) and not fullBarrier(node1, config) and @@ -237,7 +234,7 @@ private predicate additionalLocalFlowStep(Node node1, Node node2, Configuration * Holds if data can flow from `node1` to `node2` in a way that discards call contexts. */ private predicate jumpStep(Node node1, Node node2, Configuration config) { - jumpStep(node1, node2) and + jumpStepCached(node1, node2) and not outBarrier(node1, config) and not inBarrier(node2, config) and not fullBarrier(node1, config) and @@ -388,7 +385,7 @@ private module Stage1 { */ pragma[nomagic] private predicate fwdFlowIsEntered(DataFlowCall call, Cc cc, Configuration config) { - exists(ArgumentNode arg | + exists(ArgumentNodeExt arg | fwdFlow(arg, cc, config) and viableParamArg(call, _, arg) ) @@ -515,7 +512,7 @@ private module Stage1 { pragma[nomagic] predicate viableParamArgNodeCandFwd1( - DataFlowCall call, ParameterNode p, ArgumentNode arg, Configuration config + DataFlowCall call, ParameterNodeExt p, ArgumentNodeExt arg, Configuration config ) { viableParamArg(call, p, arg) and fwdFlow(arg, config) @@ -523,16 +520,16 @@ private module Stage1 { pragma[nomagic] private predicate revFlowIn( - DataFlowCall call, ArgumentNode arg, boolean toReturn, Configuration config + DataFlowCall call, ArgumentNodeExt arg, boolean toReturn, Configuration config ) { - exists(ParameterNode p | + exists(ParameterNodeExt p | revFlow(p, toReturn, config) and viableParamArgNodeCandFwd1(call, p, arg, config) ) } pragma[nomagic] - private predicate revFlowInToReturn(DataFlowCall call, ArgumentNode arg, Configuration config) { + private predicate revFlowInToReturn(DataFlowCall call, ArgumentNodeExt arg, Configuration config) { revFlowIn(call, arg, true, config) } @@ -597,7 +594,9 @@ private module Stage1 { * Holds if flow may enter through `p` and reach a return node making `p` a * candidate for the origin of a summary. */ - predicate parameterMayFlowThrough(ParameterNode p, DataFlowCallable c, Ap ap, Configuration config) { + predicate parameterMayFlowThrough( + ParameterNodeExt p, DataFlowCallable c, Ap ap, Configuration config + ) { exists(ReturnKindExt kind | throughFlowNodeCand(p, config) and returnFlowCallableNodeCand(c, kind, config) and @@ -663,7 +662,7 @@ private predicate flowOutOfCallNodeCand1( pragma[nomagic] private predicate viableParamArgNodeCand1( - DataFlowCall call, ParameterNode p, ArgumentNode arg, Configuration config + DataFlowCall call, ParameterNodeExt p, ArgumentNodeExt arg, Configuration config ) { Stage1::viableParamArgNodeCandFwd1(call, p, arg, config) and Stage1::revFlow(arg, config) @@ -675,7 +674,7 @@ private predicate viableParamArgNodeCand1( */ pragma[nomagic] private predicate flowIntoCallNodeCand1( - DataFlowCall call, ArgumentNode arg, ParameterNode p, Configuration config + DataFlowCall call, ArgumentNodeExt arg, ParameterNodeExt p, Configuration config ) { viableParamArgNodeCand1(call, p, arg, config) and Stage1::revFlow(p, config) and @@ -735,7 +734,7 @@ private predicate flowOutOfCallNodeCand1( */ pragma[nomagic] private predicate flowIntoCallNodeCand1( - DataFlowCall call, ArgumentNode arg, ParameterNode p, boolean allowsFieldFlow, + DataFlowCall call, ArgumentNodeExt arg, ParameterNodeExt p, boolean allowsFieldFlow, Configuration config ) { flowIntoCallNodeCand1(call, arg, p, config) and @@ -944,10 +943,10 @@ private module Stage2 { pragma[nomagic] private predicate fwdFlowIn( - DataFlowCall call, ParameterNode p, Cc outercc, Cc innercc, ApOption argAp, Ap ap, + DataFlowCall call, ParameterNodeExt p, Cc outercc, Cc innercc, ApOption argAp, Ap ap, Configuration config ) { - exists(ArgumentNode arg, boolean allowsFieldFlow | + exists(ArgumentNodeExt arg, boolean allowsFieldFlow | fwdFlow(arg, outercc, argAp, ap, config) and flowIntoCall(call, arg, p, allowsFieldFlow, config) and innercc = getCallContextCall(call, getNodeEnclosingCallable(p), outercc) @@ -992,7 +991,7 @@ private module Stage2 { private predicate fwdFlowIsEntered( DataFlowCall call, Cc cc, ApOption argAp, Ap ap, Configuration config ) { - exists(ParameterNode p | + exists(ParameterNodeExt p | fwdFlowIn(call, p, cc, _, argAp, ap, config) and PrevStage::parameterMayFlowThrough(p, _, getApprox(ap), config) ) @@ -1133,10 +1132,10 @@ private module Stage2 { pragma[nomagic] private predicate revFlowIn( - DataFlowCall call, ArgumentNode arg, boolean toReturn, ApOption returnAp, Ap ap, + DataFlowCall call, ArgumentNodeExt arg, boolean toReturn, ApOption returnAp, Ap ap, Configuration config ) { - exists(ParameterNode p, boolean allowsFieldFlow | + exists(ParameterNodeExt p, boolean allowsFieldFlow | revFlow(p, toReturn, returnAp, ap, config) and flowIntoCall(call, arg, p, allowsFieldFlow, config) | @@ -1146,7 +1145,7 @@ private module Stage2 { pragma[nomagic] private predicate revFlowInToReturn( - DataFlowCall call, ArgumentNode arg, Ap returnAp, Ap ap, Configuration config + DataFlowCall call, ArgumentNodeExt arg, Ap returnAp, Ap ap, Configuration config ) { revFlowIn(call, arg, true, apSome(returnAp), ap, config) } @@ -1199,13 +1198,15 @@ private module Stage2 { pragma[noinline] private predicate parameterFlow( - ParameterNode p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config + ParameterNodeExt p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config ) { revFlow(p, true, apSome(ap0), ap, config) and c = getNodeEnclosingCallable(p) } - predicate parameterMayFlowThrough(ParameterNode p, DataFlowCallable c, Ap ap, Configuration config) { + predicate parameterMayFlowThrough( + ParameterNodeExt p, DataFlowCallable c, Ap ap, Configuration config + ) { exists(ReturnNodeExt ret, Ap ap0, ReturnKindExt kind, int pos | parameterFlow(p, ap, ap0, c, config) and c = getNodeEnclosingCallable(ret) and @@ -1245,7 +1246,7 @@ private predicate flowOutOfCallNodeCand2( pragma[nomagic] private predicate flowIntoCallNodeCand2( - DataFlowCall call, ArgumentNode node1, ParameterNode node2, boolean allowsFieldFlow, + DataFlowCall call, ArgumentNodeExt node1, ParameterNodeExt node2, boolean allowsFieldFlow, Configuration config ) { flowIntoCallNodeCand1(call, node1, node2, allowsFieldFlow, config) and @@ -1260,8 +1261,8 @@ private module LocalFlowBigStep { */ private class FlowCheckNode extends Node { FlowCheckNode() { - this instanceof CastNode or - clearsContent(this, _) + castNode(this) or + clearsContentCached(this, _) } } @@ -1275,7 +1276,7 @@ private module LocalFlowBigStep { config.isSource(node) or jumpStep(_, node, config) or additionalJumpStep(_, node, config) or - node instanceof ParameterNode or + node instanceof ParameterNodeExt or node instanceof OutNodeExt or store(_, _, node, _) or read(_, _, node) or @@ -1321,21 +1322,21 @@ private module LocalFlowBigStep { Node node1, Node node2, boolean preservesValue, DataFlowType t, Configuration config, LocalCallContext cc ) { - not isUnreachableInCall(node2, cc.(LocalCallContextSpecificCall).getCall()) and + not isUnreachableInCallCached(node2, cc.(LocalCallContextSpecificCall).getCall()) and ( localFlowEntry(node1, pragma[only_bind_into](config)) and ( localFlowStepNodeCand1(node1, node2, config) and preservesValue = true and - t = getNodeType(node1) + t = getNodeDataFlowType(node1) or additionalLocalFlowStepNodeCand2(node1, node2, config) and preservesValue = false and - t = getNodeType(node2) + t = getNodeDataFlowType(node2) ) and node1 != node2 and cc.relevantFor(getNodeEnclosingCallable(node1)) and - not isUnreachableInCall(node1, cc.(LocalCallContextSpecificCall).getCall()) and + not isUnreachableInCallCached(node1, cc.(LocalCallContextSpecificCall).getCall()) and Stage2::revFlow(node2, pragma[only_bind_into](config)) or exists(Node mid | @@ -1350,7 +1351,7 @@ private module LocalFlowBigStep { additionalLocalFlowStepNodeCand2(mid, node2, config) and not mid instanceof FlowCheckNode and preservesValue = false and - t = getNodeType(node2) and + t = getNodeDataFlowType(node2) and Stage2::revFlow(node2, pragma[only_bind_into](config)) ) ) @@ -1384,7 +1385,7 @@ private module Stage3 { private ApApprox getApprox(Ap ap) { result = ap.toBoolNonEmpty() } private ApNil getApNil(Node node) { - PrevStage::revFlow(node, _) and result = TFrontNil(getNodeType(node)) + PrevStage::revFlow(node, _) and result = TFrontNil(getNodeDataFlowType(node)) } bindingset[tc, tail] @@ -1443,7 +1444,9 @@ private module Stage3 { bindingset[node, ap] private predicate filter(Node node, Ap ap) { not ap.isClearedAt(node) and - if node instanceof CastingNode then compatibleTypes(getNodeType(node), ap.getType()) else any() + if node instanceof CastingNode + then compatibleTypes(getNodeDataFlowType(node), ap.getType()) + else any() } bindingset[ap, contentType] @@ -1583,10 +1586,10 @@ private module Stage3 { pragma[nomagic] private predicate fwdFlowIn( - DataFlowCall call, ParameterNode p, Cc outercc, Cc innercc, ApOption argAp, Ap ap, + DataFlowCall call, ParameterNodeExt p, Cc outercc, Cc innercc, ApOption argAp, Ap ap, Configuration config ) { - exists(ArgumentNode arg, boolean allowsFieldFlow | + exists(ArgumentNodeExt arg, boolean allowsFieldFlow | fwdFlow(arg, outercc, argAp, ap, config) and flowIntoCall(call, arg, p, allowsFieldFlow, config) and innercc = getCallContextCall(call, getNodeEnclosingCallable(p), outercc) @@ -1631,7 +1634,7 @@ private module Stage3 { private predicate fwdFlowIsEntered( DataFlowCall call, Cc cc, ApOption argAp, Ap ap, Configuration config ) { - exists(ParameterNode p | + exists(ParameterNodeExt p | fwdFlowIn(call, p, cc, _, argAp, ap, config) and PrevStage::parameterMayFlowThrough(p, _, unbindApa(getApprox(ap)), config) ) @@ -1772,10 +1775,10 @@ private module Stage3 { pragma[nomagic] private predicate revFlowIn( - DataFlowCall call, ArgumentNode arg, boolean toReturn, ApOption returnAp, Ap ap, + DataFlowCall call, ArgumentNodeExt arg, boolean toReturn, ApOption returnAp, Ap ap, Configuration config ) { - exists(ParameterNode p, boolean allowsFieldFlow | + exists(ParameterNodeExt p, boolean allowsFieldFlow | revFlow(p, toReturn, returnAp, ap, config) and flowIntoCall(call, arg, p, allowsFieldFlow, config) | @@ -1785,7 +1788,7 @@ private module Stage3 { pragma[nomagic] private predicate revFlowInToReturn( - DataFlowCall call, ArgumentNode arg, Ap returnAp, Ap ap, Configuration config + DataFlowCall call, ArgumentNodeExt arg, Ap returnAp, Ap ap, Configuration config ) { revFlowIn(call, arg, true, apSome(returnAp), ap, config) } @@ -1838,13 +1841,15 @@ private module Stage3 { pragma[noinline] private predicate parameterFlow( - ParameterNode p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config + ParameterNodeExt p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config ) { revFlow(p, true, apSome(ap0), ap, config) and c = getNodeEnclosingCallable(p) } - predicate parameterMayFlowThrough(ParameterNode p, DataFlowCallable c, Ap ap, Configuration config) { + predicate parameterMayFlowThrough( + ParameterNodeExt p, DataFlowCallable c, Ap ap, Configuration config + ) { exists(ReturnNodeExt ret, Ap ap0, ReturnKindExt kind, int pos | parameterFlow(p, ap, ap0, c, config) and c = getNodeEnclosingCallable(ret) and @@ -2088,7 +2093,7 @@ private module Stage4 { private ApApprox getApprox(Ap ap) { result = ap.getFront() } private ApNil getApNil(Node node) { - PrevStage::revFlow(node, _) and result = TNil(getNodeType(node)) + PrevStage::revFlow(node, _) and result = TNil(getNodeDataFlowType(node)) } bindingset[tc, tail] @@ -2155,7 +2160,7 @@ private module Stage4 { pragma[nomagic] private predicate flowIntoCall( - DataFlowCall call, ArgumentNode node1, ParameterNode node2, boolean allowsFieldFlow, + DataFlowCall call, ArgumentNodeExt node1, ParameterNodeExt node2, boolean allowsFieldFlow, Configuration config ) { flowIntoCallNodeCand2(call, node1, node2, allowsFieldFlow, config) and @@ -2300,10 +2305,10 @@ private module Stage4 { pragma[nomagic] private predicate fwdFlowIn( - DataFlowCall call, ParameterNode p, Cc outercc, Cc innercc, ApOption argAp, Ap ap, + DataFlowCall call, ParameterNodeExt p, Cc outercc, Cc innercc, ApOption argAp, Ap ap, Configuration config ) { - exists(ArgumentNode arg, boolean allowsFieldFlow | + exists(ArgumentNodeExt arg, boolean allowsFieldFlow | fwdFlow(arg, outercc, argAp, ap, config) and flowIntoCall(call, arg, p, allowsFieldFlow, config) and innercc = getCallContextCall(call, getNodeEnclosingCallable(p), outercc) @@ -2348,7 +2353,7 @@ private module Stage4 { private predicate fwdFlowIsEntered( DataFlowCall call, Cc cc, ApOption argAp, Ap ap, Configuration config ) { - exists(ParameterNode p | + exists(ParameterNodeExt p | fwdFlowIn(call, p, cc, _, argAp, ap, config) and PrevStage::parameterMayFlowThrough(p, _, unbindApa(getApprox(ap)), config) ) @@ -2489,10 +2494,10 @@ private module Stage4 { pragma[nomagic] private predicate revFlowIn( - DataFlowCall call, ArgumentNode arg, boolean toReturn, ApOption returnAp, Ap ap, + DataFlowCall call, ArgumentNodeExt arg, boolean toReturn, ApOption returnAp, Ap ap, Configuration config ) { - exists(ParameterNode p, boolean allowsFieldFlow | + exists(ParameterNodeExt p, boolean allowsFieldFlow | revFlow(p, toReturn, returnAp, ap, config) and flowIntoCall(call, arg, p, allowsFieldFlow, config) | @@ -2502,7 +2507,7 @@ private module Stage4 { pragma[nomagic] private predicate revFlowInToReturn( - DataFlowCall call, ArgumentNode arg, Ap returnAp, Ap ap, Configuration config + DataFlowCall call, ArgumentNodeExt arg, Ap returnAp, Ap ap, Configuration config ) { revFlowIn(call, arg, true, apSome(returnAp), ap, config) } @@ -2555,13 +2560,15 @@ private module Stage4 { pragma[noinline] private predicate parameterFlow( - ParameterNode p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config + ParameterNodeExt p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config ) { revFlow(p, true, apSome(ap0), ap, config) and c = getNodeEnclosingCallable(p) } - predicate parameterMayFlowThrough(ParameterNode p, DataFlowCallable c, Ap ap, Configuration config) { + predicate parameterMayFlowThrough( + ParameterNodeExt p, DataFlowCallable c, Ap ap, Configuration config + ) { exists(ReturnNodeExt ret, Ap ap0, ReturnKindExt kind, int pos | parameterFlow(p, ap, ap0, c, config) and c = getNodeEnclosingCallable(ret) and @@ -2606,7 +2613,7 @@ private predicate nodeMayUseSummary(Node n, AccessPathApprox apa, Configuration private newtype TSummaryCtx = TSummaryCtxNone() or - TSummaryCtxSome(ParameterNode p, AccessPath ap) { + TSummaryCtxSome(ParameterNodeExt p, AccessPath ap) { Stage4::parameterMayFlowThrough(p, _, ap.getApprox(), _) } @@ -2627,7 +2634,7 @@ private class SummaryCtxNone extends SummaryCtx, TSummaryCtxNone { /** A summary context from which a flow summary can be generated. */ private class SummaryCtxSome extends SummaryCtx, TSummaryCtxSome { - private ParameterNode p; + private ParameterNodeExt p; private AccessPath ap; SummaryCtxSome() { this = TSummaryCtxSome(p, ap) } @@ -2758,7 +2765,7 @@ private newtype TPathNode = config.isSource(node) and cc instanceof CallContextAny and sc instanceof SummaryCtxNone and - ap = TAccessPathNil(getNodeType(node)) + ap = TAccessPathNil(getNodeDataFlowType(node)) or // ... or a step from an existing PathNode to another node. exists(PathNodeMid mid | @@ -2979,7 +2986,7 @@ class PathNode extends TPathNode { Configuration getConfiguration() { none() } private predicate isHidden() { - nodeIsHidden(this.getNode()) and + hiddenNode(this.getNode()) and not this.isSource() and not this instanceof PathNodeSink } @@ -3148,7 +3155,7 @@ private predicate pathStep(PathNodeMid mid, Node node, CallContext cc, SummaryCt cc instanceof CallContextAny and sc instanceof SummaryCtxNone and mid.getAp() instanceof AccessPathNil and - ap = TAccessPathNil(getNodeType(node)) + ap = TAccessPathNil(getNodeDataFlowType(node)) or exists(TypedContent tc | pathStoreStep(mid, node, ap.pop(tc), tc, cc)) and sc = mid.getSummaryCtx() @@ -3235,7 +3242,7 @@ pragma[noinline] private predicate pathIntoArg( PathNodeMid mid, int i, CallContext cc, DataFlowCall call, AccessPath ap, AccessPathApprox apa ) { - exists(ArgumentNode arg | + exists(ArgumentNodeExt arg | arg = mid.getNode() and cc = mid.getCallContext() and arg.argumentOf(call, i) and @@ -3248,7 +3255,7 @@ pragma[noinline] private predicate parameterCand( DataFlowCallable callable, int i, AccessPathApprox apa, Configuration config ) { - exists(ParameterNode p | + exists(ParameterNodeExt p | Stage4::revFlow(p, _, _, apa, config) and p.isParameterOf(callable, i) ) @@ -3272,7 +3279,7 @@ private predicate pathIntoCallable0( * respectively. */ private predicate pathIntoCallable( - PathNodeMid mid, ParameterNode p, CallContext outercc, CallContextCall innercc, SummaryCtx sc, + PathNodeMid mid, ParameterNodeExt p, CallContext outercc, CallContextCall innercc, SummaryCtx sc, DataFlowCall call ) { exists(int i, DataFlowCallable callable, AccessPath ap | @@ -3568,7 +3575,7 @@ private module FlowExploration { private newtype TSummaryCtx1 = TSummaryCtx1None() or - TSummaryCtx1Param(ParameterNode p) + TSummaryCtx1Param(ParameterNodeExt p) private newtype TSummaryCtx2 = TSummaryCtx2None() or @@ -3591,7 +3598,7 @@ private module FlowExploration { cc instanceof CallContextAny and sc1 = TSummaryCtx1None() and sc2 = TSummaryCtx2None() and - ap = TPartialNil(getNodeType(node)) and + ap = TPartialNil(getNodeDataFlowType(node)) and not fullBarrier(node, config) and exists(config.explorationLimit()) or @@ -3611,7 +3618,7 @@ private module FlowExploration { or exists(PartialPathNodeRev mid | revPartialPathStep(mid, node, sc1, sc2, ap, config) and - not clearsContent(node, ap.getHead()) and + not clearsContentCached(node, ap.getHead()) and not fullBarrier(node, config) and distSink(getNodeEnclosingCallable(node), config) <= config.explorationLimit() ) @@ -3625,9 +3632,9 @@ private module FlowExploration { exists(PartialPathNodeFwd mid | partialPathStep(mid, node, cc, sc1, sc2, ap, config) and not fullBarrier(node, config) and - not clearsContent(node, ap.getHead().getContent()) and + not clearsContentCached(node, ap.getHead().getContent()) and if node instanceof CastingNode - then compatibleTypes(getNodeType(node), ap.getType()) + then compatibleTypes(getNodeDataFlowType(node), ap.getType()) else any() ) } @@ -3783,7 +3790,7 @@ private module FlowExploration { PartialPathNodeFwd mid, Node node, CallContext cc, TSummaryCtx1 sc1, TSummaryCtx2 sc2, PartialAccessPath ap, Configuration config ) { - not isUnreachableInCall(node, cc.(CallContextSpecificCall).getCall()) and + not isUnreachableInCallCached(node, cc.(CallContextSpecificCall).getCall()) and ( localFlowStep(mid.getNode(), node, config) and cc = mid.getCallContext() and @@ -3797,7 +3804,7 @@ private module FlowExploration { sc1 = mid.getSummaryCtx1() and sc2 = mid.getSummaryCtx2() and mid.getAp() instanceof PartialAccessPathNil and - ap = TPartialNil(getNodeType(node)) and + ap = TPartialNil(getNodeDataFlowType(node)) and config = mid.getConfiguration() ) or @@ -3813,7 +3820,7 @@ private module FlowExploration { sc1 = TSummaryCtx1None() and sc2 = TSummaryCtx2None() and mid.getAp() instanceof PartialAccessPathNil and - ap = TPartialNil(getNodeType(node)) and + ap = TPartialNil(getNodeDataFlowType(node)) and config = mid.getConfiguration() or partialPathStoreStep(mid, _, _, node, ap) and @@ -3827,7 +3834,7 @@ private module FlowExploration { sc1 = mid.getSummaryCtx1() and sc2 = mid.getSummaryCtx2() and apConsFwd(ap, tc, ap0, config) and - compatibleTypes(ap.getType(), getNodeType(node)) + compatibleTypes(ap.getType(), getNodeDataFlowType(node)) ) or partialPathIntoCallable(mid, node, _, cc, sc1, sc2, _, ap, config) @@ -3924,7 +3931,7 @@ private module FlowExploration { PartialPathNodeFwd mid, int i, CallContext cc, DataFlowCall call, PartialAccessPath ap, Configuration config ) { - exists(ArgumentNode arg | + exists(ArgumentNodeExt arg | arg = mid.getNode() and cc = mid.getCallContext() and arg.argumentOf(call, i) and @@ -3943,7 +3950,7 @@ private module FlowExploration { } private predicate partialPathIntoCallable( - PartialPathNodeFwd mid, ParameterNode p, CallContext outercc, CallContextCall innercc, + PartialPathNodeFwd mid, ParameterNodeExt p, CallContext outercc, CallContextCall innercc, TSummaryCtx1 sc1, TSummaryCtx2 sc2, DataFlowCall call, PartialAccessPath ap, Configuration config ) { @@ -3980,7 +3987,7 @@ private module FlowExploration { DataFlowCall call, PartialPathNodeFwd mid, ReturnKindExt kind, CallContext cc, PartialAccessPath ap, Configuration config ) { - exists(ParameterNode p, CallContext innercc, TSummaryCtx1 sc1, TSummaryCtx2 sc2 | + exists(ParameterNodeExt p, CallContext innercc, TSummaryCtx1 sc1, TSummaryCtx2 sc2 | partialPathIntoCallable(mid, p, cc, innercc, sc1, sc2, call, _, config) and paramFlowsThroughInPartialPath(kind, innercc, sc1, sc2, ap, config) ) @@ -4037,7 +4044,7 @@ private module FlowExploration { apConsRev(ap, c, ap0, config) ) or - exists(ParameterNode p | + exists(ParameterNodeExt p | mid.getNode() = p and viableParamArg(_, p, node) and sc1 = mid.getSummaryCtx1() and @@ -4115,7 +4122,7 @@ private module FlowExploration { int pos, TRevSummaryCtx1Some sc1, TRevSummaryCtx2Some sc2, RevPartialAccessPath ap, Configuration config ) { - exists(PartialPathNodeRev mid, ParameterNode p | + exists(PartialPathNodeRev mid, ParameterNodeExt p | mid.getNode() = p and p.isParameterOf(_, pos) and sc1 = mid.getSummaryCtx1() and @@ -4138,7 +4145,7 @@ private module FlowExploration { pragma[nomagic] private predicate revPartialPathThroughCallable( - PartialPathNodeRev mid, ArgumentNode node, RevPartialAccessPath ap, Configuration config + PartialPathNodeRev mid, ArgumentNodeExt node, RevPartialAccessPath ap, Configuration config ) { exists(DataFlowCall call, int pos | revPartialPathThroughCallable0(call, mid, pos, ap, config) and 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 9498e51e7e6..cf83530bcbd 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 @@ -211,10 +211,7 @@ private predicate fullBarrier(Node node, Configuration config) { * Holds if data can flow in one local step from `node1` to `node2`. */ private predicate localFlowStep(Node node1, Node node2, Configuration config) { - ( - simpleLocalFlowStep(node1, node2) or - reverseStepThroughInputOutputAlias(node1, node2) - ) and + simpleLocalFlowStepExt(node1, node2) and not outBarrier(node1, config) and not inBarrier(node2, config) and not fullBarrier(node1, config) and @@ -237,7 +234,7 @@ private predicate additionalLocalFlowStep(Node node1, Node node2, Configuration * Holds if data can flow from `node1` to `node2` in a way that discards call contexts. */ private predicate jumpStep(Node node1, Node node2, Configuration config) { - jumpStep(node1, node2) and + jumpStepCached(node1, node2) and not outBarrier(node1, config) and not inBarrier(node2, config) and not fullBarrier(node1, config) and @@ -388,7 +385,7 @@ private module Stage1 { */ pragma[nomagic] private predicate fwdFlowIsEntered(DataFlowCall call, Cc cc, Configuration config) { - exists(ArgumentNode arg | + exists(ArgumentNodeExt arg | fwdFlow(arg, cc, config) and viableParamArg(call, _, arg) ) @@ -515,7 +512,7 @@ private module Stage1 { pragma[nomagic] predicate viableParamArgNodeCandFwd1( - DataFlowCall call, ParameterNode p, ArgumentNode arg, Configuration config + DataFlowCall call, ParameterNodeExt p, ArgumentNodeExt arg, Configuration config ) { viableParamArg(call, p, arg) and fwdFlow(arg, config) @@ -523,16 +520,16 @@ private module Stage1 { pragma[nomagic] private predicate revFlowIn( - DataFlowCall call, ArgumentNode arg, boolean toReturn, Configuration config + DataFlowCall call, ArgumentNodeExt arg, boolean toReturn, Configuration config ) { - exists(ParameterNode p | + exists(ParameterNodeExt p | revFlow(p, toReturn, config) and viableParamArgNodeCandFwd1(call, p, arg, config) ) } pragma[nomagic] - private predicate revFlowInToReturn(DataFlowCall call, ArgumentNode arg, Configuration config) { + private predicate revFlowInToReturn(DataFlowCall call, ArgumentNodeExt arg, Configuration config) { revFlowIn(call, arg, true, config) } @@ -597,7 +594,9 @@ private module Stage1 { * Holds if flow may enter through `p` and reach a return node making `p` a * candidate for the origin of a summary. */ - predicate parameterMayFlowThrough(ParameterNode p, DataFlowCallable c, Ap ap, Configuration config) { + predicate parameterMayFlowThrough( + ParameterNodeExt p, DataFlowCallable c, Ap ap, Configuration config + ) { exists(ReturnKindExt kind | throughFlowNodeCand(p, config) and returnFlowCallableNodeCand(c, kind, config) and @@ -663,7 +662,7 @@ private predicate flowOutOfCallNodeCand1( pragma[nomagic] private predicate viableParamArgNodeCand1( - DataFlowCall call, ParameterNode p, ArgumentNode arg, Configuration config + DataFlowCall call, ParameterNodeExt p, ArgumentNodeExt arg, Configuration config ) { Stage1::viableParamArgNodeCandFwd1(call, p, arg, config) and Stage1::revFlow(arg, config) @@ -675,7 +674,7 @@ private predicate viableParamArgNodeCand1( */ pragma[nomagic] private predicate flowIntoCallNodeCand1( - DataFlowCall call, ArgumentNode arg, ParameterNode p, Configuration config + DataFlowCall call, ArgumentNodeExt arg, ParameterNodeExt p, Configuration config ) { viableParamArgNodeCand1(call, p, arg, config) and Stage1::revFlow(p, config) and @@ -735,7 +734,7 @@ private predicate flowOutOfCallNodeCand1( */ pragma[nomagic] private predicate flowIntoCallNodeCand1( - DataFlowCall call, ArgumentNode arg, ParameterNode p, boolean allowsFieldFlow, + DataFlowCall call, ArgumentNodeExt arg, ParameterNodeExt p, boolean allowsFieldFlow, Configuration config ) { flowIntoCallNodeCand1(call, arg, p, config) and @@ -944,10 +943,10 @@ private module Stage2 { pragma[nomagic] private predicate fwdFlowIn( - DataFlowCall call, ParameterNode p, Cc outercc, Cc innercc, ApOption argAp, Ap ap, + DataFlowCall call, ParameterNodeExt p, Cc outercc, Cc innercc, ApOption argAp, Ap ap, Configuration config ) { - exists(ArgumentNode arg, boolean allowsFieldFlow | + exists(ArgumentNodeExt arg, boolean allowsFieldFlow | fwdFlow(arg, outercc, argAp, ap, config) and flowIntoCall(call, arg, p, allowsFieldFlow, config) and innercc = getCallContextCall(call, getNodeEnclosingCallable(p), outercc) @@ -992,7 +991,7 @@ private module Stage2 { private predicate fwdFlowIsEntered( DataFlowCall call, Cc cc, ApOption argAp, Ap ap, Configuration config ) { - exists(ParameterNode p | + exists(ParameterNodeExt p | fwdFlowIn(call, p, cc, _, argAp, ap, config) and PrevStage::parameterMayFlowThrough(p, _, getApprox(ap), config) ) @@ -1133,10 +1132,10 @@ private module Stage2 { pragma[nomagic] private predicate revFlowIn( - DataFlowCall call, ArgumentNode arg, boolean toReturn, ApOption returnAp, Ap ap, + DataFlowCall call, ArgumentNodeExt arg, boolean toReturn, ApOption returnAp, Ap ap, Configuration config ) { - exists(ParameterNode p, boolean allowsFieldFlow | + exists(ParameterNodeExt p, boolean allowsFieldFlow | revFlow(p, toReturn, returnAp, ap, config) and flowIntoCall(call, arg, p, allowsFieldFlow, config) | @@ -1146,7 +1145,7 @@ private module Stage2 { pragma[nomagic] private predicate revFlowInToReturn( - DataFlowCall call, ArgumentNode arg, Ap returnAp, Ap ap, Configuration config + DataFlowCall call, ArgumentNodeExt arg, Ap returnAp, Ap ap, Configuration config ) { revFlowIn(call, arg, true, apSome(returnAp), ap, config) } @@ -1199,13 +1198,15 @@ private module Stage2 { pragma[noinline] private predicate parameterFlow( - ParameterNode p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config + ParameterNodeExt p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config ) { revFlow(p, true, apSome(ap0), ap, config) and c = getNodeEnclosingCallable(p) } - predicate parameterMayFlowThrough(ParameterNode p, DataFlowCallable c, Ap ap, Configuration config) { + predicate parameterMayFlowThrough( + ParameterNodeExt p, DataFlowCallable c, Ap ap, Configuration config + ) { exists(ReturnNodeExt ret, Ap ap0, ReturnKindExt kind, int pos | parameterFlow(p, ap, ap0, c, config) and c = getNodeEnclosingCallable(ret) and @@ -1245,7 +1246,7 @@ private predicate flowOutOfCallNodeCand2( pragma[nomagic] private predicate flowIntoCallNodeCand2( - DataFlowCall call, ArgumentNode node1, ParameterNode node2, boolean allowsFieldFlow, + DataFlowCall call, ArgumentNodeExt node1, ParameterNodeExt node2, boolean allowsFieldFlow, Configuration config ) { flowIntoCallNodeCand1(call, node1, node2, allowsFieldFlow, config) and @@ -1260,8 +1261,8 @@ private module LocalFlowBigStep { */ private class FlowCheckNode extends Node { FlowCheckNode() { - this instanceof CastNode or - clearsContent(this, _) + castNode(this) or + clearsContentCached(this, _) } } @@ -1275,7 +1276,7 @@ private module LocalFlowBigStep { config.isSource(node) or jumpStep(_, node, config) or additionalJumpStep(_, node, config) or - node instanceof ParameterNode or + node instanceof ParameterNodeExt or node instanceof OutNodeExt or store(_, _, node, _) or read(_, _, node) or @@ -1321,21 +1322,21 @@ private module LocalFlowBigStep { Node node1, Node node2, boolean preservesValue, DataFlowType t, Configuration config, LocalCallContext cc ) { - not isUnreachableInCall(node2, cc.(LocalCallContextSpecificCall).getCall()) and + not isUnreachableInCallCached(node2, cc.(LocalCallContextSpecificCall).getCall()) and ( localFlowEntry(node1, pragma[only_bind_into](config)) and ( localFlowStepNodeCand1(node1, node2, config) and preservesValue = true and - t = getNodeType(node1) + t = getNodeDataFlowType(node1) or additionalLocalFlowStepNodeCand2(node1, node2, config) and preservesValue = false and - t = getNodeType(node2) + t = getNodeDataFlowType(node2) ) and node1 != node2 and cc.relevantFor(getNodeEnclosingCallable(node1)) and - not isUnreachableInCall(node1, cc.(LocalCallContextSpecificCall).getCall()) and + not isUnreachableInCallCached(node1, cc.(LocalCallContextSpecificCall).getCall()) and Stage2::revFlow(node2, pragma[only_bind_into](config)) or exists(Node mid | @@ -1350,7 +1351,7 @@ private module LocalFlowBigStep { additionalLocalFlowStepNodeCand2(mid, node2, config) and not mid instanceof FlowCheckNode and preservesValue = false and - t = getNodeType(node2) and + t = getNodeDataFlowType(node2) and Stage2::revFlow(node2, pragma[only_bind_into](config)) ) ) @@ -1384,7 +1385,7 @@ private module Stage3 { private ApApprox getApprox(Ap ap) { result = ap.toBoolNonEmpty() } private ApNil getApNil(Node node) { - PrevStage::revFlow(node, _) and result = TFrontNil(getNodeType(node)) + PrevStage::revFlow(node, _) and result = TFrontNil(getNodeDataFlowType(node)) } bindingset[tc, tail] @@ -1443,7 +1444,9 @@ private module Stage3 { bindingset[node, ap] private predicate filter(Node node, Ap ap) { not ap.isClearedAt(node) and - if node instanceof CastingNode then compatibleTypes(getNodeType(node), ap.getType()) else any() + if node instanceof CastingNode + then compatibleTypes(getNodeDataFlowType(node), ap.getType()) + else any() } bindingset[ap, contentType] @@ -1583,10 +1586,10 @@ private module Stage3 { pragma[nomagic] private predicate fwdFlowIn( - DataFlowCall call, ParameterNode p, Cc outercc, Cc innercc, ApOption argAp, Ap ap, + DataFlowCall call, ParameterNodeExt p, Cc outercc, Cc innercc, ApOption argAp, Ap ap, Configuration config ) { - exists(ArgumentNode arg, boolean allowsFieldFlow | + exists(ArgumentNodeExt arg, boolean allowsFieldFlow | fwdFlow(arg, outercc, argAp, ap, config) and flowIntoCall(call, arg, p, allowsFieldFlow, config) and innercc = getCallContextCall(call, getNodeEnclosingCallable(p), outercc) @@ -1631,7 +1634,7 @@ private module Stage3 { private predicate fwdFlowIsEntered( DataFlowCall call, Cc cc, ApOption argAp, Ap ap, Configuration config ) { - exists(ParameterNode p | + exists(ParameterNodeExt p | fwdFlowIn(call, p, cc, _, argAp, ap, config) and PrevStage::parameterMayFlowThrough(p, _, unbindApa(getApprox(ap)), config) ) @@ -1772,10 +1775,10 @@ private module Stage3 { pragma[nomagic] private predicate revFlowIn( - DataFlowCall call, ArgumentNode arg, boolean toReturn, ApOption returnAp, Ap ap, + DataFlowCall call, ArgumentNodeExt arg, boolean toReturn, ApOption returnAp, Ap ap, Configuration config ) { - exists(ParameterNode p, boolean allowsFieldFlow | + exists(ParameterNodeExt p, boolean allowsFieldFlow | revFlow(p, toReturn, returnAp, ap, config) and flowIntoCall(call, arg, p, allowsFieldFlow, config) | @@ -1785,7 +1788,7 @@ private module Stage3 { pragma[nomagic] private predicate revFlowInToReturn( - DataFlowCall call, ArgumentNode arg, Ap returnAp, Ap ap, Configuration config + DataFlowCall call, ArgumentNodeExt arg, Ap returnAp, Ap ap, Configuration config ) { revFlowIn(call, arg, true, apSome(returnAp), ap, config) } @@ -1838,13 +1841,15 @@ private module Stage3 { pragma[noinline] private predicate parameterFlow( - ParameterNode p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config + ParameterNodeExt p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config ) { revFlow(p, true, apSome(ap0), ap, config) and c = getNodeEnclosingCallable(p) } - predicate parameterMayFlowThrough(ParameterNode p, DataFlowCallable c, Ap ap, Configuration config) { + predicate parameterMayFlowThrough( + ParameterNodeExt p, DataFlowCallable c, Ap ap, Configuration config + ) { exists(ReturnNodeExt ret, Ap ap0, ReturnKindExt kind, int pos | parameterFlow(p, ap, ap0, c, config) and c = getNodeEnclosingCallable(ret) and @@ -2088,7 +2093,7 @@ private module Stage4 { private ApApprox getApprox(Ap ap) { result = ap.getFront() } private ApNil getApNil(Node node) { - PrevStage::revFlow(node, _) and result = TNil(getNodeType(node)) + PrevStage::revFlow(node, _) and result = TNil(getNodeDataFlowType(node)) } bindingset[tc, tail] @@ -2155,7 +2160,7 @@ private module Stage4 { pragma[nomagic] private predicate flowIntoCall( - DataFlowCall call, ArgumentNode node1, ParameterNode node2, boolean allowsFieldFlow, + DataFlowCall call, ArgumentNodeExt node1, ParameterNodeExt node2, boolean allowsFieldFlow, Configuration config ) { flowIntoCallNodeCand2(call, node1, node2, allowsFieldFlow, config) and @@ -2300,10 +2305,10 @@ private module Stage4 { pragma[nomagic] private predicate fwdFlowIn( - DataFlowCall call, ParameterNode p, Cc outercc, Cc innercc, ApOption argAp, Ap ap, + DataFlowCall call, ParameterNodeExt p, Cc outercc, Cc innercc, ApOption argAp, Ap ap, Configuration config ) { - exists(ArgumentNode arg, boolean allowsFieldFlow | + exists(ArgumentNodeExt arg, boolean allowsFieldFlow | fwdFlow(arg, outercc, argAp, ap, config) and flowIntoCall(call, arg, p, allowsFieldFlow, config) and innercc = getCallContextCall(call, getNodeEnclosingCallable(p), outercc) @@ -2348,7 +2353,7 @@ private module Stage4 { private predicate fwdFlowIsEntered( DataFlowCall call, Cc cc, ApOption argAp, Ap ap, Configuration config ) { - exists(ParameterNode p | + exists(ParameterNodeExt p | fwdFlowIn(call, p, cc, _, argAp, ap, config) and PrevStage::parameterMayFlowThrough(p, _, unbindApa(getApprox(ap)), config) ) @@ -2489,10 +2494,10 @@ private module Stage4 { pragma[nomagic] private predicate revFlowIn( - DataFlowCall call, ArgumentNode arg, boolean toReturn, ApOption returnAp, Ap ap, + DataFlowCall call, ArgumentNodeExt arg, boolean toReturn, ApOption returnAp, Ap ap, Configuration config ) { - exists(ParameterNode p, boolean allowsFieldFlow | + exists(ParameterNodeExt p, boolean allowsFieldFlow | revFlow(p, toReturn, returnAp, ap, config) and flowIntoCall(call, arg, p, allowsFieldFlow, config) | @@ -2502,7 +2507,7 @@ private module Stage4 { pragma[nomagic] private predicate revFlowInToReturn( - DataFlowCall call, ArgumentNode arg, Ap returnAp, Ap ap, Configuration config + DataFlowCall call, ArgumentNodeExt arg, Ap returnAp, Ap ap, Configuration config ) { revFlowIn(call, arg, true, apSome(returnAp), ap, config) } @@ -2555,13 +2560,15 @@ private module Stage4 { pragma[noinline] private predicate parameterFlow( - ParameterNode p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config + ParameterNodeExt p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config ) { revFlow(p, true, apSome(ap0), ap, config) and c = getNodeEnclosingCallable(p) } - predicate parameterMayFlowThrough(ParameterNode p, DataFlowCallable c, Ap ap, Configuration config) { + predicate parameterMayFlowThrough( + ParameterNodeExt p, DataFlowCallable c, Ap ap, Configuration config + ) { exists(ReturnNodeExt ret, Ap ap0, ReturnKindExt kind, int pos | parameterFlow(p, ap, ap0, c, config) and c = getNodeEnclosingCallable(ret) and @@ -2606,7 +2613,7 @@ private predicate nodeMayUseSummary(Node n, AccessPathApprox apa, Configuration private newtype TSummaryCtx = TSummaryCtxNone() or - TSummaryCtxSome(ParameterNode p, AccessPath ap) { + TSummaryCtxSome(ParameterNodeExt p, AccessPath ap) { Stage4::parameterMayFlowThrough(p, _, ap.getApprox(), _) } @@ -2627,7 +2634,7 @@ private class SummaryCtxNone extends SummaryCtx, TSummaryCtxNone { /** A summary context from which a flow summary can be generated. */ private class SummaryCtxSome extends SummaryCtx, TSummaryCtxSome { - private ParameterNode p; + private ParameterNodeExt p; private AccessPath ap; SummaryCtxSome() { this = TSummaryCtxSome(p, ap) } @@ -2758,7 +2765,7 @@ private newtype TPathNode = config.isSource(node) and cc instanceof CallContextAny and sc instanceof SummaryCtxNone and - ap = TAccessPathNil(getNodeType(node)) + ap = TAccessPathNil(getNodeDataFlowType(node)) or // ... or a step from an existing PathNode to another node. exists(PathNodeMid mid | @@ -2979,7 +2986,7 @@ class PathNode extends TPathNode { Configuration getConfiguration() { none() } private predicate isHidden() { - nodeIsHidden(this.getNode()) and + hiddenNode(this.getNode()) and not this.isSource() and not this instanceof PathNodeSink } @@ -3148,7 +3155,7 @@ private predicate pathStep(PathNodeMid mid, Node node, CallContext cc, SummaryCt cc instanceof CallContextAny and sc instanceof SummaryCtxNone and mid.getAp() instanceof AccessPathNil and - ap = TAccessPathNil(getNodeType(node)) + ap = TAccessPathNil(getNodeDataFlowType(node)) or exists(TypedContent tc | pathStoreStep(mid, node, ap.pop(tc), tc, cc)) and sc = mid.getSummaryCtx() @@ -3235,7 +3242,7 @@ pragma[noinline] private predicate pathIntoArg( PathNodeMid mid, int i, CallContext cc, DataFlowCall call, AccessPath ap, AccessPathApprox apa ) { - exists(ArgumentNode arg | + exists(ArgumentNodeExt arg | arg = mid.getNode() and cc = mid.getCallContext() and arg.argumentOf(call, i) and @@ -3248,7 +3255,7 @@ pragma[noinline] private predicate parameterCand( DataFlowCallable callable, int i, AccessPathApprox apa, Configuration config ) { - exists(ParameterNode p | + exists(ParameterNodeExt p | Stage4::revFlow(p, _, _, apa, config) and p.isParameterOf(callable, i) ) @@ -3272,7 +3279,7 @@ private predicate pathIntoCallable0( * respectively. */ private predicate pathIntoCallable( - PathNodeMid mid, ParameterNode p, CallContext outercc, CallContextCall innercc, SummaryCtx sc, + PathNodeMid mid, ParameterNodeExt p, CallContext outercc, CallContextCall innercc, SummaryCtx sc, DataFlowCall call ) { exists(int i, DataFlowCallable callable, AccessPath ap | @@ -3568,7 +3575,7 @@ private module FlowExploration { private newtype TSummaryCtx1 = TSummaryCtx1None() or - TSummaryCtx1Param(ParameterNode p) + TSummaryCtx1Param(ParameterNodeExt p) private newtype TSummaryCtx2 = TSummaryCtx2None() or @@ -3591,7 +3598,7 @@ private module FlowExploration { cc instanceof CallContextAny and sc1 = TSummaryCtx1None() and sc2 = TSummaryCtx2None() and - ap = TPartialNil(getNodeType(node)) and + ap = TPartialNil(getNodeDataFlowType(node)) and not fullBarrier(node, config) and exists(config.explorationLimit()) or @@ -3611,7 +3618,7 @@ private module FlowExploration { or exists(PartialPathNodeRev mid | revPartialPathStep(mid, node, sc1, sc2, ap, config) and - not clearsContent(node, ap.getHead()) and + not clearsContentCached(node, ap.getHead()) and not fullBarrier(node, config) and distSink(getNodeEnclosingCallable(node), config) <= config.explorationLimit() ) @@ -3625,9 +3632,9 @@ private module FlowExploration { exists(PartialPathNodeFwd mid | partialPathStep(mid, node, cc, sc1, sc2, ap, config) and not fullBarrier(node, config) and - not clearsContent(node, ap.getHead().getContent()) and + not clearsContentCached(node, ap.getHead().getContent()) and if node instanceof CastingNode - then compatibleTypes(getNodeType(node), ap.getType()) + then compatibleTypes(getNodeDataFlowType(node), ap.getType()) else any() ) } @@ -3783,7 +3790,7 @@ private module FlowExploration { PartialPathNodeFwd mid, Node node, CallContext cc, TSummaryCtx1 sc1, TSummaryCtx2 sc2, PartialAccessPath ap, Configuration config ) { - not isUnreachableInCall(node, cc.(CallContextSpecificCall).getCall()) and + not isUnreachableInCallCached(node, cc.(CallContextSpecificCall).getCall()) and ( localFlowStep(mid.getNode(), node, config) and cc = mid.getCallContext() and @@ -3797,7 +3804,7 @@ private module FlowExploration { sc1 = mid.getSummaryCtx1() and sc2 = mid.getSummaryCtx2() and mid.getAp() instanceof PartialAccessPathNil and - ap = TPartialNil(getNodeType(node)) and + ap = TPartialNil(getNodeDataFlowType(node)) and config = mid.getConfiguration() ) or @@ -3813,7 +3820,7 @@ private module FlowExploration { sc1 = TSummaryCtx1None() and sc2 = TSummaryCtx2None() and mid.getAp() instanceof PartialAccessPathNil and - ap = TPartialNil(getNodeType(node)) and + ap = TPartialNil(getNodeDataFlowType(node)) and config = mid.getConfiguration() or partialPathStoreStep(mid, _, _, node, ap) and @@ -3827,7 +3834,7 @@ private module FlowExploration { sc1 = mid.getSummaryCtx1() and sc2 = mid.getSummaryCtx2() and apConsFwd(ap, tc, ap0, config) and - compatibleTypes(ap.getType(), getNodeType(node)) + compatibleTypes(ap.getType(), getNodeDataFlowType(node)) ) or partialPathIntoCallable(mid, node, _, cc, sc1, sc2, _, ap, config) @@ -3924,7 +3931,7 @@ private module FlowExploration { PartialPathNodeFwd mid, int i, CallContext cc, DataFlowCall call, PartialAccessPath ap, Configuration config ) { - exists(ArgumentNode arg | + exists(ArgumentNodeExt arg | arg = mid.getNode() and cc = mid.getCallContext() and arg.argumentOf(call, i) and @@ -3943,7 +3950,7 @@ private module FlowExploration { } private predicate partialPathIntoCallable( - PartialPathNodeFwd mid, ParameterNode p, CallContext outercc, CallContextCall innercc, + PartialPathNodeFwd mid, ParameterNodeExt p, CallContext outercc, CallContextCall innercc, TSummaryCtx1 sc1, TSummaryCtx2 sc2, DataFlowCall call, PartialAccessPath ap, Configuration config ) { @@ -3980,7 +3987,7 @@ private module FlowExploration { DataFlowCall call, PartialPathNodeFwd mid, ReturnKindExt kind, CallContext cc, PartialAccessPath ap, Configuration config ) { - exists(ParameterNode p, CallContext innercc, TSummaryCtx1 sc1, TSummaryCtx2 sc2 | + exists(ParameterNodeExt p, CallContext innercc, TSummaryCtx1 sc1, TSummaryCtx2 sc2 | partialPathIntoCallable(mid, p, cc, innercc, sc1, sc2, call, _, config) and paramFlowsThroughInPartialPath(kind, innercc, sc1, sc2, ap, config) ) @@ -4037,7 +4044,7 @@ private module FlowExploration { apConsRev(ap, c, ap0, config) ) or - exists(ParameterNode p | + exists(ParameterNodeExt p | mid.getNode() = p and viableParamArg(_, p, node) and sc1 = mid.getSummaryCtx1() and @@ -4115,7 +4122,7 @@ private module FlowExploration { int pos, TRevSummaryCtx1Some sc1, TRevSummaryCtx2Some sc2, RevPartialAccessPath ap, Configuration config ) { - exists(PartialPathNodeRev mid, ParameterNode p | + exists(PartialPathNodeRev mid, ParameterNodeExt p | mid.getNode() = p and p.isParameterOf(_, pos) and sc1 = mid.getSummaryCtx1() and @@ -4138,7 +4145,7 @@ private module FlowExploration { pragma[nomagic] private predicate revPartialPathThroughCallable( - PartialPathNodeRev mid, ArgumentNode node, RevPartialAccessPath ap, Configuration config + PartialPathNodeRev mid, ArgumentNodeExt node, RevPartialAccessPath ap, Configuration config ) { exists(DataFlowCall call, int pos | revPartialPathThroughCallable0(call, mid, pos, ap, config) and 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 9498e51e7e6..cf83530bcbd 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 @@ -211,10 +211,7 @@ private predicate fullBarrier(Node node, Configuration config) { * Holds if data can flow in one local step from `node1` to `node2`. */ private predicate localFlowStep(Node node1, Node node2, Configuration config) { - ( - simpleLocalFlowStep(node1, node2) or - reverseStepThroughInputOutputAlias(node1, node2) - ) and + simpleLocalFlowStepExt(node1, node2) and not outBarrier(node1, config) and not inBarrier(node2, config) and not fullBarrier(node1, config) and @@ -237,7 +234,7 @@ private predicate additionalLocalFlowStep(Node node1, Node node2, Configuration * Holds if data can flow from `node1` to `node2` in a way that discards call contexts. */ private predicate jumpStep(Node node1, Node node2, Configuration config) { - jumpStep(node1, node2) and + jumpStepCached(node1, node2) and not outBarrier(node1, config) and not inBarrier(node2, config) and not fullBarrier(node1, config) and @@ -388,7 +385,7 @@ private module Stage1 { */ pragma[nomagic] private predicate fwdFlowIsEntered(DataFlowCall call, Cc cc, Configuration config) { - exists(ArgumentNode arg | + exists(ArgumentNodeExt arg | fwdFlow(arg, cc, config) and viableParamArg(call, _, arg) ) @@ -515,7 +512,7 @@ private module Stage1 { pragma[nomagic] predicate viableParamArgNodeCandFwd1( - DataFlowCall call, ParameterNode p, ArgumentNode arg, Configuration config + DataFlowCall call, ParameterNodeExt p, ArgumentNodeExt arg, Configuration config ) { viableParamArg(call, p, arg) and fwdFlow(arg, config) @@ -523,16 +520,16 @@ private module Stage1 { pragma[nomagic] private predicate revFlowIn( - DataFlowCall call, ArgumentNode arg, boolean toReturn, Configuration config + DataFlowCall call, ArgumentNodeExt arg, boolean toReturn, Configuration config ) { - exists(ParameterNode p | + exists(ParameterNodeExt p | revFlow(p, toReturn, config) and viableParamArgNodeCandFwd1(call, p, arg, config) ) } pragma[nomagic] - private predicate revFlowInToReturn(DataFlowCall call, ArgumentNode arg, Configuration config) { + private predicate revFlowInToReturn(DataFlowCall call, ArgumentNodeExt arg, Configuration config) { revFlowIn(call, arg, true, config) } @@ -597,7 +594,9 @@ private module Stage1 { * Holds if flow may enter through `p` and reach a return node making `p` a * candidate for the origin of a summary. */ - predicate parameterMayFlowThrough(ParameterNode p, DataFlowCallable c, Ap ap, Configuration config) { + predicate parameterMayFlowThrough( + ParameterNodeExt p, DataFlowCallable c, Ap ap, Configuration config + ) { exists(ReturnKindExt kind | throughFlowNodeCand(p, config) and returnFlowCallableNodeCand(c, kind, config) and @@ -663,7 +662,7 @@ private predicate flowOutOfCallNodeCand1( pragma[nomagic] private predicate viableParamArgNodeCand1( - DataFlowCall call, ParameterNode p, ArgumentNode arg, Configuration config + DataFlowCall call, ParameterNodeExt p, ArgumentNodeExt arg, Configuration config ) { Stage1::viableParamArgNodeCandFwd1(call, p, arg, config) and Stage1::revFlow(arg, config) @@ -675,7 +674,7 @@ private predicate viableParamArgNodeCand1( */ pragma[nomagic] private predicate flowIntoCallNodeCand1( - DataFlowCall call, ArgumentNode arg, ParameterNode p, Configuration config + DataFlowCall call, ArgumentNodeExt arg, ParameterNodeExt p, Configuration config ) { viableParamArgNodeCand1(call, p, arg, config) and Stage1::revFlow(p, config) and @@ -735,7 +734,7 @@ private predicate flowOutOfCallNodeCand1( */ pragma[nomagic] private predicate flowIntoCallNodeCand1( - DataFlowCall call, ArgumentNode arg, ParameterNode p, boolean allowsFieldFlow, + DataFlowCall call, ArgumentNodeExt arg, ParameterNodeExt p, boolean allowsFieldFlow, Configuration config ) { flowIntoCallNodeCand1(call, arg, p, config) and @@ -944,10 +943,10 @@ private module Stage2 { pragma[nomagic] private predicate fwdFlowIn( - DataFlowCall call, ParameterNode p, Cc outercc, Cc innercc, ApOption argAp, Ap ap, + DataFlowCall call, ParameterNodeExt p, Cc outercc, Cc innercc, ApOption argAp, Ap ap, Configuration config ) { - exists(ArgumentNode arg, boolean allowsFieldFlow | + exists(ArgumentNodeExt arg, boolean allowsFieldFlow | fwdFlow(arg, outercc, argAp, ap, config) and flowIntoCall(call, arg, p, allowsFieldFlow, config) and innercc = getCallContextCall(call, getNodeEnclosingCallable(p), outercc) @@ -992,7 +991,7 @@ private module Stage2 { private predicate fwdFlowIsEntered( DataFlowCall call, Cc cc, ApOption argAp, Ap ap, Configuration config ) { - exists(ParameterNode p | + exists(ParameterNodeExt p | fwdFlowIn(call, p, cc, _, argAp, ap, config) and PrevStage::parameterMayFlowThrough(p, _, getApprox(ap), config) ) @@ -1133,10 +1132,10 @@ private module Stage2 { pragma[nomagic] private predicate revFlowIn( - DataFlowCall call, ArgumentNode arg, boolean toReturn, ApOption returnAp, Ap ap, + DataFlowCall call, ArgumentNodeExt arg, boolean toReturn, ApOption returnAp, Ap ap, Configuration config ) { - exists(ParameterNode p, boolean allowsFieldFlow | + exists(ParameterNodeExt p, boolean allowsFieldFlow | revFlow(p, toReturn, returnAp, ap, config) and flowIntoCall(call, arg, p, allowsFieldFlow, config) | @@ -1146,7 +1145,7 @@ private module Stage2 { pragma[nomagic] private predicate revFlowInToReturn( - DataFlowCall call, ArgumentNode arg, Ap returnAp, Ap ap, Configuration config + DataFlowCall call, ArgumentNodeExt arg, Ap returnAp, Ap ap, Configuration config ) { revFlowIn(call, arg, true, apSome(returnAp), ap, config) } @@ -1199,13 +1198,15 @@ private module Stage2 { pragma[noinline] private predicate parameterFlow( - ParameterNode p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config + ParameterNodeExt p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config ) { revFlow(p, true, apSome(ap0), ap, config) and c = getNodeEnclosingCallable(p) } - predicate parameterMayFlowThrough(ParameterNode p, DataFlowCallable c, Ap ap, Configuration config) { + predicate parameterMayFlowThrough( + ParameterNodeExt p, DataFlowCallable c, Ap ap, Configuration config + ) { exists(ReturnNodeExt ret, Ap ap0, ReturnKindExt kind, int pos | parameterFlow(p, ap, ap0, c, config) and c = getNodeEnclosingCallable(ret) and @@ -1245,7 +1246,7 @@ private predicate flowOutOfCallNodeCand2( pragma[nomagic] private predicate flowIntoCallNodeCand2( - DataFlowCall call, ArgumentNode node1, ParameterNode node2, boolean allowsFieldFlow, + DataFlowCall call, ArgumentNodeExt node1, ParameterNodeExt node2, boolean allowsFieldFlow, Configuration config ) { flowIntoCallNodeCand1(call, node1, node2, allowsFieldFlow, config) and @@ -1260,8 +1261,8 @@ private module LocalFlowBigStep { */ private class FlowCheckNode extends Node { FlowCheckNode() { - this instanceof CastNode or - clearsContent(this, _) + castNode(this) or + clearsContentCached(this, _) } } @@ -1275,7 +1276,7 @@ private module LocalFlowBigStep { config.isSource(node) or jumpStep(_, node, config) or additionalJumpStep(_, node, config) or - node instanceof ParameterNode or + node instanceof ParameterNodeExt or node instanceof OutNodeExt or store(_, _, node, _) or read(_, _, node) or @@ -1321,21 +1322,21 @@ private module LocalFlowBigStep { Node node1, Node node2, boolean preservesValue, DataFlowType t, Configuration config, LocalCallContext cc ) { - not isUnreachableInCall(node2, cc.(LocalCallContextSpecificCall).getCall()) and + not isUnreachableInCallCached(node2, cc.(LocalCallContextSpecificCall).getCall()) and ( localFlowEntry(node1, pragma[only_bind_into](config)) and ( localFlowStepNodeCand1(node1, node2, config) and preservesValue = true and - t = getNodeType(node1) + t = getNodeDataFlowType(node1) or additionalLocalFlowStepNodeCand2(node1, node2, config) and preservesValue = false and - t = getNodeType(node2) + t = getNodeDataFlowType(node2) ) and node1 != node2 and cc.relevantFor(getNodeEnclosingCallable(node1)) and - not isUnreachableInCall(node1, cc.(LocalCallContextSpecificCall).getCall()) and + not isUnreachableInCallCached(node1, cc.(LocalCallContextSpecificCall).getCall()) and Stage2::revFlow(node2, pragma[only_bind_into](config)) or exists(Node mid | @@ -1350,7 +1351,7 @@ private module LocalFlowBigStep { additionalLocalFlowStepNodeCand2(mid, node2, config) and not mid instanceof FlowCheckNode and preservesValue = false and - t = getNodeType(node2) and + t = getNodeDataFlowType(node2) and Stage2::revFlow(node2, pragma[only_bind_into](config)) ) ) @@ -1384,7 +1385,7 @@ private module Stage3 { private ApApprox getApprox(Ap ap) { result = ap.toBoolNonEmpty() } private ApNil getApNil(Node node) { - PrevStage::revFlow(node, _) and result = TFrontNil(getNodeType(node)) + PrevStage::revFlow(node, _) and result = TFrontNil(getNodeDataFlowType(node)) } bindingset[tc, tail] @@ -1443,7 +1444,9 @@ private module Stage3 { bindingset[node, ap] private predicate filter(Node node, Ap ap) { not ap.isClearedAt(node) and - if node instanceof CastingNode then compatibleTypes(getNodeType(node), ap.getType()) else any() + if node instanceof CastingNode + then compatibleTypes(getNodeDataFlowType(node), ap.getType()) + else any() } bindingset[ap, contentType] @@ -1583,10 +1586,10 @@ private module Stage3 { pragma[nomagic] private predicate fwdFlowIn( - DataFlowCall call, ParameterNode p, Cc outercc, Cc innercc, ApOption argAp, Ap ap, + DataFlowCall call, ParameterNodeExt p, Cc outercc, Cc innercc, ApOption argAp, Ap ap, Configuration config ) { - exists(ArgumentNode arg, boolean allowsFieldFlow | + exists(ArgumentNodeExt arg, boolean allowsFieldFlow | fwdFlow(arg, outercc, argAp, ap, config) and flowIntoCall(call, arg, p, allowsFieldFlow, config) and innercc = getCallContextCall(call, getNodeEnclosingCallable(p), outercc) @@ -1631,7 +1634,7 @@ private module Stage3 { private predicate fwdFlowIsEntered( DataFlowCall call, Cc cc, ApOption argAp, Ap ap, Configuration config ) { - exists(ParameterNode p | + exists(ParameterNodeExt p | fwdFlowIn(call, p, cc, _, argAp, ap, config) and PrevStage::parameterMayFlowThrough(p, _, unbindApa(getApprox(ap)), config) ) @@ -1772,10 +1775,10 @@ private module Stage3 { pragma[nomagic] private predicate revFlowIn( - DataFlowCall call, ArgumentNode arg, boolean toReturn, ApOption returnAp, Ap ap, + DataFlowCall call, ArgumentNodeExt arg, boolean toReturn, ApOption returnAp, Ap ap, Configuration config ) { - exists(ParameterNode p, boolean allowsFieldFlow | + exists(ParameterNodeExt p, boolean allowsFieldFlow | revFlow(p, toReturn, returnAp, ap, config) and flowIntoCall(call, arg, p, allowsFieldFlow, config) | @@ -1785,7 +1788,7 @@ private module Stage3 { pragma[nomagic] private predicate revFlowInToReturn( - DataFlowCall call, ArgumentNode arg, Ap returnAp, Ap ap, Configuration config + DataFlowCall call, ArgumentNodeExt arg, Ap returnAp, Ap ap, Configuration config ) { revFlowIn(call, arg, true, apSome(returnAp), ap, config) } @@ -1838,13 +1841,15 @@ private module Stage3 { pragma[noinline] private predicate parameterFlow( - ParameterNode p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config + ParameterNodeExt p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config ) { revFlow(p, true, apSome(ap0), ap, config) and c = getNodeEnclosingCallable(p) } - predicate parameterMayFlowThrough(ParameterNode p, DataFlowCallable c, Ap ap, Configuration config) { + predicate parameterMayFlowThrough( + ParameterNodeExt p, DataFlowCallable c, Ap ap, Configuration config + ) { exists(ReturnNodeExt ret, Ap ap0, ReturnKindExt kind, int pos | parameterFlow(p, ap, ap0, c, config) and c = getNodeEnclosingCallable(ret) and @@ -2088,7 +2093,7 @@ private module Stage4 { private ApApprox getApprox(Ap ap) { result = ap.getFront() } private ApNil getApNil(Node node) { - PrevStage::revFlow(node, _) and result = TNil(getNodeType(node)) + PrevStage::revFlow(node, _) and result = TNil(getNodeDataFlowType(node)) } bindingset[tc, tail] @@ -2155,7 +2160,7 @@ private module Stage4 { pragma[nomagic] private predicate flowIntoCall( - DataFlowCall call, ArgumentNode node1, ParameterNode node2, boolean allowsFieldFlow, + DataFlowCall call, ArgumentNodeExt node1, ParameterNodeExt node2, boolean allowsFieldFlow, Configuration config ) { flowIntoCallNodeCand2(call, node1, node2, allowsFieldFlow, config) and @@ -2300,10 +2305,10 @@ private module Stage4 { pragma[nomagic] private predicate fwdFlowIn( - DataFlowCall call, ParameterNode p, Cc outercc, Cc innercc, ApOption argAp, Ap ap, + DataFlowCall call, ParameterNodeExt p, Cc outercc, Cc innercc, ApOption argAp, Ap ap, Configuration config ) { - exists(ArgumentNode arg, boolean allowsFieldFlow | + exists(ArgumentNodeExt arg, boolean allowsFieldFlow | fwdFlow(arg, outercc, argAp, ap, config) and flowIntoCall(call, arg, p, allowsFieldFlow, config) and innercc = getCallContextCall(call, getNodeEnclosingCallable(p), outercc) @@ -2348,7 +2353,7 @@ private module Stage4 { private predicate fwdFlowIsEntered( DataFlowCall call, Cc cc, ApOption argAp, Ap ap, Configuration config ) { - exists(ParameterNode p | + exists(ParameterNodeExt p | fwdFlowIn(call, p, cc, _, argAp, ap, config) and PrevStage::parameterMayFlowThrough(p, _, unbindApa(getApprox(ap)), config) ) @@ -2489,10 +2494,10 @@ private module Stage4 { pragma[nomagic] private predicate revFlowIn( - DataFlowCall call, ArgumentNode arg, boolean toReturn, ApOption returnAp, Ap ap, + DataFlowCall call, ArgumentNodeExt arg, boolean toReturn, ApOption returnAp, Ap ap, Configuration config ) { - exists(ParameterNode p, boolean allowsFieldFlow | + exists(ParameterNodeExt p, boolean allowsFieldFlow | revFlow(p, toReturn, returnAp, ap, config) and flowIntoCall(call, arg, p, allowsFieldFlow, config) | @@ -2502,7 +2507,7 @@ private module Stage4 { pragma[nomagic] private predicate revFlowInToReturn( - DataFlowCall call, ArgumentNode arg, Ap returnAp, Ap ap, Configuration config + DataFlowCall call, ArgumentNodeExt arg, Ap returnAp, Ap ap, Configuration config ) { revFlowIn(call, arg, true, apSome(returnAp), ap, config) } @@ -2555,13 +2560,15 @@ private module Stage4 { pragma[noinline] private predicate parameterFlow( - ParameterNode p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config + ParameterNodeExt p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config ) { revFlow(p, true, apSome(ap0), ap, config) and c = getNodeEnclosingCallable(p) } - predicate parameterMayFlowThrough(ParameterNode p, DataFlowCallable c, Ap ap, Configuration config) { + predicate parameterMayFlowThrough( + ParameterNodeExt p, DataFlowCallable c, Ap ap, Configuration config + ) { exists(ReturnNodeExt ret, Ap ap0, ReturnKindExt kind, int pos | parameterFlow(p, ap, ap0, c, config) and c = getNodeEnclosingCallable(ret) and @@ -2606,7 +2613,7 @@ private predicate nodeMayUseSummary(Node n, AccessPathApprox apa, Configuration private newtype TSummaryCtx = TSummaryCtxNone() or - TSummaryCtxSome(ParameterNode p, AccessPath ap) { + TSummaryCtxSome(ParameterNodeExt p, AccessPath ap) { Stage4::parameterMayFlowThrough(p, _, ap.getApprox(), _) } @@ -2627,7 +2634,7 @@ private class SummaryCtxNone extends SummaryCtx, TSummaryCtxNone { /** A summary context from which a flow summary can be generated. */ private class SummaryCtxSome extends SummaryCtx, TSummaryCtxSome { - private ParameterNode p; + private ParameterNodeExt p; private AccessPath ap; SummaryCtxSome() { this = TSummaryCtxSome(p, ap) } @@ -2758,7 +2765,7 @@ private newtype TPathNode = config.isSource(node) and cc instanceof CallContextAny and sc instanceof SummaryCtxNone and - ap = TAccessPathNil(getNodeType(node)) + ap = TAccessPathNil(getNodeDataFlowType(node)) or // ... or a step from an existing PathNode to another node. exists(PathNodeMid mid | @@ -2979,7 +2986,7 @@ class PathNode extends TPathNode { Configuration getConfiguration() { none() } private predicate isHidden() { - nodeIsHidden(this.getNode()) and + hiddenNode(this.getNode()) and not this.isSource() and not this instanceof PathNodeSink } @@ -3148,7 +3155,7 @@ private predicate pathStep(PathNodeMid mid, Node node, CallContext cc, SummaryCt cc instanceof CallContextAny and sc instanceof SummaryCtxNone and mid.getAp() instanceof AccessPathNil and - ap = TAccessPathNil(getNodeType(node)) + ap = TAccessPathNil(getNodeDataFlowType(node)) or exists(TypedContent tc | pathStoreStep(mid, node, ap.pop(tc), tc, cc)) and sc = mid.getSummaryCtx() @@ -3235,7 +3242,7 @@ pragma[noinline] private predicate pathIntoArg( PathNodeMid mid, int i, CallContext cc, DataFlowCall call, AccessPath ap, AccessPathApprox apa ) { - exists(ArgumentNode arg | + exists(ArgumentNodeExt arg | arg = mid.getNode() and cc = mid.getCallContext() and arg.argumentOf(call, i) and @@ -3248,7 +3255,7 @@ pragma[noinline] private predicate parameterCand( DataFlowCallable callable, int i, AccessPathApprox apa, Configuration config ) { - exists(ParameterNode p | + exists(ParameterNodeExt p | Stage4::revFlow(p, _, _, apa, config) and p.isParameterOf(callable, i) ) @@ -3272,7 +3279,7 @@ private predicate pathIntoCallable0( * respectively. */ private predicate pathIntoCallable( - PathNodeMid mid, ParameterNode p, CallContext outercc, CallContextCall innercc, SummaryCtx sc, + PathNodeMid mid, ParameterNodeExt p, CallContext outercc, CallContextCall innercc, SummaryCtx sc, DataFlowCall call ) { exists(int i, DataFlowCallable callable, AccessPath ap | @@ -3568,7 +3575,7 @@ private module FlowExploration { private newtype TSummaryCtx1 = TSummaryCtx1None() or - TSummaryCtx1Param(ParameterNode p) + TSummaryCtx1Param(ParameterNodeExt p) private newtype TSummaryCtx2 = TSummaryCtx2None() or @@ -3591,7 +3598,7 @@ private module FlowExploration { cc instanceof CallContextAny and sc1 = TSummaryCtx1None() and sc2 = TSummaryCtx2None() and - ap = TPartialNil(getNodeType(node)) and + ap = TPartialNil(getNodeDataFlowType(node)) and not fullBarrier(node, config) and exists(config.explorationLimit()) or @@ -3611,7 +3618,7 @@ private module FlowExploration { or exists(PartialPathNodeRev mid | revPartialPathStep(mid, node, sc1, sc2, ap, config) and - not clearsContent(node, ap.getHead()) and + not clearsContentCached(node, ap.getHead()) and not fullBarrier(node, config) and distSink(getNodeEnclosingCallable(node), config) <= config.explorationLimit() ) @@ -3625,9 +3632,9 @@ private module FlowExploration { exists(PartialPathNodeFwd mid | partialPathStep(mid, node, cc, sc1, sc2, ap, config) and not fullBarrier(node, config) and - not clearsContent(node, ap.getHead().getContent()) and + not clearsContentCached(node, ap.getHead().getContent()) and if node instanceof CastingNode - then compatibleTypes(getNodeType(node), ap.getType()) + then compatibleTypes(getNodeDataFlowType(node), ap.getType()) else any() ) } @@ -3783,7 +3790,7 @@ private module FlowExploration { PartialPathNodeFwd mid, Node node, CallContext cc, TSummaryCtx1 sc1, TSummaryCtx2 sc2, PartialAccessPath ap, Configuration config ) { - not isUnreachableInCall(node, cc.(CallContextSpecificCall).getCall()) and + not isUnreachableInCallCached(node, cc.(CallContextSpecificCall).getCall()) and ( localFlowStep(mid.getNode(), node, config) and cc = mid.getCallContext() and @@ -3797,7 +3804,7 @@ private module FlowExploration { sc1 = mid.getSummaryCtx1() and sc2 = mid.getSummaryCtx2() and mid.getAp() instanceof PartialAccessPathNil and - ap = TPartialNil(getNodeType(node)) and + ap = TPartialNil(getNodeDataFlowType(node)) and config = mid.getConfiguration() ) or @@ -3813,7 +3820,7 @@ private module FlowExploration { sc1 = TSummaryCtx1None() and sc2 = TSummaryCtx2None() and mid.getAp() instanceof PartialAccessPathNil and - ap = TPartialNil(getNodeType(node)) and + ap = TPartialNil(getNodeDataFlowType(node)) and config = mid.getConfiguration() or partialPathStoreStep(mid, _, _, node, ap) and @@ -3827,7 +3834,7 @@ private module FlowExploration { sc1 = mid.getSummaryCtx1() and sc2 = mid.getSummaryCtx2() and apConsFwd(ap, tc, ap0, config) and - compatibleTypes(ap.getType(), getNodeType(node)) + compatibleTypes(ap.getType(), getNodeDataFlowType(node)) ) or partialPathIntoCallable(mid, node, _, cc, sc1, sc2, _, ap, config) @@ -3924,7 +3931,7 @@ private module FlowExploration { PartialPathNodeFwd mid, int i, CallContext cc, DataFlowCall call, PartialAccessPath ap, Configuration config ) { - exists(ArgumentNode arg | + exists(ArgumentNodeExt arg | arg = mid.getNode() and cc = mid.getCallContext() and arg.argumentOf(call, i) and @@ -3943,7 +3950,7 @@ private module FlowExploration { } private predicate partialPathIntoCallable( - PartialPathNodeFwd mid, ParameterNode p, CallContext outercc, CallContextCall innercc, + PartialPathNodeFwd mid, ParameterNodeExt p, CallContext outercc, CallContextCall innercc, TSummaryCtx1 sc1, TSummaryCtx2 sc2, DataFlowCall call, PartialAccessPath ap, Configuration config ) { @@ -3980,7 +3987,7 @@ private module FlowExploration { DataFlowCall call, PartialPathNodeFwd mid, ReturnKindExt kind, CallContext cc, PartialAccessPath ap, Configuration config ) { - exists(ParameterNode p, CallContext innercc, TSummaryCtx1 sc1, TSummaryCtx2 sc2 | + exists(ParameterNodeExt p, CallContext innercc, TSummaryCtx1 sc1, TSummaryCtx2 sc2 | partialPathIntoCallable(mid, p, cc, innercc, sc1, sc2, call, _, config) and paramFlowsThroughInPartialPath(kind, innercc, sc1, sc2, ap, config) ) @@ -4037,7 +4044,7 @@ private module FlowExploration { apConsRev(ap, c, ap0, config) ) or - exists(ParameterNode p | + exists(ParameterNodeExt p | mid.getNode() = p and viableParamArg(_, p, node) and sc1 = mid.getSummaryCtx1() and @@ -4115,7 +4122,7 @@ private module FlowExploration { int pos, TRevSummaryCtx1Some sc1, TRevSummaryCtx2Some sc2, RevPartialAccessPath ap, Configuration config ) { - exists(PartialPathNodeRev mid, ParameterNode p | + exists(PartialPathNodeRev mid, ParameterNodeExt p | mid.getNode() = p and p.isParameterOf(_, pos) and sc1 = mid.getSummaryCtx1() and @@ -4138,7 +4145,7 @@ private module FlowExploration { pragma[nomagic] private predicate revPartialPathThroughCallable( - PartialPathNodeRev mid, ArgumentNode node, RevPartialAccessPath ap, Configuration config + PartialPathNodeRev mid, ArgumentNodeExt node, RevPartialAccessPath ap, Configuration config ) { exists(DataFlowCall call, int pos | revPartialPathThroughCallable0(call, mid, pos, ap, config) and 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 9498e51e7e6..cf83530bcbd 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 @@ -211,10 +211,7 @@ private predicate fullBarrier(Node node, Configuration config) { * Holds if data can flow in one local step from `node1` to `node2`. */ private predicate localFlowStep(Node node1, Node node2, Configuration config) { - ( - simpleLocalFlowStep(node1, node2) or - reverseStepThroughInputOutputAlias(node1, node2) - ) and + simpleLocalFlowStepExt(node1, node2) and not outBarrier(node1, config) and not inBarrier(node2, config) and not fullBarrier(node1, config) and @@ -237,7 +234,7 @@ private predicate additionalLocalFlowStep(Node node1, Node node2, Configuration * Holds if data can flow from `node1` to `node2` in a way that discards call contexts. */ private predicate jumpStep(Node node1, Node node2, Configuration config) { - jumpStep(node1, node2) and + jumpStepCached(node1, node2) and not outBarrier(node1, config) and not inBarrier(node2, config) and not fullBarrier(node1, config) and @@ -388,7 +385,7 @@ private module Stage1 { */ pragma[nomagic] private predicate fwdFlowIsEntered(DataFlowCall call, Cc cc, Configuration config) { - exists(ArgumentNode arg | + exists(ArgumentNodeExt arg | fwdFlow(arg, cc, config) and viableParamArg(call, _, arg) ) @@ -515,7 +512,7 @@ private module Stage1 { pragma[nomagic] predicate viableParamArgNodeCandFwd1( - DataFlowCall call, ParameterNode p, ArgumentNode arg, Configuration config + DataFlowCall call, ParameterNodeExt p, ArgumentNodeExt arg, Configuration config ) { viableParamArg(call, p, arg) and fwdFlow(arg, config) @@ -523,16 +520,16 @@ private module Stage1 { pragma[nomagic] private predicate revFlowIn( - DataFlowCall call, ArgumentNode arg, boolean toReturn, Configuration config + DataFlowCall call, ArgumentNodeExt arg, boolean toReturn, Configuration config ) { - exists(ParameterNode p | + exists(ParameterNodeExt p | revFlow(p, toReturn, config) and viableParamArgNodeCandFwd1(call, p, arg, config) ) } pragma[nomagic] - private predicate revFlowInToReturn(DataFlowCall call, ArgumentNode arg, Configuration config) { + private predicate revFlowInToReturn(DataFlowCall call, ArgumentNodeExt arg, Configuration config) { revFlowIn(call, arg, true, config) } @@ -597,7 +594,9 @@ private module Stage1 { * Holds if flow may enter through `p` and reach a return node making `p` a * candidate for the origin of a summary. */ - predicate parameterMayFlowThrough(ParameterNode p, DataFlowCallable c, Ap ap, Configuration config) { + predicate parameterMayFlowThrough( + ParameterNodeExt p, DataFlowCallable c, Ap ap, Configuration config + ) { exists(ReturnKindExt kind | throughFlowNodeCand(p, config) and returnFlowCallableNodeCand(c, kind, config) and @@ -663,7 +662,7 @@ private predicate flowOutOfCallNodeCand1( pragma[nomagic] private predicate viableParamArgNodeCand1( - DataFlowCall call, ParameterNode p, ArgumentNode arg, Configuration config + DataFlowCall call, ParameterNodeExt p, ArgumentNodeExt arg, Configuration config ) { Stage1::viableParamArgNodeCandFwd1(call, p, arg, config) and Stage1::revFlow(arg, config) @@ -675,7 +674,7 @@ private predicate viableParamArgNodeCand1( */ pragma[nomagic] private predicate flowIntoCallNodeCand1( - DataFlowCall call, ArgumentNode arg, ParameterNode p, Configuration config + DataFlowCall call, ArgumentNodeExt arg, ParameterNodeExt p, Configuration config ) { viableParamArgNodeCand1(call, p, arg, config) and Stage1::revFlow(p, config) and @@ -735,7 +734,7 @@ private predicate flowOutOfCallNodeCand1( */ pragma[nomagic] private predicate flowIntoCallNodeCand1( - DataFlowCall call, ArgumentNode arg, ParameterNode p, boolean allowsFieldFlow, + DataFlowCall call, ArgumentNodeExt arg, ParameterNodeExt p, boolean allowsFieldFlow, Configuration config ) { flowIntoCallNodeCand1(call, arg, p, config) and @@ -944,10 +943,10 @@ private module Stage2 { pragma[nomagic] private predicate fwdFlowIn( - DataFlowCall call, ParameterNode p, Cc outercc, Cc innercc, ApOption argAp, Ap ap, + DataFlowCall call, ParameterNodeExt p, Cc outercc, Cc innercc, ApOption argAp, Ap ap, Configuration config ) { - exists(ArgumentNode arg, boolean allowsFieldFlow | + exists(ArgumentNodeExt arg, boolean allowsFieldFlow | fwdFlow(arg, outercc, argAp, ap, config) and flowIntoCall(call, arg, p, allowsFieldFlow, config) and innercc = getCallContextCall(call, getNodeEnclosingCallable(p), outercc) @@ -992,7 +991,7 @@ private module Stage2 { private predicate fwdFlowIsEntered( DataFlowCall call, Cc cc, ApOption argAp, Ap ap, Configuration config ) { - exists(ParameterNode p | + exists(ParameterNodeExt p | fwdFlowIn(call, p, cc, _, argAp, ap, config) and PrevStage::parameterMayFlowThrough(p, _, getApprox(ap), config) ) @@ -1133,10 +1132,10 @@ private module Stage2 { pragma[nomagic] private predicate revFlowIn( - DataFlowCall call, ArgumentNode arg, boolean toReturn, ApOption returnAp, Ap ap, + DataFlowCall call, ArgumentNodeExt arg, boolean toReturn, ApOption returnAp, Ap ap, Configuration config ) { - exists(ParameterNode p, boolean allowsFieldFlow | + exists(ParameterNodeExt p, boolean allowsFieldFlow | revFlow(p, toReturn, returnAp, ap, config) and flowIntoCall(call, arg, p, allowsFieldFlow, config) | @@ -1146,7 +1145,7 @@ private module Stage2 { pragma[nomagic] private predicate revFlowInToReturn( - DataFlowCall call, ArgumentNode arg, Ap returnAp, Ap ap, Configuration config + DataFlowCall call, ArgumentNodeExt arg, Ap returnAp, Ap ap, Configuration config ) { revFlowIn(call, arg, true, apSome(returnAp), ap, config) } @@ -1199,13 +1198,15 @@ private module Stage2 { pragma[noinline] private predicate parameterFlow( - ParameterNode p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config + ParameterNodeExt p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config ) { revFlow(p, true, apSome(ap0), ap, config) and c = getNodeEnclosingCallable(p) } - predicate parameterMayFlowThrough(ParameterNode p, DataFlowCallable c, Ap ap, Configuration config) { + predicate parameterMayFlowThrough( + ParameterNodeExt p, DataFlowCallable c, Ap ap, Configuration config + ) { exists(ReturnNodeExt ret, Ap ap0, ReturnKindExt kind, int pos | parameterFlow(p, ap, ap0, c, config) and c = getNodeEnclosingCallable(ret) and @@ -1245,7 +1246,7 @@ private predicate flowOutOfCallNodeCand2( pragma[nomagic] private predicate flowIntoCallNodeCand2( - DataFlowCall call, ArgumentNode node1, ParameterNode node2, boolean allowsFieldFlow, + DataFlowCall call, ArgumentNodeExt node1, ParameterNodeExt node2, boolean allowsFieldFlow, Configuration config ) { flowIntoCallNodeCand1(call, node1, node2, allowsFieldFlow, config) and @@ -1260,8 +1261,8 @@ private module LocalFlowBigStep { */ private class FlowCheckNode extends Node { FlowCheckNode() { - this instanceof CastNode or - clearsContent(this, _) + castNode(this) or + clearsContentCached(this, _) } } @@ -1275,7 +1276,7 @@ private module LocalFlowBigStep { config.isSource(node) or jumpStep(_, node, config) or additionalJumpStep(_, node, config) or - node instanceof ParameterNode or + node instanceof ParameterNodeExt or node instanceof OutNodeExt or store(_, _, node, _) or read(_, _, node) or @@ -1321,21 +1322,21 @@ private module LocalFlowBigStep { Node node1, Node node2, boolean preservesValue, DataFlowType t, Configuration config, LocalCallContext cc ) { - not isUnreachableInCall(node2, cc.(LocalCallContextSpecificCall).getCall()) and + not isUnreachableInCallCached(node2, cc.(LocalCallContextSpecificCall).getCall()) and ( localFlowEntry(node1, pragma[only_bind_into](config)) and ( localFlowStepNodeCand1(node1, node2, config) and preservesValue = true and - t = getNodeType(node1) + t = getNodeDataFlowType(node1) or additionalLocalFlowStepNodeCand2(node1, node2, config) and preservesValue = false and - t = getNodeType(node2) + t = getNodeDataFlowType(node2) ) and node1 != node2 and cc.relevantFor(getNodeEnclosingCallable(node1)) and - not isUnreachableInCall(node1, cc.(LocalCallContextSpecificCall).getCall()) and + not isUnreachableInCallCached(node1, cc.(LocalCallContextSpecificCall).getCall()) and Stage2::revFlow(node2, pragma[only_bind_into](config)) or exists(Node mid | @@ -1350,7 +1351,7 @@ private module LocalFlowBigStep { additionalLocalFlowStepNodeCand2(mid, node2, config) and not mid instanceof FlowCheckNode and preservesValue = false and - t = getNodeType(node2) and + t = getNodeDataFlowType(node2) and Stage2::revFlow(node2, pragma[only_bind_into](config)) ) ) @@ -1384,7 +1385,7 @@ private module Stage3 { private ApApprox getApprox(Ap ap) { result = ap.toBoolNonEmpty() } private ApNil getApNil(Node node) { - PrevStage::revFlow(node, _) and result = TFrontNil(getNodeType(node)) + PrevStage::revFlow(node, _) and result = TFrontNil(getNodeDataFlowType(node)) } bindingset[tc, tail] @@ -1443,7 +1444,9 @@ private module Stage3 { bindingset[node, ap] private predicate filter(Node node, Ap ap) { not ap.isClearedAt(node) and - if node instanceof CastingNode then compatibleTypes(getNodeType(node), ap.getType()) else any() + if node instanceof CastingNode + then compatibleTypes(getNodeDataFlowType(node), ap.getType()) + else any() } bindingset[ap, contentType] @@ -1583,10 +1586,10 @@ private module Stage3 { pragma[nomagic] private predicate fwdFlowIn( - DataFlowCall call, ParameterNode p, Cc outercc, Cc innercc, ApOption argAp, Ap ap, + DataFlowCall call, ParameterNodeExt p, Cc outercc, Cc innercc, ApOption argAp, Ap ap, Configuration config ) { - exists(ArgumentNode arg, boolean allowsFieldFlow | + exists(ArgumentNodeExt arg, boolean allowsFieldFlow | fwdFlow(arg, outercc, argAp, ap, config) and flowIntoCall(call, arg, p, allowsFieldFlow, config) and innercc = getCallContextCall(call, getNodeEnclosingCallable(p), outercc) @@ -1631,7 +1634,7 @@ private module Stage3 { private predicate fwdFlowIsEntered( DataFlowCall call, Cc cc, ApOption argAp, Ap ap, Configuration config ) { - exists(ParameterNode p | + exists(ParameterNodeExt p | fwdFlowIn(call, p, cc, _, argAp, ap, config) and PrevStage::parameterMayFlowThrough(p, _, unbindApa(getApprox(ap)), config) ) @@ -1772,10 +1775,10 @@ private module Stage3 { pragma[nomagic] private predicate revFlowIn( - DataFlowCall call, ArgumentNode arg, boolean toReturn, ApOption returnAp, Ap ap, + DataFlowCall call, ArgumentNodeExt arg, boolean toReturn, ApOption returnAp, Ap ap, Configuration config ) { - exists(ParameterNode p, boolean allowsFieldFlow | + exists(ParameterNodeExt p, boolean allowsFieldFlow | revFlow(p, toReturn, returnAp, ap, config) and flowIntoCall(call, arg, p, allowsFieldFlow, config) | @@ -1785,7 +1788,7 @@ private module Stage3 { pragma[nomagic] private predicate revFlowInToReturn( - DataFlowCall call, ArgumentNode arg, Ap returnAp, Ap ap, Configuration config + DataFlowCall call, ArgumentNodeExt arg, Ap returnAp, Ap ap, Configuration config ) { revFlowIn(call, arg, true, apSome(returnAp), ap, config) } @@ -1838,13 +1841,15 @@ private module Stage3 { pragma[noinline] private predicate parameterFlow( - ParameterNode p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config + ParameterNodeExt p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config ) { revFlow(p, true, apSome(ap0), ap, config) and c = getNodeEnclosingCallable(p) } - predicate parameterMayFlowThrough(ParameterNode p, DataFlowCallable c, Ap ap, Configuration config) { + predicate parameterMayFlowThrough( + ParameterNodeExt p, DataFlowCallable c, Ap ap, Configuration config + ) { exists(ReturnNodeExt ret, Ap ap0, ReturnKindExt kind, int pos | parameterFlow(p, ap, ap0, c, config) and c = getNodeEnclosingCallable(ret) and @@ -2088,7 +2093,7 @@ private module Stage4 { private ApApprox getApprox(Ap ap) { result = ap.getFront() } private ApNil getApNil(Node node) { - PrevStage::revFlow(node, _) and result = TNil(getNodeType(node)) + PrevStage::revFlow(node, _) and result = TNil(getNodeDataFlowType(node)) } bindingset[tc, tail] @@ -2155,7 +2160,7 @@ private module Stage4 { pragma[nomagic] private predicate flowIntoCall( - DataFlowCall call, ArgumentNode node1, ParameterNode node2, boolean allowsFieldFlow, + DataFlowCall call, ArgumentNodeExt node1, ParameterNodeExt node2, boolean allowsFieldFlow, Configuration config ) { flowIntoCallNodeCand2(call, node1, node2, allowsFieldFlow, config) and @@ -2300,10 +2305,10 @@ private module Stage4 { pragma[nomagic] private predicate fwdFlowIn( - DataFlowCall call, ParameterNode p, Cc outercc, Cc innercc, ApOption argAp, Ap ap, + DataFlowCall call, ParameterNodeExt p, Cc outercc, Cc innercc, ApOption argAp, Ap ap, Configuration config ) { - exists(ArgumentNode arg, boolean allowsFieldFlow | + exists(ArgumentNodeExt arg, boolean allowsFieldFlow | fwdFlow(arg, outercc, argAp, ap, config) and flowIntoCall(call, arg, p, allowsFieldFlow, config) and innercc = getCallContextCall(call, getNodeEnclosingCallable(p), outercc) @@ -2348,7 +2353,7 @@ private module Stage4 { private predicate fwdFlowIsEntered( DataFlowCall call, Cc cc, ApOption argAp, Ap ap, Configuration config ) { - exists(ParameterNode p | + exists(ParameterNodeExt p | fwdFlowIn(call, p, cc, _, argAp, ap, config) and PrevStage::parameterMayFlowThrough(p, _, unbindApa(getApprox(ap)), config) ) @@ -2489,10 +2494,10 @@ private module Stage4 { pragma[nomagic] private predicate revFlowIn( - DataFlowCall call, ArgumentNode arg, boolean toReturn, ApOption returnAp, Ap ap, + DataFlowCall call, ArgumentNodeExt arg, boolean toReturn, ApOption returnAp, Ap ap, Configuration config ) { - exists(ParameterNode p, boolean allowsFieldFlow | + exists(ParameterNodeExt p, boolean allowsFieldFlow | revFlow(p, toReturn, returnAp, ap, config) and flowIntoCall(call, arg, p, allowsFieldFlow, config) | @@ -2502,7 +2507,7 @@ private module Stage4 { pragma[nomagic] private predicate revFlowInToReturn( - DataFlowCall call, ArgumentNode arg, Ap returnAp, Ap ap, Configuration config + DataFlowCall call, ArgumentNodeExt arg, Ap returnAp, Ap ap, Configuration config ) { revFlowIn(call, arg, true, apSome(returnAp), ap, config) } @@ -2555,13 +2560,15 @@ private module Stage4 { pragma[noinline] private predicate parameterFlow( - ParameterNode p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config + ParameterNodeExt p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config ) { revFlow(p, true, apSome(ap0), ap, config) and c = getNodeEnclosingCallable(p) } - predicate parameterMayFlowThrough(ParameterNode p, DataFlowCallable c, Ap ap, Configuration config) { + predicate parameterMayFlowThrough( + ParameterNodeExt p, DataFlowCallable c, Ap ap, Configuration config + ) { exists(ReturnNodeExt ret, Ap ap0, ReturnKindExt kind, int pos | parameterFlow(p, ap, ap0, c, config) and c = getNodeEnclosingCallable(ret) and @@ -2606,7 +2613,7 @@ private predicate nodeMayUseSummary(Node n, AccessPathApprox apa, Configuration private newtype TSummaryCtx = TSummaryCtxNone() or - TSummaryCtxSome(ParameterNode p, AccessPath ap) { + TSummaryCtxSome(ParameterNodeExt p, AccessPath ap) { Stage4::parameterMayFlowThrough(p, _, ap.getApprox(), _) } @@ -2627,7 +2634,7 @@ private class SummaryCtxNone extends SummaryCtx, TSummaryCtxNone { /** A summary context from which a flow summary can be generated. */ private class SummaryCtxSome extends SummaryCtx, TSummaryCtxSome { - private ParameterNode p; + private ParameterNodeExt p; private AccessPath ap; SummaryCtxSome() { this = TSummaryCtxSome(p, ap) } @@ -2758,7 +2765,7 @@ private newtype TPathNode = config.isSource(node) and cc instanceof CallContextAny and sc instanceof SummaryCtxNone and - ap = TAccessPathNil(getNodeType(node)) + ap = TAccessPathNil(getNodeDataFlowType(node)) or // ... or a step from an existing PathNode to another node. exists(PathNodeMid mid | @@ -2979,7 +2986,7 @@ class PathNode extends TPathNode { Configuration getConfiguration() { none() } private predicate isHidden() { - nodeIsHidden(this.getNode()) and + hiddenNode(this.getNode()) and not this.isSource() and not this instanceof PathNodeSink } @@ -3148,7 +3155,7 @@ private predicate pathStep(PathNodeMid mid, Node node, CallContext cc, SummaryCt cc instanceof CallContextAny and sc instanceof SummaryCtxNone and mid.getAp() instanceof AccessPathNil and - ap = TAccessPathNil(getNodeType(node)) + ap = TAccessPathNil(getNodeDataFlowType(node)) or exists(TypedContent tc | pathStoreStep(mid, node, ap.pop(tc), tc, cc)) and sc = mid.getSummaryCtx() @@ -3235,7 +3242,7 @@ pragma[noinline] private predicate pathIntoArg( PathNodeMid mid, int i, CallContext cc, DataFlowCall call, AccessPath ap, AccessPathApprox apa ) { - exists(ArgumentNode arg | + exists(ArgumentNodeExt arg | arg = mid.getNode() and cc = mid.getCallContext() and arg.argumentOf(call, i) and @@ -3248,7 +3255,7 @@ pragma[noinline] private predicate parameterCand( DataFlowCallable callable, int i, AccessPathApprox apa, Configuration config ) { - exists(ParameterNode p | + exists(ParameterNodeExt p | Stage4::revFlow(p, _, _, apa, config) and p.isParameterOf(callable, i) ) @@ -3272,7 +3279,7 @@ private predicate pathIntoCallable0( * respectively. */ private predicate pathIntoCallable( - PathNodeMid mid, ParameterNode p, CallContext outercc, CallContextCall innercc, SummaryCtx sc, + PathNodeMid mid, ParameterNodeExt p, CallContext outercc, CallContextCall innercc, SummaryCtx sc, DataFlowCall call ) { exists(int i, DataFlowCallable callable, AccessPath ap | @@ -3568,7 +3575,7 @@ private module FlowExploration { private newtype TSummaryCtx1 = TSummaryCtx1None() or - TSummaryCtx1Param(ParameterNode p) + TSummaryCtx1Param(ParameterNodeExt p) private newtype TSummaryCtx2 = TSummaryCtx2None() or @@ -3591,7 +3598,7 @@ private module FlowExploration { cc instanceof CallContextAny and sc1 = TSummaryCtx1None() and sc2 = TSummaryCtx2None() and - ap = TPartialNil(getNodeType(node)) and + ap = TPartialNil(getNodeDataFlowType(node)) and not fullBarrier(node, config) and exists(config.explorationLimit()) or @@ -3611,7 +3618,7 @@ private module FlowExploration { or exists(PartialPathNodeRev mid | revPartialPathStep(mid, node, sc1, sc2, ap, config) and - not clearsContent(node, ap.getHead()) and + not clearsContentCached(node, ap.getHead()) and not fullBarrier(node, config) and distSink(getNodeEnclosingCallable(node), config) <= config.explorationLimit() ) @@ -3625,9 +3632,9 @@ private module FlowExploration { exists(PartialPathNodeFwd mid | partialPathStep(mid, node, cc, sc1, sc2, ap, config) and not fullBarrier(node, config) and - not clearsContent(node, ap.getHead().getContent()) and + not clearsContentCached(node, ap.getHead().getContent()) and if node instanceof CastingNode - then compatibleTypes(getNodeType(node), ap.getType()) + then compatibleTypes(getNodeDataFlowType(node), ap.getType()) else any() ) } @@ -3783,7 +3790,7 @@ private module FlowExploration { PartialPathNodeFwd mid, Node node, CallContext cc, TSummaryCtx1 sc1, TSummaryCtx2 sc2, PartialAccessPath ap, Configuration config ) { - not isUnreachableInCall(node, cc.(CallContextSpecificCall).getCall()) and + not isUnreachableInCallCached(node, cc.(CallContextSpecificCall).getCall()) and ( localFlowStep(mid.getNode(), node, config) and cc = mid.getCallContext() and @@ -3797,7 +3804,7 @@ private module FlowExploration { sc1 = mid.getSummaryCtx1() and sc2 = mid.getSummaryCtx2() and mid.getAp() instanceof PartialAccessPathNil and - ap = TPartialNil(getNodeType(node)) and + ap = TPartialNil(getNodeDataFlowType(node)) and config = mid.getConfiguration() ) or @@ -3813,7 +3820,7 @@ private module FlowExploration { sc1 = TSummaryCtx1None() and sc2 = TSummaryCtx2None() and mid.getAp() instanceof PartialAccessPathNil and - ap = TPartialNil(getNodeType(node)) and + ap = TPartialNil(getNodeDataFlowType(node)) and config = mid.getConfiguration() or partialPathStoreStep(mid, _, _, node, ap) and @@ -3827,7 +3834,7 @@ private module FlowExploration { sc1 = mid.getSummaryCtx1() and sc2 = mid.getSummaryCtx2() and apConsFwd(ap, tc, ap0, config) and - compatibleTypes(ap.getType(), getNodeType(node)) + compatibleTypes(ap.getType(), getNodeDataFlowType(node)) ) or partialPathIntoCallable(mid, node, _, cc, sc1, sc2, _, ap, config) @@ -3924,7 +3931,7 @@ private module FlowExploration { PartialPathNodeFwd mid, int i, CallContext cc, DataFlowCall call, PartialAccessPath ap, Configuration config ) { - exists(ArgumentNode arg | + exists(ArgumentNodeExt arg | arg = mid.getNode() and cc = mid.getCallContext() and arg.argumentOf(call, i) and @@ -3943,7 +3950,7 @@ private module FlowExploration { } private predicate partialPathIntoCallable( - PartialPathNodeFwd mid, ParameterNode p, CallContext outercc, CallContextCall innercc, + PartialPathNodeFwd mid, ParameterNodeExt p, CallContext outercc, CallContextCall innercc, TSummaryCtx1 sc1, TSummaryCtx2 sc2, DataFlowCall call, PartialAccessPath ap, Configuration config ) { @@ -3980,7 +3987,7 @@ private module FlowExploration { DataFlowCall call, PartialPathNodeFwd mid, ReturnKindExt kind, CallContext cc, PartialAccessPath ap, Configuration config ) { - exists(ParameterNode p, CallContext innercc, TSummaryCtx1 sc1, TSummaryCtx2 sc2 | + exists(ParameterNodeExt p, CallContext innercc, TSummaryCtx1 sc1, TSummaryCtx2 sc2 | partialPathIntoCallable(mid, p, cc, innercc, sc1, sc2, call, _, config) and paramFlowsThroughInPartialPath(kind, innercc, sc1, sc2, ap, config) ) @@ -4037,7 +4044,7 @@ private module FlowExploration { apConsRev(ap, c, ap0, config) ) or - exists(ParameterNode p | + exists(ParameterNodeExt p | mid.getNode() = p and viableParamArg(_, p, node) and sc1 = mid.getSummaryCtx1() and @@ -4115,7 +4122,7 @@ private module FlowExploration { int pos, TRevSummaryCtx1Some sc1, TRevSummaryCtx2Some sc2, RevPartialAccessPath ap, Configuration config ) { - exists(PartialPathNodeRev mid, ParameterNode p | + exists(PartialPathNodeRev mid, ParameterNodeExt p | mid.getNode() = p and p.isParameterOf(_, pos) and sc1 = mid.getSummaryCtx1() and @@ -4138,7 +4145,7 @@ private module FlowExploration { pragma[nomagic] private predicate revPartialPathThroughCallable( - PartialPathNodeRev mid, ArgumentNode node, RevPartialAccessPath ap, Configuration config + PartialPathNodeRev mid, ArgumentNodeExt node, RevPartialAccessPath ap, Configuration config ) { exists(DataFlowCall call, int pos | revPartialPathThroughCallable0(call, mid, pos, ap, config) and 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 a51c20c2288..b5ceabc605d 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 @@ -35,22 +35,24 @@ predicate accessPathCostLimits(int apLimit, int tupleLimit) { * calls. For this reason, we cannot reuse the code from `DataFlowImpl.qll` directly. */ private module LambdaFlow { - private predicate viableParamNonLambda(DataFlowCall call, int i, ParameterNode p) { + private predicate viableParamNonLambda(DataFlowCall call, int i, ParameterNodeExt p) { p.isParameterOf(viableCallable(call), i) } - private predicate viableParamLambda(DataFlowCall call, int i, ParameterNode p) { + private predicate viableParamLambda(DataFlowCall call, int i, ParameterNodeExt p) { p.isParameterOf(viableCallableLambda(call, _), i) } - private predicate viableParamArgNonLambda(DataFlowCall call, ParameterNode p, ArgumentNode arg) { + private predicate viableParamArgNonLambda( + DataFlowCall call, ParameterNodeExt p, ArgumentNodeExt arg + ) { exists(int i | viableParamNonLambda(call, i, p) and arg.argumentOf(call, i) ) } - private predicate viableParamArgLambda(DataFlowCall call, ParameterNode p, ArgumentNode arg) { + private predicate viableParamArgLambda(DataFlowCall call, ParameterNodeExt p, ArgumentNodeExt arg) { exists(int i | viableParamLambda(call, i, p) and arg.argumentOf(call, i) @@ -118,8 +120,8 @@ private module LambdaFlow { boolean toJump, DataFlowCallOption lastCall ) { revLambdaFlow0(lambdaCall, kind, node, t, toReturn, toJump, lastCall) and - if node instanceof CastNode or node instanceof ArgumentNode or node instanceof ReturnNode - then compatibleTypes(t, getNodeType(node)) + if castNode(node) or node instanceof ArgumentNodeExt or node instanceof ReturnNode + then compatibleTypes(t, getNodeDataFlowType(node)) else any() } @@ -129,7 +131,7 @@ private module LambdaFlow { boolean toJump, DataFlowCallOption lastCall ) { lambdaCall(lambdaCall, kind, node) and - t = getNodeType(node) and + t = getNodeDataFlowType(node) and toReturn = false and toJump = false and lastCall = TDataFlowCallNone() @@ -146,7 +148,7 @@ private module LambdaFlow { getNodeEnclosingCallable(node) = getNodeEnclosingCallable(mid) | preservesValue = false and - t = getNodeType(node) + t = getNodeDataFlowType(node) or preservesValue = true and t = t0 @@ -160,7 +162,7 @@ private module LambdaFlow { toJump = true and lastCall = TDataFlowCallNone() | - jumpStep(node, mid) and + jumpStepCached(node, mid) and t = t0 or exists(boolean preservesValue | @@ -168,7 +170,7 @@ private module LambdaFlow { getNodeEnclosingCallable(node) != getNodeEnclosingCallable(mid) | preservesValue = false and - t = getNodeType(node) + t = getNodeDataFlowType(node) or preservesValue = true and t = t0 @@ -176,7 +178,7 @@ private module LambdaFlow { ) or // flow into a callable - exists(ParameterNode p, DataFlowCallOption lastCall0, DataFlowCall call | + exists(ParameterNodeExt p, DataFlowCallOption lastCall0, DataFlowCall call | revLambdaFlowIn(lambdaCall, kind, p, t, toJump, lastCall0) and ( if lastCall0 = TDataFlowCallNone() and toJump = false @@ -227,8 +229,8 @@ private module LambdaFlow { pragma[nomagic] predicate revLambdaFlowIn( - DataFlowCall lambdaCall, LambdaCallKind kind, ParameterNode p, DataFlowType t, boolean toJump, - DataFlowCallOption lastCall + DataFlowCall lambdaCall, LambdaCallKind kind, ParameterNodeExt p, DataFlowType t, + boolean toJump, DataFlowCallOption lastCall ) { revLambdaFlow(lambdaCall, kind, p, t, false, toJump, lastCall) } @@ -242,6 +244,89 @@ private DataFlowCallable viableCallableExt(DataFlowCall call) { cached private module Cached { + /** + * If needed, call this predicate from `DataFlowImplSpecific.qll` in order to + * force a stage-dependency on the `DataFlowImplCommon.qll` stage and therby + * collapsing the two stages. + */ + cached + predicate forceCachingInSameStage() { any() } + + cached + predicate nodeEnclosingCallable(Node n, DataFlowCallable c) { c = n.getEnclosingCallable() } + + cached + predicate callEnclosingCallable(DataFlowCall call, DataFlowCallable c) { + c = call.getEnclosingCallable() + } + + cached + predicate nodeDataFlowType(Node n, DataFlowType t) { t = getNodeType(n) } + + cached + predicate jumpStepCached(Node node1, Node node2) { jumpStep(node1, node2) } + + cached + predicate clearsContentCached(Node n, Content c) { clearsContent(n, c) } + + cached + predicate isUnreachableInCallCached(Node n, DataFlowCall call) { isUnreachableInCall(n, call) } + + cached + predicate outNodeExt(Node n) { + n instanceof OutNode + or + n.(PostUpdateNode).getPreUpdateNode() instanceof ArgumentNodeExt + } + + cached + predicate hiddenNode(Node n) { nodeIsHidden(n) } + + cached + OutNodeExt getAnOutNodeExt(DataFlowCall call, ReturnKindExt k) { + result = getAnOutNode(call, k.(ValueReturnKind).getKind()) + or + exists(ArgumentNodeExt arg | + result.(PostUpdateNode).getPreUpdateNode() = arg and + arg.argumentOf(call, k.(ParamUpdateReturnKind).getPosition()) + ) + } + + cached + predicate returnNodeExt(Node n, ReturnKindExt k) { + k = TValueReturn(n.(ReturnNode).getKind()) + or + exists(ParameterNodeExt p, int pos | + parameterValueFlowsToPreUpdate(p, n) and + p.isParameterOf(_, pos) and + k = TParamUpdate(pos) + ) + } + + cached + predicate castNode(Node n) { n instanceof CastNode } + + cached + predicate castingNode(Node n) { + castNode(n) or + n instanceof ParameterNodeExt or + n instanceof OutNodeExt or + // For reads, `x.f`, we want to check that the tracked type after the read (which + // is obtained by popping the head of the access path stack) is compatible with + // the type of `x.f`. + read(_, _, n) + } + + cached + predicate parameterNode(Node n, DataFlowCallable c, int i) { + n.(ParameterNode).isParameterOf(c, i) + } + + cached + predicate argumentNode(Node n, DataFlowCall call, int pos) { + n.(ArgumentNode).argumentOf(call, pos) + } + /** * Gets a viable target for the lambda call `call`. * @@ -261,7 +346,7 @@ private module Cached { * The instance parameter is considered to have index `-1`. */ pragma[nomagic] - private predicate viableParam(DataFlowCall call, int i, ParameterNode p) { + private predicate viableParam(DataFlowCall call, int i, ParameterNodeExt p) { p.isParameterOf(viableCallableExt(call), i) } @@ -270,11 +355,11 @@ private module Cached { * dispatch into account. */ cached - predicate viableParamArg(DataFlowCall call, ParameterNode p, ArgumentNode arg) { + predicate viableParamArg(DataFlowCall call, ParameterNodeExt p, ArgumentNodeExt arg) { exists(int i | viableParam(call, i, p) and arg.argumentOf(call, i) and - compatibleTypes(getNodeType(arg), getNodeType(p)) + compatibleTypes(getNodeDataFlowType(arg), getNodeDataFlowType(p)) ) } @@ -312,7 +397,7 @@ private module Cached { * `read` indicates whether it is contents of `p` that can flow to `node`. */ pragma[nomagic] - private predicate parameterValueFlowCand(ParameterNode p, Node node, boolean read) { + private predicate parameterValueFlowCand(ParameterNodeExt p, Node node, boolean read) { p = node and read = false or @@ -325,30 +410,32 @@ private module Cached { // read exists(Node mid | parameterValueFlowCand(p, mid, false) and - readStep(mid, _, node) and + read(mid, _, node) and read = true ) or // flow through: no prior read - exists(ArgumentNode arg | + exists(ArgumentNodeExt arg | parameterValueFlowArgCand(p, arg, false) and argumentValueFlowsThroughCand(arg, node, read) ) or // flow through: no read inside method - exists(ArgumentNode arg | + exists(ArgumentNodeExt arg | parameterValueFlowArgCand(p, arg, read) and argumentValueFlowsThroughCand(arg, node, false) ) } pragma[nomagic] - private predicate parameterValueFlowArgCand(ParameterNode p, ArgumentNode arg, boolean read) { + private predicate parameterValueFlowArgCand( + ParameterNodeExt p, ArgumentNodeExt arg, boolean read + ) { parameterValueFlowCand(p, arg, read) } pragma[nomagic] - predicate parameterValueFlowsToPreUpdateCand(ParameterNode p, PostUpdateNode n) { + predicate parameterValueFlowsToPreUpdateCand(ParameterNodeExt p, PostUpdateNode n) { parameterValueFlowCand(p, n.getPreUpdateNode(), false) } @@ -360,7 +447,7 @@ private module Cached { * `read` indicates whether it is contents of `p` that can flow to the return * node. */ - predicate parameterValueFlowReturnCand(ParameterNode p, ReturnKind kind, boolean read) { + predicate parameterValueFlowReturnCand(ParameterNodeExt p, ReturnKind kind, boolean read) { exists(ReturnNode ret | parameterValueFlowCand(p, ret, read) and kind = ret.getKind() @@ -369,9 +456,9 @@ private module Cached { pragma[nomagic] private predicate argumentValueFlowsThroughCand0( - DataFlowCall call, ArgumentNode arg, ReturnKind kind, boolean read + DataFlowCall call, ArgumentNodeExt arg, ReturnKind kind, boolean read ) { - exists(ParameterNode param | viableParamArg(call, param, arg) | + exists(ParameterNodeExt param | viableParamArg(call, param, arg) | parameterValueFlowReturnCand(param, kind, read) ) } @@ -382,14 +469,14 @@ private module Cached { * * `read` indicates whether it is contents of `arg` that can flow to `out`. */ - predicate argumentValueFlowsThroughCand(ArgumentNode arg, Node out, boolean read) { + predicate argumentValueFlowsThroughCand(ArgumentNodeExt arg, Node out, boolean read) { exists(DataFlowCall call, ReturnKind kind | argumentValueFlowsThroughCand0(call, arg, kind, read) and out = getAnOutNode(call, kind) ) } - predicate cand(ParameterNode p, Node n) { + predicate cand(ParameterNodeExt p, Node n) { parameterValueFlowCand(p, n, _) and ( parameterValueFlowReturnCand(p, _, _) @@ -416,21 +503,21 @@ private module Cached { * If a read step was taken, then `read` captures the `Content`, the * container type, and the content type. */ - predicate parameterValueFlow(ParameterNode p, Node node, ReadStepTypesOption read) { + predicate parameterValueFlow(ParameterNodeExt p, Node node, ReadStepTypesOption read) { parameterValueFlow0(p, node, read) and if node instanceof CastingNode then // normal flow through read = TReadStepTypesNone() and - compatibleTypes(getNodeType(p), getNodeType(node)) + compatibleTypes(getNodeDataFlowType(p), getNodeDataFlowType(node)) or // getter - compatibleTypes(read.getContentType(), getNodeType(node)) + compatibleTypes(read.getContentType(), getNodeDataFlowType(node)) else any() } pragma[nomagic] - private predicate parameterValueFlow0(ParameterNode p, Node node, ReadStepTypesOption read) { + private predicate parameterValueFlow0(ParameterNodeExt p, Node node, ReadStepTypesOption read) { p = node and Cand::cand(p, _) and read = TReadStepTypesNone() @@ -447,7 +534,7 @@ private module Cached { readStepWithTypes(mid, read.getContainerType(), read.getContent(), node, read.getContentType()) and Cand::parameterValueFlowReturnCand(p, _, true) and - compatibleTypes(getNodeType(p), read.getContainerType()) + compatibleTypes(getNodeDataFlowType(p), read.getContainerType()) ) or parameterValueFlow0_0(TReadStepTypesNone(), p, node, read) @@ -455,16 +542,16 @@ private module Cached { pragma[nomagic] private predicate parameterValueFlow0_0( - ReadStepTypesOption mustBeNone, ParameterNode p, Node node, ReadStepTypesOption read + ReadStepTypesOption mustBeNone, ParameterNodeExt p, Node node, ReadStepTypesOption read ) { // flow through: no prior read - exists(ArgumentNode arg | + exists(ArgumentNodeExt arg | parameterValueFlowArg(p, arg, mustBeNone) and argumentValueFlowsThrough(arg, read, node) ) or // flow through: no read inside method - exists(ArgumentNode arg | + exists(ArgumentNodeExt arg | parameterValueFlowArg(p, arg, read) and argumentValueFlowsThrough(arg, mustBeNone, node) ) @@ -472,7 +559,7 @@ private module Cached { pragma[nomagic] private predicate parameterValueFlowArg( - ParameterNode p, ArgumentNode arg, ReadStepTypesOption read + ParameterNodeExt p, ArgumentNodeExt arg, ReadStepTypesOption read ) { parameterValueFlow(p, arg, read) and Cand::argumentValueFlowsThroughCand(arg, _, _) @@ -480,9 +567,9 @@ private module Cached { pragma[nomagic] private predicate argumentValueFlowsThrough0( - DataFlowCall call, ArgumentNode arg, ReturnKind kind, ReadStepTypesOption read + DataFlowCall call, ArgumentNodeExt arg, ReturnKind kind, ReadStepTypesOption read ) { - exists(ParameterNode param | viableParamArg(call, param, arg) | + exists(ParameterNodeExt param | viableParamArg(call, param, arg) | parameterValueFlowReturn(param, kind, read) ) } @@ -496,18 +583,18 @@ private module Cached { * container type, and the content type. */ pragma[nomagic] - predicate argumentValueFlowsThrough(ArgumentNode arg, ReadStepTypesOption read, Node out) { + predicate argumentValueFlowsThrough(ArgumentNodeExt arg, ReadStepTypesOption read, Node out) { exists(DataFlowCall call, ReturnKind kind | argumentValueFlowsThrough0(call, arg, kind, read) and out = getAnOutNode(call, kind) | // normal flow through read = TReadStepTypesNone() and - compatibleTypes(getNodeType(arg), getNodeType(out)) + compatibleTypes(getNodeDataFlowType(arg), getNodeDataFlowType(out)) or // getter - compatibleTypes(getNodeType(arg), read.getContainerType()) and - compatibleTypes(read.getContentType(), getNodeType(out)) + compatibleTypes(getNodeDataFlowType(arg), read.getContainerType()) and + compatibleTypes(read.getContentType(), getNodeDataFlowType(out)) ) } @@ -516,7 +603,7 @@ private module Cached { * value-preserving steps and a single read step, not taking call * contexts into account, thus representing a getter-step. */ - predicate getterStep(ArgumentNode arg, Content c, Node out) { + predicate getterStep(ArgumentNodeExt arg, Content c, Node out) { argumentValueFlowsThrough(arg, TReadStepTypesSome(_, c, _), out) } @@ -529,7 +616,7 @@ private module Cached { * container type, and the content type. */ private predicate parameterValueFlowReturn( - ParameterNode p, ReturnKind kind, ReadStepTypesOption read + ParameterNodeExt p, ReturnKind kind, ReadStepTypesOption read ) { exists(ReturnNode ret | parameterValueFlow(p, ret, read) and @@ -553,7 +640,7 @@ private module Cached { private predicate mayBenefitFromCallContextExt(DataFlowCall call, DataFlowCallable callable) { mayBenefitFromCallContext(call, callable) or - callable = call.getEnclosingCallable() and + callEnclosingCallable(call, callable) and exists(viableCallableLambda(call, TDataFlowCallSome(_))) } @@ -611,7 +698,7 @@ private module Cached { mayBenefitFromCallContextExt(call, _) and c = viableCallableExt(call) and ctxtgts = count(DataFlowCall ctx | c = viableImplInCallContextExt(call, ctx)) and - tgts = strictcount(DataFlowCall ctx | viableCallableExt(ctx) = call.getEnclosingCallable()) and + tgts = strictcount(DataFlowCall ctx | callEnclosingCallable(call, viableCallableExt(ctx))) and ctxtgts < tgts ) } @@ -635,8 +722,7 @@ private module Cached { * Holds if `p` can flow to the pre-update node associated with post-update * node `n`, in the same callable, using only value-preserving steps. */ - cached - predicate parameterValueFlowsToPreUpdate(ParameterNode p, PostUpdateNode n) { + private predicate parameterValueFlowsToPreUpdate(ParameterNodeExt p, PostUpdateNode n) { parameterValueFlow(p, n.getPreUpdateNode(), TReadStepTypesNone()) } @@ -644,9 +730,9 @@ private module Cached { Node node1, Content c, Node node2, DataFlowType contentType, DataFlowType containerType ) { storeStep(node1, c, node2) and - readStep(_, c, _) and - contentType = getNodeType(node1) and - containerType = getNodeType(node2) + read(_, c, _) and + contentType = getNodeDataFlowType(node1) and + containerType = getNodeDataFlowType(node2) or exists(Node n1, Node n2 | n1 = node1.(PostUpdateNode).getPreUpdateNode() and @@ -654,12 +740,15 @@ private module Cached { | argumentValueFlowsThrough(n2, TReadStepTypesSome(containerType, c, contentType), n1) or - readStep(n2, c, n1) and - contentType = getNodeType(n1) and - containerType = getNodeType(n2) + read(n2, c, n1) and + contentType = getNodeDataFlowType(n1) and + containerType = getNodeDataFlowType(n2) ) } + cached + predicate read(Node node1, Content c, Node node2) { readStep(node1, c, node2) } + /** * Holds if data can flow from `node1` to `node2` via a direct assignment to * `f`. @@ -678,8 +767,9 @@ private module Cached { * are aliases. A typical example is a function returning `this`, implementing a fluent * interface. */ - cached - predicate reverseStepThroughInputOutputAlias(PostUpdateNode fromNode, PostUpdateNode toNode) { + private predicate reverseStepThroughInputOutputAlias( + PostUpdateNode fromNode, PostUpdateNode toNode + ) { exists(Node fromPre, Node toPre | fromPre = fromNode.getPreUpdateNode() and toPre = toNode.getPreUpdateNode() @@ -688,14 +778,20 @@ private module Cached { // Does the language-specific simpleLocalFlowStep already model flow // from function input to output? fromPre = getAnOutNode(c, _) and - toPre.(ArgumentNode).argumentOf(c, _) and - simpleLocalFlowStep(toPre.(ArgumentNode), fromPre) + toPre.(ArgumentNodeExt).argumentOf(c, _) and + simpleLocalFlowStep(toPre.(ArgumentNodeExt), fromPre) ) or argumentValueFlowsThrough(toPre, TReadStepTypesNone(), fromPre) ) } + cached + predicate simpleLocalFlowStepExt(Node node1, Node node2) { + simpleLocalFlowStep(node1, node2) or + reverseStepThroughInputOutputAlias(node1, node2) + } + /** * Holds if the call context `call` either improves virtual dispatch in * `callable` or if it allows us to prune unreachable nodes in `callable`. @@ -704,7 +800,7 @@ private module Cached { predicate recordDataFlowCallSite(DataFlowCall call, DataFlowCallable callable) { reducedViableImplInCallContext(_, callable, call) or - exists(Node n | getNodeEnclosingCallable(n) = callable | isUnreachableInCall(n, call)) + exists(Node n | getNodeEnclosingCallable(n) = callable | isUnreachableInCallCached(n, call)) } cached @@ -726,12 +822,12 @@ private module Cached { cached newtype TLocalFlowCallContext = TAnyLocalCall() or - TSpecificLocalCall(DataFlowCall call) { isUnreachableInCall(_, call) } + TSpecificLocalCall(DataFlowCall call) { isUnreachableInCallCached(_, call) } cached newtype TReturnKindExt = TValueReturn(ReturnKind kind) or - TParamUpdate(int pos) { exists(ParameterNode p | p.isParameterOf(_, pos)) } + TParamUpdate(int pos) { exists(ParameterNodeExt p | p.isParameterOf(_, pos)) } cached newtype TBooleanOption = @@ -761,23 +857,15 @@ private module Cached { * A `Node` at which a cast can occur such that the type should be checked. */ class CastingNode extends Node { - CastingNode() { - this instanceof ParameterNode or - this instanceof CastNode or - this instanceof OutNodeExt or - // For reads, `x.f`, we want to check that the tracked type after the read (which - // is obtained by popping the head of the access path stack) is compatible with - // the type of `x.f`. - readStep(_, _, this) - } + CastingNode() { castingNode(this) } } private predicate readStepWithTypes( Node n1, DataFlowType container, Content c, Node n2, DataFlowType content ) { - readStep(n1, c, n2) and - container = getNodeType(n1) and - content = getNodeType(n2) + read(n1, c, n2) and + container = getNodeDataFlowType(n1) and + content = getNodeDataFlowType(n2) } private newtype TReadStepTypesOption = @@ -854,7 +942,7 @@ class CallContextSomeCall extends CallContextCall, TSomeCall { override string toString() { result = "CcSomeCall" } override predicate relevantFor(DataFlowCallable callable) { - exists(ParameterNode p | getNodeEnclosingCallable(p) = callable) + exists(ParameterNodeExt p | getNodeEnclosingCallable(p) = callable) } override predicate matchesCall(DataFlowCall call) { any() } @@ -866,7 +954,7 @@ class CallContextReturn extends CallContextNoCall, TReturn { } override predicate relevantFor(DataFlowCallable callable) { - exists(DataFlowCall call | this = TReturn(_, call) and call.getEnclosingCallable() = callable) + exists(DataFlowCall call | this = TReturn(_, call) and callEnclosingCallable(call, callable)) } } @@ -899,7 +987,7 @@ class LocalCallContextSpecificCall extends LocalCallContext, TSpecificLocalCall } private predicate relevantLocalCCtx(DataFlowCall call, DataFlowCallable callable) { - exists(Node n | getNodeEnclosingCallable(n) = callable and isUnreachableInCall(n, call)) + exists(Node n | getNodeEnclosingCallable(n) = callable and isUnreachableInCallCached(n, call)) } /** @@ -913,26 +1001,37 @@ LocalCallContext getLocalCallContext(CallContext ctx, DataFlowCallable callable) else result instanceof LocalCallContextAny } +/** + * The value of a parameter at function entry, viewed as a node in a data + * flow graph. + */ +class ParameterNodeExt extends Node { + ParameterNodeExt() { parameterNode(this, _, _) } + + /** + * Holds if this node is the parameter of callable `c` at the specified + * (zero-based) position. + */ + predicate isParameterOf(DataFlowCallable c, int i) { parameterNode(this, c, i) } +} + +/** A data-flow node that represents a call argument. */ +class ArgumentNodeExt extends Node { + ArgumentNodeExt() { argumentNode(this, _, _) } + + /** Holds if this argument occurs at the given position in the given call. */ + final predicate argumentOf(DataFlowCall call, int pos) { argumentNode(this, call, pos) } +} + /** * A node from which flow can return to the caller. This is either a regular * `ReturnNode` or a `PostUpdateNode` corresponding to the value of a parameter. */ class ReturnNodeExt extends Node { - ReturnNodeExt() { - this instanceof ReturnNode or - parameterValueFlowsToPreUpdate(_, this) - } + ReturnNodeExt() { returnNodeExt(this, _) } /** Gets the kind of this returned value. */ - ReturnKindExt getKind() { - result = TValueReturn(this.(ReturnNode).getKind()) - or - exists(ParameterNode p, int pos | - parameterValueFlowsToPreUpdate(p, this) and - p.isParameterOf(_, pos) and - result = TParamUpdate(pos) - ) - } + ReturnKindExt getKind() { returnNodeExt(this, result) } } /** @@ -940,11 +1039,7 @@ class ReturnNodeExt extends Node { * or a post-update node associated with a call argument. */ class OutNodeExt extends Node { - OutNodeExt() { - this instanceof OutNode - or - this.(PostUpdateNode).getPreUpdateNode() instanceof ArgumentNode - } + OutNodeExt() { outNodeExt(this) } } /** @@ -957,7 +1052,7 @@ abstract class ReturnKindExt extends TReturnKindExt { abstract string toString(); /** Gets a node corresponding to data flow out of `call`. */ - abstract OutNodeExt getAnOutNode(DataFlowCall call); + final OutNodeExt getAnOutNode(DataFlowCall call) { result = getAnOutNodeExt(call, this) } } class ValueReturnKind extends ReturnKindExt, TValueReturn { @@ -968,10 +1063,6 @@ class ValueReturnKind extends ReturnKindExt, TValueReturn { ReturnKind getKind() { result = kind } override string toString() { result = kind.toString() } - - override OutNodeExt getAnOutNode(DataFlowCall call) { - result = getAnOutNode(call, this.getKind()) - } } class ParamUpdateReturnKind extends ReturnKindExt, TParamUpdate { @@ -982,13 +1073,6 @@ class ParamUpdateReturnKind extends ReturnKindExt, TParamUpdate { int getPosition() { result = pos } override string toString() { result = "param update " + pos } - - override OutNodeExt getAnOutNode(DataFlowCall call) { - exists(ArgumentNode arg | - result.(PostUpdateNode).getPreUpdateNode() = arg and - arg.argumentOf(call, this.getPosition()) - ) - } } /** A callable tagged with a relevant return kind. */ @@ -1015,10 +1099,13 @@ class ReturnPosition extends TReturnPosition0 { */ pragma[inline] DataFlowCallable getNodeEnclosingCallable(Node n) { - exists(Node n0 | - pragma[only_bind_into](n0) = n and - pragma[only_bind_into](result) = n0.getEnclosingCallable() - ) + nodeEnclosingCallable(pragma[only_bind_out](n), pragma[only_bind_into](result)) +} + +/** Gets the type of `n` used for type pruning. */ +pragma[inline] +DataFlowType getNodeDataFlowType(Node n) { + nodeDataFlowType(pragma[only_bind_out](n), pragma[only_bind_into](result)) } pragma[noinline] @@ -1042,7 +1129,7 @@ predicate resolveReturn(CallContext cc, DataFlowCallable callable, DataFlowCall cc instanceof CallContextAny and callable = viableCallableExt(call) or exists(DataFlowCallable c0, DataFlowCall call0 | - call0.getEnclosingCallable() = callable and + callEnclosingCallable(call0, callable) and cc = TReturn(c0, call0) and c0 = prunedViableImplInCallContextReverse(call0, call) ) @@ -1063,8 +1150,6 @@ DataFlowCallable resolveCall(DataFlowCall call, CallContext cc) { result = viableCallableExt(call) and cc instanceof CallContextReturn } -predicate read = readStep/3; - /** An optional Boolean value. */ class BooleanOption extends TBooleanOption { string toString() { @@ -1116,7 +1201,7 @@ abstract class AccessPathFront extends TAccessPathFront { TypedContent getHead() { this = TFrontHead(result) } - predicate isClearedAt(Node n) { clearsContent(n, getHead().getContent()) } + predicate isClearedAt(Node n) { clearsContentCached(n, getHead().getContent()) } } class AccessPathFrontNil extends AccessPathFront, TFrontNil { 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 9498e51e7e6..cf83530bcbd 100644 --- a/csharp/ql/src/semmle/code/csharp/dataflow/internal/DataFlowImpl2.qll +++ b/csharp/ql/src/semmle/code/csharp/dataflow/internal/DataFlowImpl2.qll @@ -211,10 +211,7 @@ private predicate fullBarrier(Node node, Configuration config) { * Holds if data can flow in one local step from `node1` to `node2`. */ private predicate localFlowStep(Node node1, Node node2, Configuration config) { - ( - simpleLocalFlowStep(node1, node2) or - reverseStepThroughInputOutputAlias(node1, node2) - ) and + simpleLocalFlowStepExt(node1, node2) and not outBarrier(node1, config) and not inBarrier(node2, config) and not fullBarrier(node1, config) and @@ -237,7 +234,7 @@ private predicate additionalLocalFlowStep(Node node1, Node node2, Configuration * Holds if data can flow from `node1` to `node2` in a way that discards call contexts. */ private predicate jumpStep(Node node1, Node node2, Configuration config) { - jumpStep(node1, node2) and + jumpStepCached(node1, node2) and not outBarrier(node1, config) and not inBarrier(node2, config) and not fullBarrier(node1, config) and @@ -388,7 +385,7 @@ private module Stage1 { */ pragma[nomagic] private predicate fwdFlowIsEntered(DataFlowCall call, Cc cc, Configuration config) { - exists(ArgumentNode arg | + exists(ArgumentNodeExt arg | fwdFlow(arg, cc, config) and viableParamArg(call, _, arg) ) @@ -515,7 +512,7 @@ private module Stage1 { pragma[nomagic] predicate viableParamArgNodeCandFwd1( - DataFlowCall call, ParameterNode p, ArgumentNode arg, Configuration config + DataFlowCall call, ParameterNodeExt p, ArgumentNodeExt arg, Configuration config ) { viableParamArg(call, p, arg) and fwdFlow(arg, config) @@ -523,16 +520,16 @@ private module Stage1 { pragma[nomagic] private predicate revFlowIn( - DataFlowCall call, ArgumentNode arg, boolean toReturn, Configuration config + DataFlowCall call, ArgumentNodeExt arg, boolean toReturn, Configuration config ) { - exists(ParameterNode p | + exists(ParameterNodeExt p | revFlow(p, toReturn, config) and viableParamArgNodeCandFwd1(call, p, arg, config) ) } pragma[nomagic] - private predicate revFlowInToReturn(DataFlowCall call, ArgumentNode arg, Configuration config) { + private predicate revFlowInToReturn(DataFlowCall call, ArgumentNodeExt arg, Configuration config) { revFlowIn(call, arg, true, config) } @@ -597,7 +594,9 @@ private module Stage1 { * Holds if flow may enter through `p` and reach a return node making `p` a * candidate for the origin of a summary. */ - predicate parameterMayFlowThrough(ParameterNode p, DataFlowCallable c, Ap ap, Configuration config) { + predicate parameterMayFlowThrough( + ParameterNodeExt p, DataFlowCallable c, Ap ap, Configuration config + ) { exists(ReturnKindExt kind | throughFlowNodeCand(p, config) and returnFlowCallableNodeCand(c, kind, config) and @@ -663,7 +662,7 @@ private predicate flowOutOfCallNodeCand1( pragma[nomagic] private predicate viableParamArgNodeCand1( - DataFlowCall call, ParameterNode p, ArgumentNode arg, Configuration config + DataFlowCall call, ParameterNodeExt p, ArgumentNodeExt arg, Configuration config ) { Stage1::viableParamArgNodeCandFwd1(call, p, arg, config) and Stage1::revFlow(arg, config) @@ -675,7 +674,7 @@ private predicate viableParamArgNodeCand1( */ pragma[nomagic] private predicate flowIntoCallNodeCand1( - DataFlowCall call, ArgumentNode arg, ParameterNode p, Configuration config + DataFlowCall call, ArgumentNodeExt arg, ParameterNodeExt p, Configuration config ) { viableParamArgNodeCand1(call, p, arg, config) and Stage1::revFlow(p, config) and @@ -735,7 +734,7 @@ private predicate flowOutOfCallNodeCand1( */ pragma[nomagic] private predicate flowIntoCallNodeCand1( - DataFlowCall call, ArgumentNode arg, ParameterNode p, boolean allowsFieldFlow, + DataFlowCall call, ArgumentNodeExt arg, ParameterNodeExt p, boolean allowsFieldFlow, Configuration config ) { flowIntoCallNodeCand1(call, arg, p, config) and @@ -944,10 +943,10 @@ private module Stage2 { pragma[nomagic] private predicate fwdFlowIn( - DataFlowCall call, ParameterNode p, Cc outercc, Cc innercc, ApOption argAp, Ap ap, + DataFlowCall call, ParameterNodeExt p, Cc outercc, Cc innercc, ApOption argAp, Ap ap, Configuration config ) { - exists(ArgumentNode arg, boolean allowsFieldFlow | + exists(ArgumentNodeExt arg, boolean allowsFieldFlow | fwdFlow(arg, outercc, argAp, ap, config) and flowIntoCall(call, arg, p, allowsFieldFlow, config) and innercc = getCallContextCall(call, getNodeEnclosingCallable(p), outercc) @@ -992,7 +991,7 @@ private module Stage2 { private predicate fwdFlowIsEntered( DataFlowCall call, Cc cc, ApOption argAp, Ap ap, Configuration config ) { - exists(ParameterNode p | + exists(ParameterNodeExt p | fwdFlowIn(call, p, cc, _, argAp, ap, config) and PrevStage::parameterMayFlowThrough(p, _, getApprox(ap), config) ) @@ -1133,10 +1132,10 @@ private module Stage2 { pragma[nomagic] private predicate revFlowIn( - DataFlowCall call, ArgumentNode arg, boolean toReturn, ApOption returnAp, Ap ap, + DataFlowCall call, ArgumentNodeExt arg, boolean toReturn, ApOption returnAp, Ap ap, Configuration config ) { - exists(ParameterNode p, boolean allowsFieldFlow | + exists(ParameterNodeExt p, boolean allowsFieldFlow | revFlow(p, toReturn, returnAp, ap, config) and flowIntoCall(call, arg, p, allowsFieldFlow, config) | @@ -1146,7 +1145,7 @@ private module Stage2 { pragma[nomagic] private predicate revFlowInToReturn( - DataFlowCall call, ArgumentNode arg, Ap returnAp, Ap ap, Configuration config + DataFlowCall call, ArgumentNodeExt arg, Ap returnAp, Ap ap, Configuration config ) { revFlowIn(call, arg, true, apSome(returnAp), ap, config) } @@ -1199,13 +1198,15 @@ private module Stage2 { pragma[noinline] private predicate parameterFlow( - ParameterNode p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config + ParameterNodeExt p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config ) { revFlow(p, true, apSome(ap0), ap, config) and c = getNodeEnclosingCallable(p) } - predicate parameterMayFlowThrough(ParameterNode p, DataFlowCallable c, Ap ap, Configuration config) { + predicate parameterMayFlowThrough( + ParameterNodeExt p, DataFlowCallable c, Ap ap, Configuration config + ) { exists(ReturnNodeExt ret, Ap ap0, ReturnKindExt kind, int pos | parameterFlow(p, ap, ap0, c, config) and c = getNodeEnclosingCallable(ret) and @@ -1245,7 +1246,7 @@ private predicate flowOutOfCallNodeCand2( pragma[nomagic] private predicate flowIntoCallNodeCand2( - DataFlowCall call, ArgumentNode node1, ParameterNode node2, boolean allowsFieldFlow, + DataFlowCall call, ArgumentNodeExt node1, ParameterNodeExt node2, boolean allowsFieldFlow, Configuration config ) { flowIntoCallNodeCand1(call, node1, node2, allowsFieldFlow, config) and @@ -1260,8 +1261,8 @@ private module LocalFlowBigStep { */ private class FlowCheckNode extends Node { FlowCheckNode() { - this instanceof CastNode or - clearsContent(this, _) + castNode(this) or + clearsContentCached(this, _) } } @@ -1275,7 +1276,7 @@ private module LocalFlowBigStep { config.isSource(node) or jumpStep(_, node, config) or additionalJumpStep(_, node, config) or - node instanceof ParameterNode or + node instanceof ParameterNodeExt or node instanceof OutNodeExt or store(_, _, node, _) or read(_, _, node) or @@ -1321,21 +1322,21 @@ private module LocalFlowBigStep { Node node1, Node node2, boolean preservesValue, DataFlowType t, Configuration config, LocalCallContext cc ) { - not isUnreachableInCall(node2, cc.(LocalCallContextSpecificCall).getCall()) and + not isUnreachableInCallCached(node2, cc.(LocalCallContextSpecificCall).getCall()) and ( localFlowEntry(node1, pragma[only_bind_into](config)) and ( localFlowStepNodeCand1(node1, node2, config) and preservesValue = true and - t = getNodeType(node1) + t = getNodeDataFlowType(node1) or additionalLocalFlowStepNodeCand2(node1, node2, config) and preservesValue = false and - t = getNodeType(node2) + t = getNodeDataFlowType(node2) ) and node1 != node2 and cc.relevantFor(getNodeEnclosingCallable(node1)) and - not isUnreachableInCall(node1, cc.(LocalCallContextSpecificCall).getCall()) and + not isUnreachableInCallCached(node1, cc.(LocalCallContextSpecificCall).getCall()) and Stage2::revFlow(node2, pragma[only_bind_into](config)) or exists(Node mid | @@ -1350,7 +1351,7 @@ private module LocalFlowBigStep { additionalLocalFlowStepNodeCand2(mid, node2, config) and not mid instanceof FlowCheckNode and preservesValue = false and - t = getNodeType(node2) and + t = getNodeDataFlowType(node2) and Stage2::revFlow(node2, pragma[only_bind_into](config)) ) ) @@ -1384,7 +1385,7 @@ private module Stage3 { private ApApprox getApprox(Ap ap) { result = ap.toBoolNonEmpty() } private ApNil getApNil(Node node) { - PrevStage::revFlow(node, _) and result = TFrontNil(getNodeType(node)) + PrevStage::revFlow(node, _) and result = TFrontNil(getNodeDataFlowType(node)) } bindingset[tc, tail] @@ -1443,7 +1444,9 @@ private module Stage3 { bindingset[node, ap] private predicate filter(Node node, Ap ap) { not ap.isClearedAt(node) and - if node instanceof CastingNode then compatibleTypes(getNodeType(node), ap.getType()) else any() + if node instanceof CastingNode + then compatibleTypes(getNodeDataFlowType(node), ap.getType()) + else any() } bindingset[ap, contentType] @@ -1583,10 +1586,10 @@ private module Stage3 { pragma[nomagic] private predicate fwdFlowIn( - DataFlowCall call, ParameterNode p, Cc outercc, Cc innercc, ApOption argAp, Ap ap, + DataFlowCall call, ParameterNodeExt p, Cc outercc, Cc innercc, ApOption argAp, Ap ap, Configuration config ) { - exists(ArgumentNode arg, boolean allowsFieldFlow | + exists(ArgumentNodeExt arg, boolean allowsFieldFlow | fwdFlow(arg, outercc, argAp, ap, config) and flowIntoCall(call, arg, p, allowsFieldFlow, config) and innercc = getCallContextCall(call, getNodeEnclosingCallable(p), outercc) @@ -1631,7 +1634,7 @@ private module Stage3 { private predicate fwdFlowIsEntered( DataFlowCall call, Cc cc, ApOption argAp, Ap ap, Configuration config ) { - exists(ParameterNode p | + exists(ParameterNodeExt p | fwdFlowIn(call, p, cc, _, argAp, ap, config) and PrevStage::parameterMayFlowThrough(p, _, unbindApa(getApprox(ap)), config) ) @@ -1772,10 +1775,10 @@ private module Stage3 { pragma[nomagic] private predicate revFlowIn( - DataFlowCall call, ArgumentNode arg, boolean toReturn, ApOption returnAp, Ap ap, + DataFlowCall call, ArgumentNodeExt arg, boolean toReturn, ApOption returnAp, Ap ap, Configuration config ) { - exists(ParameterNode p, boolean allowsFieldFlow | + exists(ParameterNodeExt p, boolean allowsFieldFlow | revFlow(p, toReturn, returnAp, ap, config) and flowIntoCall(call, arg, p, allowsFieldFlow, config) | @@ -1785,7 +1788,7 @@ private module Stage3 { pragma[nomagic] private predicate revFlowInToReturn( - DataFlowCall call, ArgumentNode arg, Ap returnAp, Ap ap, Configuration config + DataFlowCall call, ArgumentNodeExt arg, Ap returnAp, Ap ap, Configuration config ) { revFlowIn(call, arg, true, apSome(returnAp), ap, config) } @@ -1838,13 +1841,15 @@ private module Stage3 { pragma[noinline] private predicate parameterFlow( - ParameterNode p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config + ParameterNodeExt p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config ) { revFlow(p, true, apSome(ap0), ap, config) and c = getNodeEnclosingCallable(p) } - predicate parameterMayFlowThrough(ParameterNode p, DataFlowCallable c, Ap ap, Configuration config) { + predicate parameterMayFlowThrough( + ParameterNodeExt p, DataFlowCallable c, Ap ap, Configuration config + ) { exists(ReturnNodeExt ret, Ap ap0, ReturnKindExt kind, int pos | parameterFlow(p, ap, ap0, c, config) and c = getNodeEnclosingCallable(ret) and @@ -2088,7 +2093,7 @@ private module Stage4 { private ApApprox getApprox(Ap ap) { result = ap.getFront() } private ApNil getApNil(Node node) { - PrevStage::revFlow(node, _) and result = TNil(getNodeType(node)) + PrevStage::revFlow(node, _) and result = TNil(getNodeDataFlowType(node)) } bindingset[tc, tail] @@ -2155,7 +2160,7 @@ private module Stage4 { pragma[nomagic] private predicate flowIntoCall( - DataFlowCall call, ArgumentNode node1, ParameterNode node2, boolean allowsFieldFlow, + DataFlowCall call, ArgumentNodeExt node1, ParameterNodeExt node2, boolean allowsFieldFlow, Configuration config ) { flowIntoCallNodeCand2(call, node1, node2, allowsFieldFlow, config) and @@ -2300,10 +2305,10 @@ private module Stage4 { pragma[nomagic] private predicate fwdFlowIn( - DataFlowCall call, ParameterNode p, Cc outercc, Cc innercc, ApOption argAp, Ap ap, + DataFlowCall call, ParameterNodeExt p, Cc outercc, Cc innercc, ApOption argAp, Ap ap, Configuration config ) { - exists(ArgumentNode arg, boolean allowsFieldFlow | + exists(ArgumentNodeExt arg, boolean allowsFieldFlow | fwdFlow(arg, outercc, argAp, ap, config) and flowIntoCall(call, arg, p, allowsFieldFlow, config) and innercc = getCallContextCall(call, getNodeEnclosingCallable(p), outercc) @@ -2348,7 +2353,7 @@ private module Stage4 { private predicate fwdFlowIsEntered( DataFlowCall call, Cc cc, ApOption argAp, Ap ap, Configuration config ) { - exists(ParameterNode p | + exists(ParameterNodeExt p | fwdFlowIn(call, p, cc, _, argAp, ap, config) and PrevStage::parameterMayFlowThrough(p, _, unbindApa(getApprox(ap)), config) ) @@ -2489,10 +2494,10 @@ private module Stage4 { pragma[nomagic] private predicate revFlowIn( - DataFlowCall call, ArgumentNode arg, boolean toReturn, ApOption returnAp, Ap ap, + DataFlowCall call, ArgumentNodeExt arg, boolean toReturn, ApOption returnAp, Ap ap, Configuration config ) { - exists(ParameterNode p, boolean allowsFieldFlow | + exists(ParameterNodeExt p, boolean allowsFieldFlow | revFlow(p, toReturn, returnAp, ap, config) and flowIntoCall(call, arg, p, allowsFieldFlow, config) | @@ -2502,7 +2507,7 @@ private module Stage4 { pragma[nomagic] private predicate revFlowInToReturn( - DataFlowCall call, ArgumentNode arg, Ap returnAp, Ap ap, Configuration config + DataFlowCall call, ArgumentNodeExt arg, Ap returnAp, Ap ap, Configuration config ) { revFlowIn(call, arg, true, apSome(returnAp), ap, config) } @@ -2555,13 +2560,15 @@ private module Stage4 { pragma[noinline] private predicate parameterFlow( - ParameterNode p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config + ParameterNodeExt p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config ) { revFlow(p, true, apSome(ap0), ap, config) and c = getNodeEnclosingCallable(p) } - predicate parameterMayFlowThrough(ParameterNode p, DataFlowCallable c, Ap ap, Configuration config) { + predicate parameterMayFlowThrough( + ParameterNodeExt p, DataFlowCallable c, Ap ap, Configuration config + ) { exists(ReturnNodeExt ret, Ap ap0, ReturnKindExt kind, int pos | parameterFlow(p, ap, ap0, c, config) and c = getNodeEnclosingCallable(ret) and @@ -2606,7 +2613,7 @@ private predicate nodeMayUseSummary(Node n, AccessPathApprox apa, Configuration private newtype TSummaryCtx = TSummaryCtxNone() or - TSummaryCtxSome(ParameterNode p, AccessPath ap) { + TSummaryCtxSome(ParameterNodeExt p, AccessPath ap) { Stage4::parameterMayFlowThrough(p, _, ap.getApprox(), _) } @@ -2627,7 +2634,7 @@ private class SummaryCtxNone extends SummaryCtx, TSummaryCtxNone { /** A summary context from which a flow summary can be generated. */ private class SummaryCtxSome extends SummaryCtx, TSummaryCtxSome { - private ParameterNode p; + private ParameterNodeExt p; private AccessPath ap; SummaryCtxSome() { this = TSummaryCtxSome(p, ap) } @@ -2758,7 +2765,7 @@ private newtype TPathNode = config.isSource(node) and cc instanceof CallContextAny and sc instanceof SummaryCtxNone and - ap = TAccessPathNil(getNodeType(node)) + ap = TAccessPathNil(getNodeDataFlowType(node)) or // ... or a step from an existing PathNode to another node. exists(PathNodeMid mid | @@ -2979,7 +2986,7 @@ class PathNode extends TPathNode { Configuration getConfiguration() { none() } private predicate isHidden() { - nodeIsHidden(this.getNode()) and + hiddenNode(this.getNode()) and not this.isSource() and not this instanceof PathNodeSink } @@ -3148,7 +3155,7 @@ private predicate pathStep(PathNodeMid mid, Node node, CallContext cc, SummaryCt cc instanceof CallContextAny and sc instanceof SummaryCtxNone and mid.getAp() instanceof AccessPathNil and - ap = TAccessPathNil(getNodeType(node)) + ap = TAccessPathNil(getNodeDataFlowType(node)) or exists(TypedContent tc | pathStoreStep(mid, node, ap.pop(tc), tc, cc)) and sc = mid.getSummaryCtx() @@ -3235,7 +3242,7 @@ pragma[noinline] private predicate pathIntoArg( PathNodeMid mid, int i, CallContext cc, DataFlowCall call, AccessPath ap, AccessPathApprox apa ) { - exists(ArgumentNode arg | + exists(ArgumentNodeExt arg | arg = mid.getNode() and cc = mid.getCallContext() and arg.argumentOf(call, i) and @@ -3248,7 +3255,7 @@ pragma[noinline] private predicate parameterCand( DataFlowCallable callable, int i, AccessPathApprox apa, Configuration config ) { - exists(ParameterNode p | + exists(ParameterNodeExt p | Stage4::revFlow(p, _, _, apa, config) and p.isParameterOf(callable, i) ) @@ -3272,7 +3279,7 @@ private predicate pathIntoCallable0( * respectively. */ private predicate pathIntoCallable( - PathNodeMid mid, ParameterNode p, CallContext outercc, CallContextCall innercc, SummaryCtx sc, + PathNodeMid mid, ParameterNodeExt p, CallContext outercc, CallContextCall innercc, SummaryCtx sc, DataFlowCall call ) { exists(int i, DataFlowCallable callable, AccessPath ap | @@ -3568,7 +3575,7 @@ private module FlowExploration { private newtype TSummaryCtx1 = TSummaryCtx1None() or - TSummaryCtx1Param(ParameterNode p) + TSummaryCtx1Param(ParameterNodeExt p) private newtype TSummaryCtx2 = TSummaryCtx2None() or @@ -3591,7 +3598,7 @@ private module FlowExploration { cc instanceof CallContextAny and sc1 = TSummaryCtx1None() and sc2 = TSummaryCtx2None() and - ap = TPartialNil(getNodeType(node)) and + ap = TPartialNil(getNodeDataFlowType(node)) and not fullBarrier(node, config) and exists(config.explorationLimit()) or @@ -3611,7 +3618,7 @@ private module FlowExploration { or exists(PartialPathNodeRev mid | revPartialPathStep(mid, node, sc1, sc2, ap, config) and - not clearsContent(node, ap.getHead()) and + not clearsContentCached(node, ap.getHead()) and not fullBarrier(node, config) and distSink(getNodeEnclosingCallable(node), config) <= config.explorationLimit() ) @@ -3625,9 +3632,9 @@ private module FlowExploration { exists(PartialPathNodeFwd mid | partialPathStep(mid, node, cc, sc1, sc2, ap, config) and not fullBarrier(node, config) and - not clearsContent(node, ap.getHead().getContent()) and + not clearsContentCached(node, ap.getHead().getContent()) and if node instanceof CastingNode - then compatibleTypes(getNodeType(node), ap.getType()) + then compatibleTypes(getNodeDataFlowType(node), ap.getType()) else any() ) } @@ -3783,7 +3790,7 @@ private module FlowExploration { PartialPathNodeFwd mid, Node node, CallContext cc, TSummaryCtx1 sc1, TSummaryCtx2 sc2, PartialAccessPath ap, Configuration config ) { - not isUnreachableInCall(node, cc.(CallContextSpecificCall).getCall()) and + not isUnreachableInCallCached(node, cc.(CallContextSpecificCall).getCall()) and ( localFlowStep(mid.getNode(), node, config) and cc = mid.getCallContext() and @@ -3797,7 +3804,7 @@ private module FlowExploration { sc1 = mid.getSummaryCtx1() and sc2 = mid.getSummaryCtx2() and mid.getAp() instanceof PartialAccessPathNil and - ap = TPartialNil(getNodeType(node)) and + ap = TPartialNil(getNodeDataFlowType(node)) and config = mid.getConfiguration() ) or @@ -3813,7 +3820,7 @@ private module FlowExploration { sc1 = TSummaryCtx1None() and sc2 = TSummaryCtx2None() and mid.getAp() instanceof PartialAccessPathNil and - ap = TPartialNil(getNodeType(node)) and + ap = TPartialNil(getNodeDataFlowType(node)) and config = mid.getConfiguration() or partialPathStoreStep(mid, _, _, node, ap) and @@ -3827,7 +3834,7 @@ private module FlowExploration { sc1 = mid.getSummaryCtx1() and sc2 = mid.getSummaryCtx2() and apConsFwd(ap, tc, ap0, config) and - compatibleTypes(ap.getType(), getNodeType(node)) + compatibleTypes(ap.getType(), getNodeDataFlowType(node)) ) or partialPathIntoCallable(mid, node, _, cc, sc1, sc2, _, ap, config) @@ -3924,7 +3931,7 @@ private module FlowExploration { PartialPathNodeFwd mid, int i, CallContext cc, DataFlowCall call, PartialAccessPath ap, Configuration config ) { - exists(ArgumentNode arg | + exists(ArgumentNodeExt arg | arg = mid.getNode() and cc = mid.getCallContext() and arg.argumentOf(call, i) and @@ -3943,7 +3950,7 @@ private module FlowExploration { } private predicate partialPathIntoCallable( - PartialPathNodeFwd mid, ParameterNode p, CallContext outercc, CallContextCall innercc, + PartialPathNodeFwd mid, ParameterNodeExt p, CallContext outercc, CallContextCall innercc, TSummaryCtx1 sc1, TSummaryCtx2 sc2, DataFlowCall call, PartialAccessPath ap, Configuration config ) { @@ -3980,7 +3987,7 @@ private module FlowExploration { DataFlowCall call, PartialPathNodeFwd mid, ReturnKindExt kind, CallContext cc, PartialAccessPath ap, Configuration config ) { - exists(ParameterNode p, CallContext innercc, TSummaryCtx1 sc1, TSummaryCtx2 sc2 | + exists(ParameterNodeExt p, CallContext innercc, TSummaryCtx1 sc1, TSummaryCtx2 sc2 | partialPathIntoCallable(mid, p, cc, innercc, sc1, sc2, call, _, config) and paramFlowsThroughInPartialPath(kind, innercc, sc1, sc2, ap, config) ) @@ -4037,7 +4044,7 @@ private module FlowExploration { apConsRev(ap, c, ap0, config) ) or - exists(ParameterNode p | + exists(ParameterNodeExt p | mid.getNode() = p and viableParamArg(_, p, node) and sc1 = mid.getSummaryCtx1() and @@ -4115,7 +4122,7 @@ private module FlowExploration { int pos, TRevSummaryCtx1Some sc1, TRevSummaryCtx2Some sc2, RevPartialAccessPath ap, Configuration config ) { - exists(PartialPathNodeRev mid, ParameterNode p | + exists(PartialPathNodeRev mid, ParameterNodeExt p | mid.getNode() = p and p.isParameterOf(_, pos) and sc1 = mid.getSummaryCtx1() and @@ -4138,7 +4145,7 @@ private module FlowExploration { pragma[nomagic] private predicate revPartialPathThroughCallable( - PartialPathNodeRev mid, ArgumentNode node, RevPartialAccessPath ap, Configuration config + PartialPathNodeRev mid, ArgumentNodeExt node, RevPartialAccessPath ap, Configuration config ) { exists(DataFlowCall call, int pos | revPartialPathThroughCallable0(call, mid, pos, ap, config) and 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 9498e51e7e6..cf83530bcbd 100644 --- a/csharp/ql/src/semmle/code/csharp/dataflow/internal/DataFlowImpl3.qll +++ b/csharp/ql/src/semmle/code/csharp/dataflow/internal/DataFlowImpl3.qll @@ -211,10 +211,7 @@ private predicate fullBarrier(Node node, Configuration config) { * Holds if data can flow in one local step from `node1` to `node2`. */ private predicate localFlowStep(Node node1, Node node2, Configuration config) { - ( - simpleLocalFlowStep(node1, node2) or - reverseStepThroughInputOutputAlias(node1, node2) - ) and + simpleLocalFlowStepExt(node1, node2) and not outBarrier(node1, config) and not inBarrier(node2, config) and not fullBarrier(node1, config) and @@ -237,7 +234,7 @@ private predicate additionalLocalFlowStep(Node node1, Node node2, Configuration * Holds if data can flow from `node1` to `node2` in a way that discards call contexts. */ private predicate jumpStep(Node node1, Node node2, Configuration config) { - jumpStep(node1, node2) and + jumpStepCached(node1, node2) and not outBarrier(node1, config) and not inBarrier(node2, config) and not fullBarrier(node1, config) and @@ -388,7 +385,7 @@ private module Stage1 { */ pragma[nomagic] private predicate fwdFlowIsEntered(DataFlowCall call, Cc cc, Configuration config) { - exists(ArgumentNode arg | + exists(ArgumentNodeExt arg | fwdFlow(arg, cc, config) and viableParamArg(call, _, arg) ) @@ -515,7 +512,7 @@ private module Stage1 { pragma[nomagic] predicate viableParamArgNodeCandFwd1( - DataFlowCall call, ParameterNode p, ArgumentNode arg, Configuration config + DataFlowCall call, ParameterNodeExt p, ArgumentNodeExt arg, Configuration config ) { viableParamArg(call, p, arg) and fwdFlow(arg, config) @@ -523,16 +520,16 @@ private module Stage1 { pragma[nomagic] private predicate revFlowIn( - DataFlowCall call, ArgumentNode arg, boolean toReturn, Configuration config + DataFlowCall call, ArgumentNodeExt arg, boolean toReturn, Configuration config ) { - exists(ParameterNode p | + exists(ParameterNodeExt p | revFlow(p, toReturn, config) and viableParamArgNodeCandFwd1(call, p, arg, config) ) } pragma[nomagic] - private predicate revFlowInToReturn(DataFlowCall call, ArgumentNode arg, Configuration config) { + private predicate revFlowInToReturn(DataFlowCall call, ArgumentNodeExt arg, Configuration config) { revFlowIn(call, arg, true, config) } @@ -597,7 +594,9 @@ private module Stage1 { * Holds if flow may enter through `p` and reach a return node making `p` a * candidate for the origin of a summary. */ - predicate parameterMayFlowThrough(ParameterNode p, DataFlowCallable c, Ap ap, Configuration config) { + predicate parameterMayFlowThrough( + ParameterNodeExt p, DataFlowCallable c, Ap ap, Configuration config + ) { exists(ReturnKindExt kind | throughFlowNodeCand(p, config) and returnFlowCallableNodeCand(c, kind, config) and @@ -663,7 +662,7 @@ private predicate flowOutOfCallNodeCand1( pragma[nomagic] private predicate viableParamArgNodeCand1( - DataFlowCall call, ParameterNode p, ArgumentNode arg, Configuration config + DataFlowCall call, ParameterNodeExt p, ArgumentNodeExt arg, Configuration config ) { Stage1::viableParamArgNodeCandFwd1(call, p, arg, config) and Stage1::revFlow(arg, config) @@ -675,7 +674,7 @@ private predicate viableParamArgNodeCand1( */ pragma[nomagic] private predicate flowIntoCallNodeCand1( - DataFlowCall call, ArgumentNode arg, ParameterNode p, Configuration config + DataFlowCall call, ArgumentNodeExt arg, ParameterNodeExt p, Configuration config ) { viableParamArgNodeCand1(call, p, arg, config) and Stage1::revFlow(p, config) and @@ -735,7 +734,7 @@ private predicate flowOutOfCallNodeCand1( */ pragma[nomagic] private predicate flowIntoCallNodeCand1( - DataFlowCall call, ArgumentNode arg, ParameterNode p, boolean allowsFieldFlow, + DataFlowCall call, ArgumentNodeExt arg, ParameterNodeExt p, boolean allowsFieldFlow, Configuration config ) { flowIntoCallNodeCand1(call, arg, p, config) and @@ -944,10 +943,10 @@ private module Stage2 { pragma[nomagic] private predicate fwdFlowIn( - DataFlowCall call, ParameterNode p, Cc outercc, Cc innercc, ApOption argAp, Ap ap, + DataFlowCall call, ParameterNodeExt p, Cc outercc, Cc innercc, ApOption argAp, Ap ap, Configuration config ) { - exists(ArgumentNode arg, boolean allowsFieldFlow | + exists(ArgumentNodeExt arg, boolean allowsFieldFlow | fwdFlow(arg, outercc, argAp, ap, config) and flowIntoCall(call, arg, p, allowsFieldFlow, config) and innercc = getCallContextCall(call, getNodeEnclosingCallable(p), outercc) @@ -992,7 +991,7 @@ private module Stage2 { private predicate fwdFlowIsEntered( DataFlowCall call, Cc cc, ApOption argAp, Ap ap, Configuration config ) { - exists(ParameterNode p | + exists(ParameterNodeExt p | fwdFlowIn(call, p, cc, _, argAp, ap, config) and PrevStage::parameterMayFlowThrough(p, _, getApprox(ap), config) ) @@ -1133,10 +1132,10 @@ private module Stage2 { pragma[nomagic] private predicate revFlowIn( - DataFlowCall call, ArgumentNode arg, boolean toReturn, ApOption returnAp, Ap ap, + DataFlowCall call, ArgumentNodeExt arg, boolean toReturn, ApOption returnAp, Ap ap, Configuration config ) { - exists(ParameterNode p, boolean allowsFieldFlow | + exists(ParameterNodeExt p, boolean allowsFieldFlow | revFlow(p, toReturn, returnAp, ap, config) and flowIntoCall(call, arg, p, allowsFieldFlow, config) | @@ -1146,7 +1145,7 @@ private module Stage2 { pragma[nomagic] private predicate revFlowInToReturn( - DataFlowCall call, ArgumentNode arg, Ap returnAp, Ap ap, Configuration config + DataFlowCall call, ArgumentNodeExt arg, Ap returnAp, Ap ap, Configuration config ) { revFlowIn(call, arg, true, apSome(returnAp), ap, config) } @@ -1199,13 +1198,15 @@ private module Stage2 { pragma[noinline] private predicate parameterFlow( - ParameterNode p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config + ParameterNodeExt p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config ) { revFlow(p, true, apSome(ap0), ap, config) and c = getNodeEnclosingCallable(p) } - predicate parameterMayFlowThrough(ParameterNode p, DataFlowCallable c, Ap ap, Configuration config) { + predicate parameterMayFlowThrough( + ParameterNodeExt p, DataFlowCallable c, Ap ap, Configuration config + ) { exists(ReturnNodeExt ret, Ap ap0, ReturnKindExt kind, int pos | parameterFlow(p, ap, ap0, c, config) and c = getNodeEnclosingCallable(ret) and @@ -1245,7 +1246,7 @@ private predicate flowOutOfCallNodeCand2( pragma[nomagic] private predicate flowIntoCallNodeCand2( - DataFlowCall call, ArgumentNode node1, ParameterNode node2, boolean allowsFieldFlow, + DataFlowCall call, ArgumentNodeExt node1, ParameterNodeExt node2, boolean allowsFieldFlow, Configuration config ) { flowIntoCallNodeCand1(call, node1, node2, allowsFieldFlow, config) and @@ -1260,8 +1261,8 @@ private module LocalFlowBigStep { */ private class FlowCheckNode extends Node { FlowCheckNode() { - this instanceof CastNode or - clearsContent(this, _) + castNode(this) or + clearsContentCached(this, _) } } @@ -1275,7 +1276,7 @@ private module LocalFlowBigStep { config.isSource(node) or jumpStep(_, node, config) or additionalJumpStep(_, node, config) or - node instanceof ParameterNode or + node instanceof ParameterNodeExt or node instanceof OutNodeExt or store(_, _, node, _) or read(_, _, node) or @@ -1321,21 +1322,21 @@ private module LocalFlowBigStep { Node node1, Node node2, boolean preservesValue, DataFlowType t, Configuration config, LocalCallContext cc ) { - not isUnreachableInCall(node2, cc.(LocalCallContextSpecificCall).getCall()) and + not isUnreachableInCallCached(node2, cc.(LocalCallContextSpecificCall).getCall()) and ( localFlowEntry(node1, pragma[only_bind_into](config)) and ( localFlowStepNodeCand1(node1, node2, config) and preservesValue = true and - t = getNodeType(node1) + t = getNodeDataFlowType(node1) or additionalLocalFlowStepNodeCand2(node1, node2, config) and preservesValue = false and - t = getNodeType(node2) + t = getNodeDataFlowType(node2) ) and node1 != node2 and cc.relevantFor(getNodeEnclosingCallable(node1)) and - not isUnreachableInCall(node1, cc.(LocalCallContextSpecificCall).getCall()) and + not isUnreachableInCallCached(node1, cc.(LocalCallContextSpecificCall).getCall()) and Stage2::revFlow(node2, pragma[only_bind_into](config)) or exists(Node mid | @@ -1350,7 +1351,7 @@ private module LocalFlowBigStep { additionalLocalFlowStepNodeCand2(mid, node2, config) and not mid instanceof FlowCheckNode and preservesValue = false and - t = getNodeType(node2) and + t = getNodeDataFlowType(node2) and Stage2::revFlow(node2, pragma[only_bind_into](config)) ) ) @@ -1384,7 +1385,7 @@ private module Stage3 { private ApApprox getApprox(Ap ap) { result = ap.toBoolNonEmpty() } private ApNil getApNil(Node node) { - PrevStage::revFlow(node, _) and result = TFrontNil(getNodeType(node)) + PrevStage::revFlow(node, _) and result = TFrontNil(getNodeDataFlowType(node)) } bindingset[tc, tail] @@ -1443,7 +1444,9 @@ private module Stage3 { bindingset[node, ap] private predicate filter(Node node, Ap ap) { not ap.isClearedAt(node) and - if node instanceof CastingNode then compatibleTypes(getNodeType(node), ap.getType()) else any() + if node instanceof CastingNode + then compatibleTypes(getNodeDataFlowType(node), ap.getType()) + else any() } bindingset[ap, contentType] @@ -1583,10 +1586,10 @@ private module Stage3 { pragma[nomagic] private predicate fwdFlowIn( - DataFlowCall call, ParameterNode p, Cc outercc, Cc innercc, ApOption argAp, Ap ap, + DataFlowCall call, ParameterNodeExt p, Cc outercc, Cc innercc, ApOption argAp, Ap ap, Configuration config ) { - exists(ArgumentNode arg, boolean allowsFieldFlow | + exists(ArgumentNodeExt arg, boolean allowsFieldFlow | fwdFlow(arg, outercc, argAp, ap, config) and flowIntoCall(call, arg, p, allowsFieldFlow, config) and innercc = getCallContextCall(call, getNodeEnclosingCallable(p), outercc) @@ -1631,7 +1634,7 @@ private module Stage3 { private predicate fwdFlowIsEntered( DataFlowCall call, Cc cc, ApOption argAp, Ap ap, Configuration config ) { - exists(ParameterNode p | + exists(ParameterNodeExt p | fwdFlowIn(call, p, cc, _, argAp, ap, config) and PrevStage::parameterMayFlowThrough(p, _, unbindApa(getApprox(ap)), config) ) @@ -1772,10 +1775,10 @@ private module Stage3 { pragma[nomagic] private predicate revFlowIn( - DataFlowCall call, ArgumentNode arg, boolean toReturn, ApOption returnAp, Ap ap, + DataFlowCall call, ArgumentNodeExt arg, boolean toReturn, ApOption returnAp, Ap ap, Configuration config ) { - exists(ParameterNode p, boolean allowsFieldFlow | + exists(ParameterNodeExt p, boolean allowsFieldFlow | revFlow(p, toReturn, returnAp, ap, config) and flowIntoCall(call, arg, p, allowsFieldFlow, config) | @@ -1785,7 +1788,7 @@ private module Stage3 { pragma[nomagic] private predicate revFlowInToReturn( - DataFlowCall call, ArgumentNode arg, Ap returnAp, Ap ap, Configuration config + DataFlowCall call, ArgumentNodeExt arg, Ap returnAp, Ap ap, Configuration config ) { revFlowIn(call, arg, true, apSome(returnAp), ap, config) } @@ -1838,13 +1841,15 @@ private module Stage3 { pragma[noinline] private predicate parameterFlow( - ParameterNode p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config + ParameterNodeExt p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config ) { revFlow(p, true, apSome(ap0), ap, config) and c = getNodeEnclosingCallable(p) } - predicate parameterMayFlowThrough(ParameterNode p, DataFlowCallable c, Ap ap, Configuration config) { + predicate parameterMayFlowThrough( + ParameterNodeExt p, DataFlowCallable c, Ap ap, Configuration config + ) { exists(ReturnNodeExt ret, Ap ap0, ReturnKindExt kind, int pos | parameterFlow(p, ap, ap0, c, config) and c = getNodeEnclosingCallable(ret) and @@ -2088,7 +2093,7 @@ private module Stage4 { private ApApprox getApprox(Ap ap) { result = ap.getFront() } private ApNil getApNil(Node node) { - PrevStage::revFlow(node, _) and result = TNil(getNodeType(node)) + PrevStage::revFlow(node, _) and result = TNil(getNodeDataFlowType(node)) } bindingset[tc, tail] @@ -2155,7 +2160,7 @@ private module Stage4 { pragma[nomagic] private predicate flowIntoCall( - DataFlowCall call, ArgumentNode node1, ParameterNode node2, boolean allowsFieldFlow, + DataFlowCall call, ArgumentNodeExt node1, ParameterNodeExt node2, boolean allowsFieldFlow, Configuration config ) { flowIntoCallNodeCand2(call, node1, node2, allowsFieldFlow, config) and @@ -2300,10 +2305,10 @@ private module Stage4 { pragma[nomagic] private predicate fwdFlowIn( - DataFlowCall call, ParameterNode p, Cc outercc, Cc innercc, ApOption argAp, Ap ap, + DataFlowCall call, ParameterNodeExt p, Cc outercc, Cc innercc, ApOption argAp, Ap ap, Configuration config ) { - exists(ArgumentNode arg, boolean allowsFieldFlow | + exists(ArgumentNodeExt arg, boolean allowsFieldFlow | fwdFlow(arg, outercc, argAp, ap, config) and flowIntoCall(call, arg, p, allowsFieldFlow, config) and innercc = getCallContextCall(call, getNodeEnclosingCallable(p), outercc) @@ -2348,7 +2353,7 @@ private module Stage4 { private predicate fwdFlowIsEntered( DataFlowCall call, Cc cc, ApOption argAp, Ap ap, Configuration config ) { - exists(ParameterNode p | + exists(ParameterNodeExt p | fwdFlowIn(call, p, cc, _, argAp, ap, config) and PrevStage::parameterMayFlowThrough(p, _, unbindApa(getApprox(ap)), config) ) @@ -2489,10 +2494,10 @@ private module Stage4 { pragma[nomagic] private predicate revFlowIn( - DataFlowCall call, ArgumentNode arg, boolean toReturn, ApOption returnAp, Ap ap, + DataFlowCall call, ArgumentNodeExt arg, boolean toReturn, ApOption returnAp, Ap ap, Configuration config ) { - exists(ParameterNode p, boolean allowsFieldFlow | + exists(ParameterNodeExt p, boolean allowsFieldFlow | revFlow(p, toReturn, returnAp, ap, config) and flowIntoCall(call, arg, p, allowsFieldFlow, config) | @@ -2502,7 +2507,7 @@ private module Stage4 { pragma[nomagic] private predicate revFlowInToReturn( - DataFlowCall call, ArgumentNode arg, Ap returnAp, Ap ap, Configuration config + DataFlowCall call, ArgumentNodeExt arg, Ap returnAp, Ap ap, Configuration config ) { revFlowIn(call, arg, true, apSome(returnAp), ap, config) } @@ -2555,13 +2560,15 @@ private module Stage4 { pragma[noinline] private predicate parameterFlow( - ParameterNode p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config + ParameterNodeExt p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config ) { revFlow(p, true, apSome(ap0), ap, config) and c = getNodeEnclosingCallable(p) } - predicate parameterMayFlowThrough(ParameterNode p, DataFlowCallable c, Ap ap, Configuration config) { + predicate parameterMayFlowThrough( + ParameterNodeExt p, DataFlowCallable c, Ap ap, Configuration config + ) { exists(ReturnNodeExt ret, Ap ap0, ReturnKindExt kind, int pos | parameterFlow(p, ap, ap0, c, config) and c = getNodeEnclosingCallable(ret) and @@ -2606,7 +2613,7 @@ private predicate nodeMayUseSummary(Node n, AccessPathApprox apa, Configuration private newtype TSummaryCtx = TSummaryCtxNone() or - TSummaryCtxSome(ParameterNode p, AccessPath ap) { + TSummaryCtxSome(ParameterNodeExt p, AccessPath ap) { Stage4::parameterMayFlowThrough(p, _, ap.getApprox(), _) } @@ -2627,7 +2634,7 @@ private class SummaryCtxNone extends SummaryCtx, TSummaryCtxNone { /** A summary context from which a flow summary can be generated. */ private class SummaryCtxSome extends SummaryCtx, TSummaryCtxSome { - private ParameterNode p; + private ParameterNodeExt p; private AccessPath ap; SummaryCtxSome() { this = TSummaryCtxSome(p, ap) } @@ -2758,7 +2765,7 @@ private newtype TPathNode = config.isSource(node) and cc instanceof CallContextAny and sc instanceof SummaryCtxNone and - ap = TAccessPathNil(getNodeType(node)) + ap = TAccessPathNil(getNodeDataFlowType(node)) or // ... or a step from an existing PathNode to another node. exists(PathNodeMid mid | @@ -2979,7 +2986,7 @@ class PathNode extends TPathNode { Configuration getConfiguration() { none() } private predicate isHidden() { - nodeIsHidden(this.getNode()) and + hiddenNode(this.getNode()) and not this.isSource() and not this instanceof PathNodeSink } @@ -3148,7 +3155,7 @@ private predicate pathStep(PathNodeMid mid, Node node, CallContext cc, SummaryCt cc instanceof CallContextAny and sc instanceof SummaryCtxNone and mid.getAp() instanceof AccessPathNil and - ap = TAccessPathNil(getNodeType(node)) + ap = TAccessPathNil(getNodeDataFlowType(node)) or exists(TypedContent tc | pathStoreStep(mid, node, ap.pop(tc), tc, cc)) and sc = mid.getSummaryCtx() @@ -3235,7 +3242,7 @@ pragma[noinline] private predicate pathIntoArg( PathNodeMid mid, int i, CallContext cc, DataFlowCall call, AccessPath ap, AccessPathApprox apa ) { - exists(ArgumentNode arg | + exists(ArgumentNodeExt arg | arg = mid.getNode() and cc = mid.getCallContext() and arg.argumentOf(call, i) and @@ -3248,7 +3255,7 @@ pragma[noinline] private predicate parameterCand( DataFlowCallable callable, int i, AccessPathApprox apa, Configuration config ) { - exists(ParameterNode p | + exists(ParameterNodeExt p | Stage4::revFlow(p, _, _, apa, config) and p.isParameterOf(callable, i) ) @@ -3272,7 +3279,7 @@ private predicate pathIntoCallable0( * respectively. */ private predicate pathIntoCallable( - PathNodeMid mid, ParameterNode p, CallContext outercc, CallContextCall innercc, SummaryCtx sc, + PathNodeMid mid, ParameterNodeExt p, CallContext outercc, CallContextCall innercc, SummaryCtx sc, DataFlowCall call ) { exists(int i, DataFlowCallable callable, AccessPath ap | @@ -3568,7 +3575,7 @@ private module FlowExploration { private newtype TSummaryCtx1 = TSummaryCtx1None() or - TSummaryCtx1Param(ParameterNode p) + TSummaryCtx1Param(ParameterNodeExt p) private newtype TSummaryCtx2 = TSummaryCtx2None() or @@ -3591,7 +3598,7 @@ private module FlowExploration { cc instanceof CallContextAny and sc1 = TSummaryCtx1None() and sc2 = TSummaryCtx2None() and - ap = TPartialNil(getNodeType(node)) and + ap = TPartialNil(getNodeDataFlowType(node)) and not fullBarrier(node, config) and exists(config.explorationLimit()) or @@ -3611,7 +3618,7 @@ private module FlowExploration { or exists(PartialPathNodeRev mid | revPartialPathStep(mid, node, sc1, sc2, ap, config) and - not clearsContent(node, ap.getHead()) and + not clearsContentCached(node, ap.getHead()) and not fullBarrier(node, config) and distSink(getNodeEnclosingCallable(node), config) <= config.explorationLimit() ) @@ -3625,9 +3632,9 @@ private module FlowExploration { exists(PartialPathNodeFwd mid | partialPathStep(mid, node, cc, sc1, sc2, ap, config) and not fullBarrier(node, config) and - not clearsContent(node, ap.getHead().getContent()) and + not clearsContentCached(node, ap.getHead().getContent()) and if node instanceof CastingNode - then compatibleTypes(getNodeType(node), ap.getType()) + then compatibleTypes(getNodeDataFlowType(node), ap.getType()) else any() ) } @@ -3783,7 +3790,7 @@ private module FlowExploration { PartialPathNodeFwd mid, Node node, CallContext cc, TSummaryCtx1 sc1, TSummaryCtx2 sc2, PartialAccessPath ap, Configuration config ) { - not isUnreachableInCall(node, cc.(CallContextSpecificCall).getCall()) and + not isUnreachableInCallCached(node, cc.(CallContextSpecificCall).getCall()) and ( localFlowStep(mid.getNode(), node, config) and cc = mid.getCallContext() and @@ -3797,7 +3804,7 @@ private module FlowExploration { sc1 = mid.getSummaryCtx1() and sc2 = mid.getSummaryCtx2() and mid.getAp() instanceof PartialAccessPathNil and - ap = TPartialNil(getNodeType(node)) and + ap = TPartialNil(getNodeDataFlowType(node)) and config = mid.getConfiguration() ) or @@ -3813,7 +3820,7 @@ private module FlowExploration { sc1 = TSummaryCtx1None() and sc2 = TSummaryCtx2None() and mid.getAp() instanceof PartialAccessPathNil and - ap = TPartialNil(getNodeType(node)) and + ap = TPartialNil(getNodeDataFlowType(node)) and config = mid.getConfiguration() or partialPathStoreStep(mid, _, _, node, ap) and @@ -3827,7 +3834,7 @@ private module FlowExploration { sc1 = mid.getSummaryCtx1() and sc2 = mid.getSummaryCtx2() and apConsFwd(ap, tc, ap0, config) and - compatibleTypes(ap.getType(), getNodeType(node)) + compatibleTypes(ap.getType(), getNodeDataFlowType(node)) ) or partialPathIntoCallable(mid, node, _, cc, sc1, sc2, _, ap, config) @@ -3924,7 +3931,7 @@ private module FlowExploration { PartialPathNodeFwd mid, int i, CallContext cc, DataFlowCall call, PartialAccessPath ap, Configuration config ) { - exists(ArgumentNode arg | + exists(ArgumentNodeExt arg | arg = mid.getNode() and cc = mid.getCallContext() and arg.argumentOf(call, i) and @@ -3943,7 +3950,7 @@ private module FlowExploration { } private predicate partialPathIntoCallable( - PartialPathNodeFwd mid, ParameterNode p, CallContext outercc, CallContextCall innercc, + PartialPathNodeFwd mid, ParameterNodeExt p, CallContext outercc, CallContextCall innercc, TSummaryCtx1 sc1, TSummaryCtx2 sc2, DataFlowCall call, PartialAccessPath ap, Configuration config ) { @@ -3980,7 +3987,7 @@ private module FlowExploration { DataFlowCall call, PartialPathNodeFwd mid, ReturnKindExt kind, CallContext cc, PartialAccessPath ap, Configuration config ) { - exists(ParameterNode p, CallContext innercc, TSummaryCtx1 sc1, TSummaryCtx2 sc2 | + exists(ParameterNodeExt p, CallContext innercc, TSummaryCtx1 sc1, TSummaryCtx2 sc2 | partialPathIntoCallable(mid, p, cc, innercc, sc1, sc2, call, _, config) and paramFlowsThroughInPartialPath(kind, innercc, sc1, sc2, ap, config) ) @@ -4037,7 +4044,7 @@ private module FlowExploration { apConsRev(ap, c, ap0, config) ) or - exists(ParameterNode p | + exists(ParameterNodeExt p | mid.getNode() = p and viableParamArg(_, p, node) and sc1 = mid.getSummaryCtx1() and @@ -4115,7 +4122,7 @@ private module FlowExploration { int pos, TRevSummaryCtx1Some sc1, TRevSummaryCtx2Some sc2, RevPartialAccessPath ap, Configuration config ) { - exists(PartialPathNodeRev mid, ParameterNode p | + exists(PartialPathNodeRev mid, ParameterNodeExt p | mid.getNode() = p and p.isParameterOf(_, pos) and sc1 = mid.getSummaryCtx1() and @@ -4138,7 +4145,7 @@ private module FlowExploration { pragma[nomagic] private predicate revPartialPathThroughCallable( - PartialPathNodeRev mid, ArgumentNode node, RevPartialAccessPath ap, Configuration config + PartialPathNodeRev mid, ArgumentNodeExt node, RevPartialAccessPath ap, Configuration config ) { exists(DataFlowCall call, int pos | revPartialPathThroughCallable0(call, mid, pos, ap, config) and 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 9498e51e7e6..cf83530bcbd 100644 --- a/csharp/ql/src/semmle/code/csharp/dataflow/internal/DataFlowImpl4.qll +++ b/csharp/ql/src/semmle/code/csharp/dataflow/internal/DataFlowImpl4.qll @@ -211,10 +211,7 @@ private predicate fullBarrier(Node node, Configuration config) { * Holds if data can flow in one local step from `node1` to `node2`. */ private predicate localFlowStep(Node node1, Node node2, Configuration config) { - ( - simpleLocalFlowStep(node1, node2) or - reverseStepThroughInputOutputAlias(node1, node2) - ) and + simpleLocalFlowStepExt(node1, node2) and not outBarrier(node1, config) and not inBarrier(node2, config) and not fullBarrier(node1, config) and @@ -237,7 +234,7 @@ private predicate additionalLocalFlowStep(Node node1, Node node2, Configuration * Holds if data can flow from `node1` to `node2` in a way that discards call contexts. */ private predicate jumpStep(Node node1, Node node2, Configuration config) { - jumpStep(node1, node2) and + jumpStepCached(node1, node2) and not outBarrier(node1, config) and not inBarrier(node2, config) and not fullBarrier(node1, config) and @@ -388,7 +385,7 @@ private module Stage1 { */ pragma[nomagic] private predicate fwdFlowIsEntered(DataFlowCall call, Cc cc, Configuration config) { - exists(ArgumentNode arg | + exists(ArgumentNodeExt arg | fwdFlow(arg, cc, config) and viableParamArg(call, _, arg) ) @@ -515,7 +512,7 @@ private module Stage1 { pragma[nomagic] predicate viableParamArgNodeCandFwd1( - DataFlowCall call, ParameterNode p, ArgumentNode arg, Configuration config + DataFlowCall call, ParameterNodeExt p, ArgumentNodeExt arg, Configuration config ) { viableParamArg(call, p, arg) and fwdFlow(arg, config) @@ -523,16 +520,16 @@ private module Stage1 { pragma[nomagic] private predicate revFlowIn( - DataFlowCall call, ArgumentNode arg, boolean toReturn, Configuration config + DataFlowCall call, ArgumentNodeExt arg, boolean toReturn, Configuration config ) { - exists(ParameterNode p | + exists(ParameterNodeExt p | revFlow(p, toReturn, config) and viableParamArgNodeCandFwd1(call, p, arg, config) ) } pragma[nomagic] - private predicate revFlowInToReturn(DataFlowCall call, ArgumentNode arg, Configuration config) { + private predicate revFlowInToReturn(DataFlowCall call, ArgumentNodeExt arg, Configuration config) { revFlowIn(call, arg, true, config) } @@ -597,7 +594,9 @@ private module Stage1 { * Holds if flow may enter through `p` and reach a return node making `p` a * candidate for the origin of a summary. */ - predicate parameterMayFlowThrough(ParameterNode p, DataFlowCallable c, Ap ap, Configuration config) { + predicate parameterMayFlowThrough( + ParameterNodeExt p, DataFlowCallable c, Ap ap, Configuration config + ) { exists(ReturnKindExt kind | throughFlowNodeCand(p, config) and returnFlowCallableNodeCand(c, kind, config) and @@ -663,7 +662,7 @@ private predicate flowOutOfCallNodeCand1( pragma[nomagic] private predicate viableParamArgNodeCand1( - DataFlowCall call, ParameterNode p, ArgumentNode arg, Configuration config + DataFlowCall call, ParameterNodeExt p, ArgumentNodeExt arg, Configuration config ) { Stage1::viableParamArgNodeCandFwd1(call, p, arg, config) and Stage1::revFlow(arg, config) @@ -675,7 +674,7 @@ private predicate viableParamArgNodeCand1( */ pragma[nomagic] private predicate flowIntoCallNodeCand1( - DataFlowCall call, ArgumentNode arg, ParameterNode p, Configuration config + DataFlowCall call, ArgumentNodeExt arg, ParameterNodeExt p, Configuration config ) { viableParamArgNodeCand1(call, p, arg, config) and Stage1::revFlow(p, config) and @@ -735,7 +734,7 @@ private predicate flowOutOfCallNodeCand1( */ pragma[nomagic] private predicate flowIntoCallNodeCand1( - DataFlowCall call, ArgumentNode arg, ParameterNode p, boolean allowsFieldFlow, + DataFlowCall call, ArgumentNodeExt arg, ParameterNodeExt p, boolean allowsFieldFlow, Configuration config ) { flowIntoCallNodeCand1(call, arg, p, config) and @@ -944,10 +943,10 @@ private module Stage2 { pragma[nomagic] private predicate fwdFlowIn( - DataFlowCall call, ParameterNode p, Cc outercc, Cc innercc, ApOption argAp, Ap ap, + DataFlowCall call, ParameterNodeExt p, Cc outercc, Cc innercc, ApOption argAp, Ap ap, Configuration config ) { - exists(ArgumentNode arg, boolean allowsFieldFlow | + exists(ArgumentNodeExt arg, boolean allowsFieldFlow | fwdFlow(arg, outercc, argAp, ap, config) and flowIntoCall(call, arg, p, allowsFieldFlow, config) and innercc = getCallContextCall(call, getNodeEnclosingCallable(p), outercc) @@ -992,7 +991,7 @@ private module Stage2 { private predicate fwdFlowIsEntered( DataFlowCall call, Cc cc, ApOption argAp, Ap ap, Configuration config ) { - exists(ParameterNode p | + exists(ParameterNodeExt p | fwdFlowIn(call, p, cc, _, argAp, ap, config) and PrevStage::parameterMayFlowThrough(p, _, getApprox(ap), config) ) @@ -1133,10 +1132,10 @@ private module Stage2 { pragma[nomagic] private predicate revFlowIn( - DataFlowCall call, ArgumentNode arg, boolean toReturn, ApOption returnAp, Ap ap, + DataFlowCall call, ArgumentNodeExt arg, boolean toReturn, ApOption returnAp, Ap ap, Configuration config ) { - exists(ParameterNode p, boolean allowsFieldFlow | + exists(ParameterNodeExt p, boolean allowsFieldFlow | revFlow(p, toReturn, returnAp, ap, config) and flowIntoCall(call, arg, p, allowsFieldFlow, config) | @@ -1146,7 +1145,7 @@ private module Stage2 { pragma[nomagic] private predicate revFlowInToReturn( - DataFlowCall call, ArgumentNode arg, Ap returnAp, Ap ap, Configuration config + DataFlowCall call, ArgumentNodeExt arg, Ap returnAp, Ap ap, Configuration config ) { revFlowIn(call, arg, true, apSome(returnAp), ap, config) } @@ -1199,13 +1198,15 @@ private module Stage2 { pragma[noinline] private predicate parameterFlow( - ParameterNode p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config + ParameterNodeExt p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config ) { revFlow(p, true, apSome(ap0), ap, config) and c = getNodeEnclosingCallable(p) } - predicate parameterMayFlowThrough(ParameterNode p, DataFlowCallable c, Ap ap, Configuration config) { + predicate parameterMayFlowThrough( + ParameterNodeExt p, DataFlowCallable c, Ap ap, Configuration config + ) { exists(ReturnNodeExt ret, Ap ap0, ReturnKindExt kind, int pos | parameterFlow(p, ap, ap0, c, config) and c = getNodeEnclosingCallable(ret) and @@ -1245,7 +1246,7 @@ private predicate flowOutOfCallNodeCand2( pragma[nomagic] private predicate flowIntoCallNodeCand2( - DataFlowCall call, ArgumentNode node1, ParameterNode node2, boolean allowsFieldFlow, + DataFlowCall call, ArgumentNodeExt node1, ParameterNodeExt node2, boolean allowsFieldFlow, Configuration config ) { flowIntoCallNodeCand1(call, node1, node2, allowsFieldFlow, config) and @@ -1260,8 +1261,8 @@ private module LocalFlowBigStep { */ private class FlowCheckNode extends Node { FlowCheckNode() { - this instanceof CastNode or - clearsContent(this, _) + castNode(this) or + clearsContentCached(this, _) } } @@ -1275,7 +1276,7 @@ private module LocalFlowBigStep { config.isSource(node) or jumpStep(_, node, config) or additionalJumpStep(_, node, config) or - node instanceof ParameterNode or + node instanceof ParameterNodeExt or node instanceof OutNodeExt or store(_, _, node, _) or read(_, _, node) or @@ -1321,21 +1322,21 @@ private module LocalFlowBigStep { Node node1, Node node2, boolean preservesValue, DataFlowType t, Configuration config, LocalCallContext cc ) { - not isUnreachableInCall(node2, cc.(LocalCallContextSpecificCall).getCall()) and + not isUnreachableInCallCached(node2, cc.(LocalCallContextSpecificCall).getCall()) and ( localFlowEntry(node1, pragma[only_bind_into](config)) and ( localFlowStepNodeCand1(node1, node2, config) and preservesValue = true and - t = getNodeType(node1) + t = getNodeDataFlowType(node1) or additionalLocalFlowStepNodeCand2(node1, node2, config) and preservesValue = false and - t = getNodeType(node2) + t = getNodeDataFlowType(node2) ) and node1 != node2 and cc.relevantFor(getNodeEnclosingCallable(node1)) and - not isUnreachableInCall(node1, cc.(LocalCallContextSpecificCall).getCall()) and + not isUnreachableInCallCached(node1, cc.(LocalCallContextSpecificCall).getCall()) and Stage2::revFlow(node2, pragma[only_bind_into](config)) or exists(Node mid | @@ -1350,7 +1351,7 @@ private module LocalFlowBigStep { additionalLocalFlowStepNodeCand2(mid, node2, config) and not mid instanceof FlowCheckNode and preservesValue = false and - t = getNodeType(node2) and + t = getNodeDataFlowType(node2) and Stage2::revFlow(node2, pragma[only_bind_into](config)) ) ) @@ -1384,7 +1385,7 @@ private module Stage3 { private ApApprox getApprox(Ap ap) { result = ap.toBoolNonEmpty() } private ApNil getApNil(Node node) { - PrevStage::revFlow(node, _) and result = TFrontNil(getNodeType(node)) + PrevStage::revFlow(node, _) and result = TFrontNil(getNodeDataFlowType(node)) } bindingset[tc, tail] @@ -1443,7 +1444,9 @@ private module Stage3 { bindingset[node, ap] private predicate filter(Node node, Ap ap) { not ap.isClearedAt(node) and - if node instanceof CastingNode then compatibleTypes(getNodeType(node), ap.getType()) else any() + if node instanceof CastingNode + then compatibleTypes(getNodeDataFlowType(node), ap.getType()) + else any() } bindingset[ap, contentType] @@ -1583,10 +1586,10 @@ private module Stage3 { pragma[nomagic] private predicate fwdFlowIn( - DataFlowCall call, ParameterNode p, Cc outercc, Cc innercc, ApOption argAp, Ap ap, + DataFlowCall call, ParameterNodeExt p, Cc outercc, Cc innercc, ApOption argAp, Ap ap, Configuration config ) { - exists(ArgumentNode arg, boolean allowsFieldFlow | + exists(ArgumentNodeExt arg, boolean allowsFieldFlow | fwdFlow(arg, outercc, argAp, ap, config) and flowIntoCall(call, arg, p, allowsFieldFlow, config) and innercc = getCallContextCall(call, getNodeEnclosingCallable(p), outercc) @@ -1631,7 +1634,7 @@ private module Stage3 { private predicate fwdFlowIsEntered( DataFlowCall call, Cc cc, ApOption argAp, Ap ap, Configuration config ) { - exists(ParameterNode p | + exists(ParameterNodeExt p | fwdFlowIn(call, p, cc, _, argAp, ap, config) and PrevStage::parameterMayFlowThrough(p, _, unbindApa(getApprox(ap)), config) ) @@ -1772,10 +1775,10 @@ private module Stage3 { pragma[nomagic] private predicate revFlowIn( - DataFlowCall call, ArgumentNode arg, boolean toReturn, ApOption returnAp, Ap ap, + DataFlowCall call, ArgumentNodeExt arg, boolean toReturn, ApOption returnAp, Ap ap, Configuration config ) { - exists(ParameterNode p, boolean allowsFieldFlow | + exists(ParameterNodeExt p, boolean allowsFieldFlow | revFlow(p, toReturn, returnAp, ap, config) and flowIntoCall(call, arg, p, allowsFieldFlow, config) | @@ -1785,7 +1788,7 @@ private module Stage3 { pragma[nomagic] private predicate revFlowInToReturn( - DataFlowCall call, ArgumentNode arg, Ap returnAp, Ap ap, Configuration config + DataFlowCall call, ArgumentNodeExt arg, Ap returnAp, Ap ap, Configuration config ) { revFlowIn(call, arg, true, apSome(returnAp), ap, config) } @@ -1838,13 +1841,15 @@ private module Stage3 { pragma[noinline] private predicate parameterFlow( - ParameterNode p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config + ParameterNodeExt p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config ) { revFlow(p, true, apSome(ap0), ap, config) and c = getNodeEnclosingCallable(p) } - predicate parameterMayFlowThrough(ParameterNode p, DataFlowCallable c, Ap ap, Configuration config) { + predicate parameterMayFlowThrough( + ParameterNodeExt p, DataFlowCallable c, Ap ap, Configuration config + ) { exists(ReturnNodeExt ret, Ap ap0, ReturnKindExt kind, int pos | parameterFlow(p, ap, ap0, c, config) and c = getNodeEnclosingCallable(ret) and @@ -2088,7 +2093,7 @@ private module Stage4 { private ApApprox getApprox(Ap ap) { result = ap.getFront() } private ApNil getApNil(Node node) { - PrevStage::revFlow(node, _) and result = TNil(getNodeType(node)) + PrevStage::revFlow(node, _) and result = TNil(getNodeDataFlowType(node)) } bindingset[tc, tail] @@ -2155,7 +2160,7 @@ private module Stage4 { pragma[nomagic] private predicate flowIntoCall( - DataFlowCall call, ArgumentNode node1, ParameterNode node2, boolean allowsFieldFlow, + DataFlowCall call, ArgumentNodeExt node1, ParameterNodeExt node2, boolean allowsFieldFlow, Configuration config ) { flowIntoCallNodeCand2(call, node1, node2, allowsFieldFlow, config) and @@ -2300,10 +2305,10 @@ private module Stage4 { pragma[nomagic] private predicate fwdFlowIn( - DataFlowCall call, ParameterNode p, Cc outercc, Cc innercc, ApOption argAp, Ap ap, + DataFlowCall call, ParameterNodeExt p, Cc outercc, Cc innercc, ApOption argAp, Ap ap, Configuration config ) { - exists(ArgumentNode arg, boolean allowsFieldFlow | + exists(ArgumentNodeExt arg, boolean allowsFieldFlow | fwdFlow(arg, outercc, argAp, ap, config) and flowIntoCall(call, arg, p, allowsFieldFlow, config) and innercc = getCallContextCall(call, getNodeEnclosingCallable(p), outercc) @@ -2348,7 +2353,7 @@ private module Stage4 { private predicate fwdFlowIsEntered( DataFlowCall call, Cc cc, ApOption argAp, Ap ap, Configuration config ) { - exists(ParameterNode p | + exists(ParameterNodeExt p | fwdFlowIn(call, p, cc, _, argAp, ap, config) and PrevStage::parameterMayFlowThrough(p, _, unbindApa(getApprox(ap)), config) ) @@ -2489,10 +2494,10 @@ private module Stage4 { pragma[nomagic] private predicate revFlowIn( - DataFlowCall call, ArgumentNode arg, boolean toReturn, ApOption returnAp, Ap ap, + DataFlowCall call, ArgumentNodeExt arg, boolean toReturn, ApOption returnAp, Ap ap, Configuration config ) { - exists(ParameterNode p, boolean allowsFieldFlow | + exists(ParameterNodeExt p, boolean allowsFieldFlow | revFlow(p, toReturn, returnAp, ap, config) and flowIntoCall(call, arg, p, allowsFieldFlow, config) | @@ -2502,7 +2507,7 @@ private module Stage4 { pragma[nomagic] private predicate revFlowInToReturn( - DataFlowCall call, ArgumentNode arg, Ap returnAp, Ap ap, Configuration config + DataFlowCall call, ArgumentNodeExt arg, Ap returnAp, Ap ap, Configuration config ) { revFlowIn(call, arg, true, apSome(returnAp), ap, config) } @@ -2555,13 +2560,15 @@ private module Stage4 { pragma[noinline] private predicate parameterFlow( - ParameterNode p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config + ParameterNodeExt p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config ) { revFlow(p, true, apSome(ap0), ap, config) and c = getNodeEnclosingCallable(p) } - predicate parameterMayFlowThrough(ParameterNode p, DataFlowCallable c, Ap ap, Configuration config) { + predicate parameterMayFlowThrough( + ParameterNodeExt p, DataFlowCallable c, Ap ap, Configuration config + ) { exists(ReturnNodeExt ret, Ap ap0, ReturnKindExt kind, int pos | parameterFlow(p, ap, ap0, c, config) and c = getNodeEnclosingCallable(ret) and @@ -2606,7 +2613,7 @@ private predicate nodeMayUseSummary(Node n, AccessPathApprox apa, Configuration private newtype TSummaryCtx = TSummaryCtxNone() or - TSummaryCtxSome(ParameterNode p, AccessPath ap) { + TSummaryCtxSome(ParameterNodeExt p, AccessPath ap) { Stage4::parameterMayFlowThrough(p, _, ap.getApprox(), _) } @@ -2627,7 +2634,7 @@ private class SummaryCtxNone extends SummaryCtx, TSummaryCtxNone { /** A summary context from which a flow summary can be generated. */ private class SummaryCtxSome extends SummaryCtx, TSummaryCtxSome { - private ParameterNode p; + private ParameterNodeExt p; private AccessPath ap; SummaryCtxSome() { this = TSummaryCtxSome(p, ap) } @@ -2758,7 +2765,7 @@ private newtype TPathNode = config.isSource(node) and cc instanceof CallContextAny and sc instanceof SummaryCtxNone and - ap = TAccessPathNil(getNodeType(node)) + ap = TAccessPathNil(getNodeDataFlowType(node)) or // ... or a step from an existing PathNode to another node. exists(PathNodeMid mid | @@ -2979,7 +2986,7 @@ class PathNode extends TPathNode { Configuration getConfiguration() { none() } private predicate isHidden() { - nodeIsHidden(this.getNode()) and + hiddenNode(this.getNode()) and not this.isSource() and not this instanceof PathNodeSink } @@ -3148,7 +3155,7 @@ private predicate pathStep(PathNodeMid mid, Node node, CallContext cc, SummaryCt cc instanceof CallContextAny and sc instanceof SummaryCtxNone and mid.getAp() instanceof AccessPathNil and - ap = TAccessPathNil(getNodeType(node)) + ap = TAccessPathNil(getNodeDataFlowType(node)) or exists(TypedContent tc | pathStoreStep(mid, node, ap.pop(tc), tc, cc)) and sc = mid.getSummaryCtx() @@ -3235,7 +3242,7 @@ pragma[noinline] private predicate pathIntoArg( PathNodeMid mid, int i, CallContext cc, DataFlowCall call, AccessPath ap, AccessPathApprox apa ) { - exists(ArgumentNode arg | + exists(ArgumentNodeExt arg | arg = mid.getNode() and cc = mid.getCallContext() and arg.argumentOf(call, i) and @@ -3248,7 +3255,7 @@ pragma[noinline] private predicate parameterCand( DataFlowCallable callable, int i, AccessPathApprox apa, Configuration config ) { - exists(ParameterNode p | + exists(ParameterNodeExt p | Stage4::revFlow(p, _, _, apa, config) and p.isParameterOf(callable, i) ) @@ -3272,7 +3279,7 @@ private predicate pathIntoCallable0( * respectively. */ private predicate pathIntoCallable( - PathNodeMid mid, ParameterNode p, CallContext outercc, CallContextCall innercc, SummaryCtx sc, + PathNodeMid mid, ParameterNodeExt p, CallContext outercc, CallContextCall innercc, SummaryCtx sc, DataFlowCall call ) { exists(int i, DataFlowCallable callable, AccessPath ap | @@ -3568,7 +3575,7 @@ private module FlowExploration { private newtype TSummaryCtx1 = TSummaryCtx1None() or - TSummaryCtx1Param(ParameterNode p) + TSummaryCtx1Param(ParameterNodeExt p) private newtype TSummaryCtx2 = TSummaryCtx2None() or @@ -3591,7 +3598,7 @@ private module FlowExploration { cc instanceof CallContextAny and sc1 = TSummaryCtx1None() and sc2 = TSummaryCtx2None() and - ap = TPartialNil(getNodeType(node)) and + ap = TPartialNil(getNodeDataFlowType(node)) and not fullBarrier(node, config) and exists(config.explorationLimit()) or @@ -3611,7 +3618,7 @@ private module FlowExploration { or exists(PartialPathNodeRev mid | revPartialPathStep(mid, node, sc1, sc2, ap, config) and - not clearsContent(node, ap.getHead()) and + not clearsContentCached(node, ap.getHead()) and not fullBarrier(node, config) and distSink(getNodeEnclosingCallable(node), config) <= config.explorationLimit() ) @@ -3625,9 +3632,9 @@ private module FlowExploration { exists(PartialPathNodeFwd mid | partialPathStep(mid, node, cc, sc1, sc2, ap, config) and not fullBarrier(node, config) and - not clearsContent(node, ap.getHead().getContent()) and + not clearsContentCached(node, ap.getHead().getContent()) and if node instanceof CastingNode - then compatibleTypes(getNodeType(node), ap.getType()) + then compatibleTypes(getNodeDataFlowType(node), ap.getType()) else any() ) } @@ -3783,7 +3790,7 @@ private module FlowExploration { PartialPathNodeFwd mid, Node node, CallContext cc, TSummaryCtx1 sc1, TSummaryCtx2 sc2, PartialAccessPath ap, Configuration config ) { - not isUnreachableInCall(node, cc.(CallContextSpecificCall).getCall()) and + not isUnreachableInCallCached(node, cc.(CallContextSpecificCall).getCall()) and ( localFlowStep(mid.getNode(), node, config) and cc = mid.getCallContext() and @@ -3797,7 +3804,7 @@ private module FlowExploration { sc1 = mid.getSummaryCtx1() and sc2 = mid.getSummaryCtx2() and mid.getAp() instanceof PartialAccessPathNil and - ap = TPartialNil(getNodeType(node)) and + ap = TPartialNil(getNodeDataFlowType(node)) and config = mid.getConfiguration() ) or @@ -3813,7 +3820,7 @@ private module FlowExploration { sc1 = TSummaryCtx1None() and sc2 = TSummaryCtx2None() and mid.getAp() instanceof PartialAccessPathNil and - ap = TPartialNil(getNodeType(node)) and + ap = TPartialNil(getNodeDataFlowType(node)) and config = mid.getConfiguration() or partialPathStoreStep(mid, _, _, node, ap) and @@ -3827,7 +3834,7 @@ private module FlowExploration { sc1 = mid.getSummaryCtx1() and sc2 = mid.getSummaryCtx2() and apConsFwd(ap, tc, ap0, config) and - compatibleTypes(ap.getType(), getNodeType(node)) + compatibleTypes(ap.getType(), getNodeDataFlowType(node)) ) or partialPathIntoCallable(mid, node, _, cc, sc1, sc2, _, ap, config) @@ -3924,7 +3931,7 @@ private module FlowExploration { PartialPathNodeFwd mid, int i, CallContext cc, DataFlowCall call, PartialAccessPath ap, Configuration config ) { - exists(ArgumentNode arg | + exists(ArgumentNodeExt arg | arg = mid.getNode() and cc = mid.getCallContext() and arg.argumentOf(call, i) and @@ -3943,7 +3950,7 @@ private module FlowExploration { } private predicate partialPathIntoCallable( - PartialPathNodeFwd mid, ParameterNode p, CallContext outercc, CallContextCall innercc, + PartialPathNodeFwd mid, ParameterNodeExt p, CallContext outercc, CallContextCall innercc, TSummaryCtx1 sc1, TSummaryCtx2 sc2, DataFlowCall call, PartialAccessPath ap, Configuration config ) { @@ -3980,7 +3987,7 @@ private module FlowExploration { DataFlowCall call, PartialPathNodeFwd mid, ReturnKindExt kind, CallContext cc, PartialAccessPath ap, Configuration config ) { - exists(ParameterNode p, CallContext innercc, TSummaryCtx1 sc1, TSummaryCtx2 sc2 | + exists(ParameterNodeExt p, CallContext innercc, TSummaryCtx1 sc1, TSummaryCtx2 sc2 | partialPathIntoCallable(mid, p, cc, innercc, sc1, sc2, call, _, config) and paramFlowsThroughInPartialPath(kind, innercc, sc1, sc2, ap, config) ) @@ -4037,7 +4044,7 @@ private module FlowExploration { apConsRev(ap, c, ap0, config) ) or - exists(ParameterNode p | + exists(ParameterNodeExt p | mid.getNode() = p and viableParamArg(_, p, node) and sc1 = mid.getSummaryCtx1() and @@ -4115,7 +4122,7 @@ private module FlowExploration { int pos, TRevSummaryCtx1Some sc1, TRevSummaryCtx2Some sc2, RevPartialAccessPath ap, Configuration config ) { - exists(PartialPathNodeRev mid, ParameterNode p | + exists(PartialPathNodeRev mid, ParameterNodeExt p | mid.getNode() = p and p.isParameterOf(_, pos) and sc1 = mid.getSummaryCtx1() and @@ -4138,7 +4145,7 @@ private module FlowExploration { pragma[nomagic] private predicate revPartialPathThroughCallable( - PartialPathNodeRev mid, ArgumentNode node, RevPartialAccessPath ap, Configuration config + PartialPathNodeRev mid, ArgumentNodeExt node, RevPartialAccessPath ap, Configuration config ) { exists(DataFlowCall call, int pos | revPartialPathThroughCallable0(call, mid, pos, ap, config) and 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 9498e51e7e6..cf83530bcbd 100644 --- a/csharp/ql/src/semmle/code/csharp/dataflow/internal/DataFlowImpl5.qll +++ b/csharp/ql/src/semmle/code/csharp/dataflow/internal/DataFlowImpl5.qll @@ -211,10 +211,7 @@ private predicate fullBarrier(Node node, Configuration config) { * Holds if data can flow in one local step from `node1` to `node2`. */ private predicate localFlowStep(Node node1, Node node2, Configuration config) { - ( - simpleLocalFlowStep(node1, node2) or - reverseStepThroughInputOutputAlias(node1, node2) - ) and + simpleLocalFlowStepExt(node1, node2) and not outBarrier(node1, config) and not inBarrier(node2, config) and not fullBarrier(node1, config) and @@ -237,7 +234,7 @@ private predicate additionalLocalFlowStep(Node node1, Node node2, Configuration * Holds if data can flow from `node1` to `node2` in a way that discards call contexts. */ private predicate jumpStep(Node node1, Node node2, Configuration config) { - jumpStep(node1, node2) and + jumpStepCached(node1, node2) and not outBarrier(node1, config) and not inBarrier(node2, config) and not fullBarrier(node1, config) and @@ -388,7 +385,7 @@ private module Stage1 { */ pragma[nomagic] private predicate fwdFlowIsEntered(DataFlowCall call, Cc cc, Configuration config) { - exists(ArgumentNode arg | + exists(ArgumentNodeExt arg | fwdFlow(arg, cc, config) and viableParamArg(call, _, arg) ) @@ -515,7 +512,7 @@ private module Stage1 { pragma[nomagic] predicate viableParamArgNodeCandFwd1( - DataFlowCall call, ParameterNode p, ArgumentNode arg, Configuration config + DataFlowCall call, ParameterNodeExt p, ArgumentNodeExt arg, Configuration config ) { viableParamArg(call, p, arg) and fwdFlow(arg, config) @@ -523,16 +520,16 @@ private module Stage1 { pragma[nomagic] private predicate revFlowIn( - DataFlowCall call, ArgumentNode arg, boolean toReturn, Configuration config + DataFlowCall call, ArgumentNodeExt arg, boolean toReturn, Configuration config ) { - exists(ParameterNode p | + exists(ParameterNodeExt p | revFlow(p, toReturn, config) and viableParamArgNodeCandFwd1(call, p, arg, config) ) } pragma[nomagic] - private predicate revFlowInToReturn(DataFlowCall call, ArgumentNode arg, Configuration config) { + private predicate revFlowInToReturn(DataFlowCall call, ArgumentNodeExt arg, Configuration config) { revFlowIn(call, arg, true, config) } @@ -597,7 +594,9 @@ private module Stage1 { * Holds if flow may enter through `p` and reach a return node making `p` a * candidate for the origin of a summary. */ - predicate parameterMayFlowThrough(ParameterNode p, DataFlowCallable c, Ap ap, Configuration config) { + predicate parameterMayFlowThrough( + ParameterNodeExt p, DataFlowCallable c, Ap ap, Configuration config + ) { exists(ReturnKindExt kind | throughFlowNodeCand(p, config) and returnFlowCallableNodeCand(c, kind, config) and @@ -663,7 +662,7 @@ private predicate flowOutOfCallNodeCand1( pragma[nomagic] private predicate viableParamArgNodeCand1( - DataFlowCall call, ParameterNode p, ArgumentNode arg, Configuration config + DataFlowCall call, ParameterNodeExt p, ArgumentNodeExt arg, Configuration config ) { Stage1::viableParamArgNodeCandFwd1(call, p, arg, config) and Stage1::revFlow(arg, config) @@ -675,7 +674,7 @@ private predicate viableParamArgNodeCand1( */ pragma[nomagic] private predicate flowIntoCallNodeCand1( - DataFlowCall call, ArgumentNode arg, ParameterNode p, Configuration config + DataFlowCall call, ArgumentNodeExt arg, ParameterNodeExt p, Configuration config ) { viableParamArgNodeCand1(call, p, arg, config) and Stage1::revFlow(p, config) and @@ -735,7 +734,7 @@ private predicate flowOutOfCallNodeCand1( */ pragma[nomagic] private predicate flowIntoCallNodeCand1( - DataFlowCall call, ArgumentNode arg, ParameterNode p, boolean allowsFieldFlow, + DataFlowCall call, ArgumentNodeExt arg, ParameterNodeExt p, boolean allowsFieldFlow, Configuration config ) { flowIntoCallNodeCand1(call, arg, p, config) and @@ -944,10 +943,10 @@ private module Stage2 { pragma[nomagic] private predicate fwdFlowIn( - DataFlowCall call, ParameterNode p, Cc outercc, Cc innercc, ApOption argAp, Ap ap, + DataFlowCall call, ParameterNodeExt p, Cc outercc, Cc innercc, ApOption argAp, Ap ap, Configuration config ) { - exists(ArgumentNode arg, boolean allowsFieldFlow | + exists(ArgumentNodeExt arg, boolean allowsFieldFlow | fwdFlow(arg, outercc, argAp, ap, config) and flowIntoCall(call, arg, p, allowsFieldFlow, config) and innercc = getCallContextCall(call, getNodeEnclosingCallable(p), outercc) @@ -992,7 +991,7 @@ private module Stage2 { private predicate fwdFlowIsEntered( DataFlowCall call, Cc cc, ApOption argAp, Ap ap, Configuration config ) { - exists(ParameterNode p | + exists(ParameterNodeExt p | fwdFlowIn(call, p, cc, _, argAp, ap, config) and PrevStage::parameterMayFlowThrough(p, _, getApprox(ap), config) ) @@ -1133,10 +1132,10 @@ private module Stage2 { pragma[nomagic] private predicate revFlowIn( - DataFlowCall call, ArgumentNode arg, boolean toReturn, ApOption returnAp, Ap ap, + DataFlowCall call, ArgumentNodeExt arg, boolean toReturn, ApOption returnAp, Ap ap, Configuration config ) { - exists(ParameterNode p, boolean allowsFieldFlow | + exists(ParameterNodeExt p, boolean allowsFieldFlow | revFlow(p, toReturn, returnAp, ap, config) and flowIntoCall(call, arg, p, allowsFieldFlow, config) | @@ -1146,7 +1145,7 @@ private module Stage2 { pragma[nomagic] private predicate revFlowInToReturn( - DataFlowCall call, ArgumentNode arg, Ap returnAp, Ap ap, Configuration config + DataFlowCall call, ArgumentNodeExt arg, Ap returnAp, Ap ap, Configuration config ) { revFlowIn(call, arg, true, apSome(returnAp), ap, config) } @@ -1199,13 +1198,15 @@ private module Stage2 { pragma[noinline] private predicate parameterFlow( - ParameterNode p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config + ParameterNodeExt p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config ) { revFlow(p, true, apSome(ap0), ap, config) and c = getNodeEnclosingCallable(p) } - predicate parameterMayFlowThrough(ParameterNode p, DataFlowCallable c, Ap ap, Configuration config) { + predicate parameterMayFlowThrough( + ParameterNodeExt p, DataFlowCallable c, Ap ap, Configuration config + ) { exists(ReturnNodeExt ret, Ap ap0, ReturnKindExt kind, int pos | parameterFlow(p, ap, ap0, c, config) and c = getNodeEnclosingCallable(ret) and @@ -1245,7 +1246,7 @@ private predicate flowOutOfCallNodeCand2( pragma[nomagic] private predicate flowIntoCallNodeCand2( - DataFlowCall call, ArgumentNode node1, ParameterNode node2, boolean allowsFieldFlow, + DataFlowCall call, ArgumentNodeExt node1, ParameterNodeExt node2, boolean allowsFieldFlow, Configuration config ) { flowIntoCallNodeCand1(call, node1, node2, allowsFieldFlow, config) and @@ -1260,8 +1261,8 @@ private module LocalFlowBigStep { */ private class FlowCheckNode extends Node { FlowCheckNode() { - this instanceof CastNode or - clearsContent(this, _) + castNode(this) or + clearsContentCached(this, _) } } @@ -1275,7 +1276,7 @@ private module LocalFlowBigStep { config.isSource(node) or jumpStep(_, node, config) or additionalJumpStep(_, node, config) or - node instanceof ParameterNode or + node instanceof ParameterNodeExt or node instanceof OutNodeExt or store(_, _, node, _) or read(_, _, node) or @@ -1321,21 +1322,21 @@ private module LocalFlowBigStep { Node node1, Node node2, boolean preservesValue, DataFlowType t, Configuration config, LocalCallContext cc ) { - not isUnreachableInCall(node2, cc.(LocalCallContextSpecificCall).getCall()) and + not isUnreachableInCallCached(node2, cc.(LocalCallContextSpecificCall).getCall()) and ( localFlowEntry(node1, pragma[only_bind_into](config)) and ( localFlowStepNodeCand1(node1, node2, config) and preservesValue = true and - t = getNodeType(node1) + t = getNodeDataFlowType(node1) or additionalLocalFlowStepNodeCand2(node1, node2, config) and preservesValue = false and - t = getNodeType(node2) + t = getNodeDataFlowType(node2) ) and node1 != node2 and cc.relevantFor(getNodeEnclosingCallable(node1)) and - not isUnreachableInCall(node1, cc.(LocalCallContextSpecificCall).getCall()) and + not isUnreachableInCallCached(node1, cc.(LocalCallContextSpecificCall).getCall()) and Stage2::revFlow(node2, pragma[only_bind_into](config)) or exists(Node mid | @@ -1350,7 +1351,7 @@ private module LocalFlowBigStep { additionalLocalFlowStepNodeCand2(mid, node2, config) and not mid instanceof FlowCheckNode and preservesValue = false and - t = getNodeType(node2) and + t = getNodeDataFlowType(node2) and Stage2::revFlow(node2, pragma[only_bind_into](config)) ) ) @@ -1384,7 +1385,7 @@ private module Stage3 { private ApApprox getApprox(Ap ap) { result = ap.toBoolNonEmpty() } private ApNil getApNil(Node node) { - PrevStage::revFlow(node, _) and result = TFrontNil(getNodeType(node)) + PrevStage::revFlow(node, _) and result = TFrontNil(getNodeDataFlowType(node)) } bindingset[tc, tail] @@ -1443,7 +1444,9 @@ private module Stage3 { bindingset[node, ap] private predicate filter(Node node, Ap ap) { not ap.isClearedAt(node) and - if node instanceof CastingNode then compatibleTypes(getNodeType(node), ap.getType()) else any() + if node instanceof CastingNode + then compatibleTypes(getNodeDataFlowType(node), ap.getType()) + else any() } bindingset[ap, contentType] @@ -1583,10 +1586,10 @@ private module Stage3 { pragma[nomagic] private predicate fwdFlowIn( - DataFlowCall call, ParameterNode p, Cc outercc, Cc innercc, ApOption argAp, Ap ap, + DataFlowCall call, ParameterNodeExt p, Cc outercc, Cc innercc, ApOption argAp, Ap ap, Configuration config ) { - exists(ArgumentNode arg, boolean allowsFieldFlow | + exists(ArgumentNodeExt arg, boolean allowsFieldFlow | fwdFlow(arg, outercc, argAp, ap, config) and flowIntoCall(call, arg, p, allowsFieldFlow, config) and innercc = getCallContextCall(call, getNodeEnclosingCallable(p), outercc) @@ -1631,7 +1634,7 @@ private module Stage3 { private predicate fwdFlowIsEntered( DataFlowCall call, Cc cc, ApOption argAp, Ap ap, Configuration config ) { - exists(ParameterNode p | + exists(ParameterNodeExt p | fwdFlowIn(call, p, cc, _, argAp, ap, config) and PrevStage::parameterMayFlowThrough(p, _, unbindApa(getApprox(ap)), config) ) @@ -1772,10 +1775,10 @@ private module Stage3 { pragma[nomagic] private predicate revFlowIn( - DataFlowCall call, ArgumentNode arg, boolean toReturn, ApOption returnAp, Ap ap, + DataFlowCall call, ArgumentNodeExt arg, boolean toReturn, ApOption returnAp, Ap ap, Configuration config ) { - exists(ParameterNode p, boolean allowsFieldFlow | + exists(ParameterNodeExt p, boolean allowsFieldFlow | revFlow(p, toReturn, returnAp, ap, config) and flowIntoCall(call, arg, p, allowsFieldFlow, config) | @@ -1785,7 +1788,7 @@ private module Stage3 { pragma[nomagic] private predicate revFlowInToReturn( - DataFlowCall call, ArgumentNode arg, Ap returnAp, Ap ap, Configuration config + DataFlowCall call, ArgumentNodeExt arg, Ap returnAp, Ap ap, Configuration config ) { revFlowIn(call, arg, true, apSome(returnAp), ap, config) } @@ -1838,13 +1841,15 @@ private module Stage3 { pragma[noinline] private predicate parameterFlow( - ParameterNode p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config + ParameterNodeExt p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config ) { revFlow(p, true, apSome(ap0), ap, config) and c = getNodeEnclosingCallable(p) } - predicate parameterMayFlowThrough(ParameterNode p, DataFlowCallable c, Ap ap, Configuration config) { + predicate parameterMayFlowThrough( + ParameterNodeExt p, DataFlowCallable c, Ap ap, Configuration config + ) { exists(ReturnNodeExt ret, Ap ap0, ReturnKindExt kind, int pos | parameterFlow(p, ap, ap0, c, config) and c = getNodeEnclosingCallable(ret) and @@ -2088,7 +2093,7 @@ private module Stage4 { private ApApprox getApprox(Ap ap) { result = ap.getFront() } private ApNil getApNil(Node node) { - PrevStage::revFlow(node, _) and result = TNil(getNodeType(node)) + PrevStage::revFlow(node, _) and result = TNil(getNodeDataFlowType(node)) } bindingset[tc, tail] @@ -2155,7 +2160,7 @@ private module Stage4 { pragma[nomagic] private predicate flowIntoCall( - DataFlowCall call, ArgumentNode node1, ParameterNode node2, boolean allowsFieldFlow, + DataFlowCall call, ArgumentNodeExt node1, ParameterNodeExt node2, boolean allowsFieldFlow, Configuration config ) { flowIntoCallNodeCand2(call, node1, node2, allowsFieldFlow, config) and @@ -2300,10 +2305,10 @@ private module Stage4 { pragma[nomagic] private predicate fwdFlowIn( - DataFlowCall call, ParameterNode p, Cc outercc, Cc innercc, ApOption argAp, Ap ap, + DataFlowCall call, ParameterNodeExt p, Cc outercc, Cc innercc, ApOption argAp, Ap ap, Configuration config ) { - exists(ArgumentNode arg, boolean allowsFieldFlow | + exists(ArgumentNodeExt arg, boolean allowsFieldFlow | fwdFlow(arg, outercc, argAp, ap, config) and flowIntoCall(call, arg, p, allowsFieldFlow, config) and innercc = getCallContextCall(call, getNodeEnclosingCallable(p), outercc) @@ -2348,7 +2353,7 @@ private module Stage4 { private predicate fwdFlowIsEntered( DataFlowCall call, Cc cc, ApOption argAp, Ap ap, Configuration config ) { - exists(ParameterNode p | + exists(ParameterNodeExt p | fwdFlowIn(call, p, cc, _, argAp, ap, config) and PrevStage::parameterMayFlowThrough(p, _, unbindApa(getApprox(ap)), config) ) @@ -2489,10 +2494,10 @@ private module Stage4 { pragma[nomagic] private predicate revFlowIn( - DataFlowCall call, ArgumentNode arg, boolean toReturn, ApOption returnAp, Ap ap, + DataFlowCall call, ArgumentNodeExt arg, boolean toReturn, ApOption returnAp, Ap ap, Configuration config ) { - exists(ParameterNode p, boolean allowsFieldFlow | + exists(ParameterNodeExt p, boolean allowsFieldFlow | revFlow(p, toReturn, returnAp, ap, config) and flowIntoCall(call, arg, p, allowsFieldFlow, config) | @@ -2502,7 +2507,7 @@ private module Stage4 { pragma[nomagic] private predicate revFlowInToReturn( - DataFlowCall call, ArgumentNode arg, Ap returnAp, Ap ap, Configuration config + DataFlowCall call, ArgumentNodeExt arg, Ap returnAp, Ap ap, Configuration config ) { revFlowIn(call, arg, true, apSome(returnAp), ap, config) } @@ -2555,13 +2560,15 @@ private module Stage4 { pragma[noinline] private predicate parameterFlow( - ParameterNode p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config + ParameterNodeExt p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config ) { revFlow(p, true, apSome(ap0), ap, config) and c = getNodeEnclosingCallable(p) } - predicate parameterMayFlowThrough(ParameterNode p, DataFlowCallable c, Ap ap, Configuration config) { + predicate parameterMayFlowThrough( + ParameterNodeExt p, DataFlowCallable c, Ap ap, Configuration config + ) { exists(ReturnNodeExt ret, Ap ap0, ReturnKindExt kind, int pos | parameterFlow(p, ap, ap0, c, config) and c = getNodeEnclosingCallable(ret) and @@ -2606,7 +2613,7 @@ private predicate nodeMayUseSummary(Node n, AccessPathApprox apa, Configuration private newtype TSummaryCtx = TSummaryCtxNone() or - TSummaryCtxSome(ParameterNode p, AccessPath ap) { + TSummaryCtxSome(ParameterNodeExt p, AccessPath ap) { Stage4::parameterMayFlowThrough(p, _, ap.getApprox(), _) } @@ -2627,7 +2634,7 @@ private class SummaryCtxNone extends SummaryCtx, TSummaryCtxNone { /** A summary context from which a flow summary can be generated. */ private class SummaryCtxSome extends SummaryCtx, TSummaryCtxSome { - private ParameterNode p; + private ParameterNodeExt p; private AccessPath ap; SummaryCtxSome() { this = TSummaryCtxSome(p, ap) } @@ -2758,7 +2765,7 @@ private newtype TPathNode = config.isSource(node) and cc instanceof CallContextAny and sc instanceof SummaryCtxNone and - ap = TAccessPathNil(getNodeType(node)) + ap = TAccessPathNil(getNodeDataFlowType(node)) or // ... or a step from an existing PathNode to another node. exists(PathNodeMid mid | @@ -2979,7 +2986,7 @@ class PathNode extends TPathNode { Configuration getConfiguration() { none() } private predicate isHidden() { - nodeIsHidden(this.getNode()) and + hiddenNode(this.getNode()) and not this.isSource() and not this instanceof PathNodeSink } @@ -3148,7 +3155,7 @@ private predicate pathStep(PathNodeMid mid, Node node, CallContext cc, SummaryCt cc instanceof CallContextAny and sc instanceof SummaryCtxNone and mid.getAp() instanceof AccessPathNil and - ap = TAccessPathNil(getNodeType(node)) + ap = TAccessPathNil(getNodeDataFlowType(node)) or exists(TypedContent tc | pathStoreStep(mid, node, ap.pop(tc), tc, cc)) and sc = mid.getSummaryCtx() @@ -3235,7 +3242,7 @@ pragma[noinline] private predicate pathIntoArg( PathNodeMid mid, int i, CallContext cc, DataFlowCall call, AccessPath ap, AccessPathApprox apa ) { - exists(ArgumentNode arg | + exists(ArgumentNodeExt arg | arg = mid.getNode() and cc = mid.getCallContext() and arg.argumentOf(call, i) and @@ -3248,7 +3255,7 @@ pragma[noinline] private predicate parameterCand( DataFlowCallable callable, int i, AccessPathApprox apa, Configuration config ) { - exists(ParameterNode p | + exists(ParameterNodeExt p | Stage4::revFlow(p, _, _, apa, config) and p.isParameterOf(callable, i) ) @@ -3272,7 +3279,7 @@ private predicate pathIntoCallable0( * respectively. */ private predicate pathIntoCallable( - PathNodeMid mid, ParameterNode p, CallContext outercc, CallContextCall innercc, SummaryCtx sc, + PathNodeMid mid, ParameterNodeExt p, CallContext outercc, CallContextCall innercc, SummaryCtx sc, DataFlowCall call ) { exists(int i, DataFlowCallable callable, AccessPath ap | @@ -3568,7 +3575,7 @@ private module FlowExploration { private newtype TSummaryCtx1 = TSummaryCtx1None() or - TSummaryCtx1Param(ParameterNode p) + TSummaryCtx1Param(ParameterNodeExt p) private newtype TSummaryCtx2 = TSummaryCtx2None() or @@ -3591,7 +3598,7 @@ private module FlowExploration { cc instanceof CallContextAny and sc1 = TSummaryCtx1None() and sc2 = TSummaryCtx2None() and - ap = TPartialNil(getNodeType(node)) and + ap = TPartialNil(getNodeDataFlowType(node)) and not fullBarrier(node, config) and exists(config.explorationLimit()) or @@ -3611,7 +3618,7 @@ private module FlowExploration { or exists(PartialPathNodeRev mid | revPartialPathStep(mid, node, sc1, sc2, ap, config) and - not clearsContent(node, ap.getHead()) and + not clearsContentCached(node, ap.getHead()) and not fullBarrier(node, config) and distSink(getNodeEnclosingCallable(node), config) <= config.explorationLimit() ) @@ -3625,9 +3632,9 @@ private module FlowExploration { exists(PartialPathNodeFwd mid | partialPathStep(mid, node, cc, sc1, sc2, ap, config) and not fullBarrier(node, config) and - not clearsContent(node, ap.getHead().getContent()) and + not clearsContentCached(node, ap.getHead().getContent()) and if node instanceof CastingNode - then compatibleTypes(getNodeType(node), ap.getType()) + then compatibleTypes(getNodeDataFlowType(node), ap.getType()) else any() ) } @@ -3783,7 +3790,7 @@ private module FlowExploration { PartialPathNodeFwd mid, Node node, CallContext cc, TSummaryCtx1 sc1, TSummaryCtx2 sc2, PartialAccessPath ap, Configuration config ) { - not isUnreachableInCall(node, cc.(CallContextSpecificCall).getCall()) and + not isUnreachableInCallCached(node, cc.(CallContextSpecificCall).getCall()) and ( localFlowStep(mid.getNode(), node, config) and cc = mid.getCallContext() and @@ -3797,7 +3804,7 @@ private module FlowExploration { sc1 = mid.getSummaryCtx1() and sc2 = mid.getSummaryCtx2() and mid.getAp() instanceof PartialAccessPathNil and - ap = TPartialNil(getNodeType(node)) and + ap = TPartialNil(getNodeDataFlowType(node)) and config = mid.getConfiguration() ) or @@ -3813,7 +3820,7 @@ private module FlowExploration { sc1 = TSummaryCtx1None() and sc2 = TSummaryCtx2None() and mid.getAp() instanceof PartialAccessPathNil and - ap = TPartialNil(getNodeType(node)) and + ap = TPartialNil(getNodeDataFlowType(node)) and config = mid.getConfiguration() or partialPathStoreStep(mid, _, _, node, ap) and @@ -3827,7 +3834,7 @@ private module FlowExploration { sc1 = mid.getSummaryCtx1() and sc2 = mid.getSummaryCtx2() and apConsFwd(ap, tc, ap0, config) and - compatibleTypes(ap.getType(), getNodeType(node)) + compatibleTypes(ap.getType(), getNodeDataFlowType(node)) ) or partialPathIntoCallable(mid, node, _, cc, sc1, sc2, _, ap, config) @@ -3924,7 +3931,7 @@ private module FlowExploration { PartialPathNodeFwd mid, int i, CallContext cc, DataFlowCall call, PartialAccessPath ap, Configuration config ) { - exists(ArgumentNode arg | + exists(ArgumentNodeExt arg | arg = mid.getNode() and cc = mid.getCallContext() and arg.argumentOf(call, i) and @@ -3943,7 +3950,7 @@ private module FlowExploration { } private predicate partialPathIntoCallable( - PartialPathNodeFwd mid, ParameterNode p, CallContext outercc, CallContextCall innercc, + PartialPathNodeFwd mid, ParameterNodeExt p, CallContext outercc, CallContextCall innercc, TSummaryCtx1 sc1, TSummaryCtx2 sc2, DataFlowCall call, PartialAccessPath ap, Configuration config ) { @@ -3980,7 +3987,7 @@ private module FlowExploration { DataFlowCall call, PartialPathNodeFwd mid, ReturnKindExt kind, CallContext cc, PartialAccessPath ap, Configuration config ) { - exists(ParameterNode p, CallContext innercc, TSummaryCtx1 sc1, TSummaryCtx2 sc2 | + exists(ParameterNodeExt p, CallContext innercc, TSummaryCtx1 sc1, TSummaryCtx2 sc2 | partialPathIntoCallable(mid, p, cc, innercc, sc1, sc2, call, _, config) and paramFlowsThroughInPartialPath(kind, innercc, sc1, sc2, ap, config) ) @@ -4037,7 +4044,7 @@ private module FlowExploration { apConsRev(ap, c, ap0, config) ) or - exists(ParameterNode p | + exists(ParameterNodeExt p | mid.getNode() = p and viableParamArg(_, p, node) and sc1 = mid.getSummaryCtx1() and @@ -4115,7 +4122,7 @@ private module FlowExploration { int pos, TRevSummaryCtx1Some sc1, TRevSummaryCtx2Some sc2, RevPartialAccessPath ap, Configuration config ) { - exists(PartialPathNodeRev mid, ParameterNode p | + exists(PartialPathNodeRev mid, ParameterNodeExt p | mid.getNode() = p and p.isParameterOf(_, pos) and sc1 = mid.getSummaryCtx1() and @@ -4138,7 +4145,7 @@ private module FlowExploration { pragma[nomagic] private predicate revPartialPathThroughCallable( - PartialPathNodeRev mid, ArgumentNode node, RevPartialAccessPath ap, Configuration config + PartialPathNodeRev mid, ArgumentNodeExt node, RevPartialAccessPath ap, Configuration config ) { exists(DataFlowCall call, int pos | revPartialPathThroughCallable0(call, mid, pos, ap, config) and 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 9498e51e7e6..cf83530bcbd 100644 --- a/java/ql/src/semmle/code/java/dataflow/internal/DataFlowImpl.qll +++ b/java/ql/src/semmle/code/java/dataflow/internal/DataFlowImpl.qll @@ -211,10 +211,7 @@ private predicate fullBarrier(Node node, Configuration config) { * Holds if data can flow in one local step from `node1` to `node2`. */ private predicate localFlowStep(Node node1, Node node2, Configuration config) { - ( - simpleLocalFlowStep(node1, node2) or - reverseStepThroughInputOutputAlias(node1, node2) - ) and + simpleLocalFlowStepExt(node1, node2) and not outBarrier(node1, config) and not inBarrier(node2, config) and not fullBarrier(node1, config) and @@ -237,7 +234,7 @@ private predicate additionalLocalFlowStep(Node node1, Node node2, Configuration * Holds if data can flow from `node1` to `node2` in a way that discards call contexts. */ private predicate jumpStep(Node node1, Node node2, Configuration config) { - jumpStep(node1, node2) and + jumpStepCached(node1, node2) and not outBarrier(node1, config) and not inBarrier(node2, config) and not fullBarrier(node1, config) and @@ -388,7 +385,7 @@ private module Stage1 { */ pragma[nomagic] private predicate fwdFlowIsEntered(DataFlowCall call, Cc cc, Configuration config) { - exists(ArgumentNode arg | + exists(ArgumentNodeExt arg | fwdFlow(arg, cc, config) and viableParamArg(call, _, arg) ) @@ -515,7 +512,7 @@ private module Stage1 { pragma[nomagic] predicate viableParamArgNodeCandFwd1( - DataFlowCall call, ParameterNode p, ArgumentNode arg, Configuration config + DataFlowCall call, ParameterNodeExt p, ArgumentNodeExt arg, Configuration config ) { viableParamArg(call, p, arg) and fwdFlow(arg, config) @@ -523,16 +520,16 @@ private module Stage1 { pragma[nomagic] private predicate revFlowIn( - DataFlowCall call, ArgumentNode arg, boolean toReturn, Configuration config + DataFlowCall call, ArgumentNodeExt arg, boolean toReturn, Configuration config ) { - exists(ParameterNode p | + exists(ParameterNodeExt p | revFlow(p, toReturn, config) and viableParamArgNodeCandFwd1(call, p, arg, config) ) } pragma[nomagic] - private predicate revFlowInToReturn(DataFlowCall call, ArgumentNode arg, Configuration config) { + private predicate revFlowInToReturn(DataFlowCall call, ArgumentNodeExt arg, Configuration config) { revFlowIn(call, arg, true, config) } @@ -597,7 +594,9 @@ private module Stage1 { * Holds if flow may enter through `p` and reach a return node making `p` a * candidate for the origin of a summary. */ - predicate parameterMayFlowThrough(ParameterNode p, DataFlowCallable c, Ap ap, Configuration config) { + predicate parameterMayFlowThrough( + ParameterNodeExt p, DataFlowCallable c, Ap ap, Configuration config + ) { exists(ReturnKindExt kind | throughFlowNodeCand(p, config) and returnFlowCallableNodeCand(c, kind, config) and @@ -663,7 +662,7 @@ private predicate flowOutOfCallNodeCand1( pragma[nomagic] private predicate viableParamArgNodeCand1( - DataFlowCall call, ParameterNode p, ArgumentNode arg, Configuration config + DataFlowCall call, ParameterNodeExt p, ArgumentNodeExt arg, Configuration config ) { Stage1::viableParamArgNodeCandFwd1(call, p, arg, config) and Stage1::revFlow(arg, config) @@ -675,7 +674,7 @@ private predicate viableParamArgNodeCand1( */ pragma[nomagic] private predicate flowIntoCallNodeCand1( - DataFlowCall call, ArgumentNode arg, ParameterNode p, Configuration config + DataFlowCall call, ArgumentNodeExt arg, ParameterNodeExt p, Configuration config ) { viableParamArgNodeCand1(call, p, arg, config) and Stage1::revFlow(p, config) and @@ -735,7 +734,7 @@ private predicate flowOutOfCallNodeCand1( */ pragma[nomagic] private predicate flowIntoCallNodeCand1( - DataFlowCall call, ArgumentNode arg, ParameterNode p, boolean allowsFieldFlow, + DataFlowCall call, ArgumentNodeExt arg, ParameterNodeExt p, boolean allowsFieldFlow, Configuration config ) { flowIntoCallNodeCand1(call, arg, p, config) and @@ -944,10 +943,10 @@ private module Stage2 { pragma[nomagic] private predicate fwdFlowIn( - DataFlowCall call, ParameterNode p, Cc outercc, Cc innercc, ApOption argAp, Ap ap, + DataFlowCall call, ParameterNodeExt p, Cc outercc, Cc innercc, ApOption argAp, Ap ap, Configuration config ) { - exists(ArgumentNode arg, boolean allowsFieldFlow | + exists(ArgumentNodeExt arg, boolean allowsFieldFlow | fwdFlow(arg, outercc, argAp, ap, config) and flowIntoCall(call, arg, p, allowsFieldFlow, config) and innercc = getCallContextCall(call, getNodeEnclosingCallable(p), outercc) @@ -992,7 +991,7 @@ private module Stage2 { private predicate fwdFlowIsEntered( DataFlowCall call, Cc cc, ApOption argAp, Ap ap, Configuration config ) { - exists(ParameterNode p | + exists(ParameterNodeExt p | fwdFlowIn(call, p, cc, _, argAp, ap, config) and PrevStage::parameterMayFlowThrough(p, _, getApprox(ap), config) ) @@ -1133,10 +1132,10 @@ private module Stage2 { pragma[nomagic] private predicate revFlowIn( - DataFlowCall call, ArgumentNode arg, boolean toReturn, ApOption returnAp, Ap ap, + DataFlowCall call, ArgumentNodeExt arg, boolean toReturn, ApOption returnAp, Ap ap, Configuration config ) { - exists(ParameterNode p, boolean allowsFieldFlow | + exists(ParameterNodeExt p, boolean allowsFieldFlow | revFlow(p, toReturn, returnAp, ap, config) and flowIntoCall(call, arg, p, allowsFieldFlow, config) | @@ -1146,7 +1145,7 @@ private module Stage2 { pragma[nomagic] private predicate revFlowInToReturn( - DataFlowCall call, ArgumentNode arg, Ap returnAp, Ap ap, Configuration config + DataFlowCall call, ArgumentNodeExt arg, Ap returnAp, Ap ap, Configuration config ) { revFlowIn(call, arg, true, apSome(returnAp), ap, config) } @@ -1199,13 +1198,15 @@ private module Stage2 { pragma[noinline] private predicate parameterFlow( - ParameterNode p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config + ParameterNodeExt p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config ) { revFlow(p, true, apSome(ap0), ap, config) and c = getNodeEnclosingCallable(p) } - predicate parameterMayFlowThrough(ParameterNode p, DataFlowCallable c, Ap ap, Configuration config) { + predicate parameterMayFlowThrough( + ParameterNodeExt p, DataFlowCallable c, Ap ap, Configuration config + ) { exists(ReturnNodeExt ret, Ap ap0, ReturnKindExt kind, int pos | parameterFlow(p, ap, ap0, c, config) and c = getNodeEnclosingCallable(ret) and @@ -1245,7 +1246,7 @@ private predicate flowOutOfCallNodeCand2( pragma[nomagic] private predicate flowIntoCallNodeCand2( - DataFlowCall call, ArgumentNode node1, ParameterNode node2, boolean allowsFieldFlow, + DataFlowCall call, ArgumentNodeExt node1, ParameterNodeExt node2, boolean allowsFieldFlow, Configuration config ) { flowIntoCallNodeCand1(call, node1, node2, allowsFieldFlow, config) and @@ -1260,8 +1261,8 @@ private module LocalFlowBigStep { */ private class FlowCheckNode extends Node { FlowCheckNode() { - this instanceof CastNode or - clearsContent(this, _) + castNode(this) or + clearsContentCached(this, _) } } @@ -1275,7 +1276,7 @@ private module LocalFlowBigStep { config.isSource(node) or jumpStep(_, node, config) or additionalJumpStep(_, node, config) or - node instanceof ParameterNode or + node instanceof ParameterNodeExt or node instanceof OutNodeExt or store(_, _, node, _) or read(_, _, node) or @@ -1321,21 +1322,21 @@ private module LocalFlowBigStep { Node node1, Node node2, boolean preservesValue, DataFlowType t, Configuration config, LocalCallContext cc ) { - not isUnreachableInCall(node2, cc.(LocalCallContextSpecificCall).getCall()) and + not isUnreachableInCallCached(node2, cc.(LocalCallContextSpecificCall).getCall()) and ( localFlowEntry(node1, pragma[only_bind_into](config)) and ( localFlowStepNodeCand1(node1, node2, config) and preservesValue = true and - t = getNodeType(node1) + t = getNodeDataFlowType(node1) or additionalLocalFlowStepNodeCand2(node1, node2, config) and preservesValue = false and - t = getNodeType(node2) + t = getNodeDataFlowType(node2) ) and node1 != node2 and cc.relevantFor(getNodeEnclosingCallable(node1)) and - not isUnreachableInCall(node1, cc.(LocalCallContextSpecificCall).getCall()) and + not isUnreachableInCallCached(node1, cc.(LocalCallContextSpecificCall).getCall()) and Stage2::revFlow(node2, pragma[only_bind_into](config)) or exists(Node mid | @@ -1350,7 +1351,7 @@ private module LocalFlowBigStep { additionalLocalFlowStepNodeCand2(mid, node2, config) and not mid instanceof FlowCheckNode and preservesValue = false and - t = getNodeType(node2) and + t = getNodeDataFlowType(node2) and Stage2::revFlow(node2, pragma[only_bind_into](config)) ) ) @@ -1384,7 +1385,7 @@ private module Stage3 { private ApApprox getApprox(Ap ap) { result = ap.toBoolNonEmpty() } private ApNil getApNil(Node node) { - PrevStage::revFlow(node, _) and result = TFrontNil(getNodeType(node)) + PrevStage::revFlow(node, _) and result = TFrontNil(getNodeDataFlowType(node)) } bindingset[tc, tail] @@ -1443,7 +1444,9 @@ private module Stage3 { bindingset[node, ap] private predicate filter(Node node, Ap ap) { not ap.isClearedAt(node) and - if node instanceof CastingNode then compatibleTypes(getNodeType(node), ap.getType()) else any() + if node instanceof CastingNode + then compatibleTypes(getNodeDataFlowType(node), ap.getType()) + else any() } bindingset[ap, contentType] @@ -1583,10 +1586,10 @@ private module Stage3 { pragma[nomagic] private predicate fwdFlowIn( - DataFlowCall call, ParameterNode p, Cc outercc, Cc innercc, ApOption argAp, Ap ap, + DataFlowCall call, ParameterNodeExt p, Cc outercc, Cc innercc, ApOption argAp, Ap ap, Configuration config ) { - exists(ArgumentNode arg, boolean allowsFieldFlow | + exists(ArgumentNodeExt arg, boolean allowsFieldFlow | fwdFlow(arg, outercc, argAp, ap, config) and flowIntoCall(call, arg, p, allowsFieldFlow, config) and innercc = getCallContextCall(call, getNodeEnclosingCallable(p), outercc) @@ -1631,7 +1634,7 @@ private module Stage3 { private predicate fwdFlowIsEntered( DataFlowCall call, Cc cc, ApOption argAp, Ap ap, Configuration config ) { - exists(ParameterNode p | + exists(ParameterNodeExt p | fwdFlowIn(call, p, cc, _, argAp, ap, config) and PrevStage::parameterMayFlowThrough(p, _, unbindApa(getApprox(ap)), config) ) @@ -1772,10 +1775,10 @@ private module Stage3 { pragma[nomagic] private predicate revFlowIn( - DataFlowCall call, ArgumentNode arg, boolean toReturn, ApOption returnAp, Ap ap, + DataFlowCall call, ArgumentNodeExt arg, boolean toReturn, ApOption returnAp, Ap ap, Configuration config ) { - exists(ParameterNode p, boolean allowsFieldFlow | + exists(ParameterNodeExt p, boolean allowsFieldFlow | revFlow(p, toReturn, returnAp, ap, config) and flowIntoCall(call, arg, p, allowsFieldFlow, config) | @@ -1785,7 +1788,7 @@ private module Stage3 { pragma[nomagic] private predicate revFlowInToReturn( - DataFlowCall call, ArgumentNode arg, Ap returnAp, Ap ap, Configuration config + DataFlowCall call, ArgumentNodeExt arg, Ap returnAp, Ap ap, Configuration config ) { revFlowIn(call, arg, true, apSome(returnAp), ap, config) } @@ -1838,13 +1841,15 @@ private module Stage3 { pragma[noinline] private predicate parameterFlow( - ParameterNode p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config + ParameterNodeExt p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config ) { revFlow(p, true, apSome(ap0), ap, config) and c = getNodeEnclosingCallable(p) } - predicate parameterMayFlowThrough(ParameterNode p, DataFlowCallable c, Ap ap, Configuration config) { + predicate parameterMayFlowThrough( + ParameterNodeExt p, DataFlowCallable c, Ap ap, Configuration config + ) { exists(ReturnNodeExt ret, Ap ap0, ReturnKindExt kind, int pos | parameterFlow(p, ap, ap0, c, config) and c = getNodeEnclosingCallable(ret) and @@ -2088,7 +2093,7 @@ private module Stage4 { private ApApprox getApprox(Ap ap) { result = ap.getFront() } private ApNil getApNil(Node node) { - PrevStage::revFlow(node, _) and result = TNil(getNodeType(node)) + PrevStage::revFlow(node, _) and result = TNil(getNodeDataFlowType(node)) } bindingset[tc, tail] @@ -2155,7 +2160,7 @@ private module Stage4 { pragma[nomagic] private predicate flowIntoCall( - DataFlowCall call, ArgumentNode node1, ParameterNode node2, boolean allowsFieldFlow, + DataFlowCall call, ArgumentNodeExt node1, ParameterNodeExt node2, boolean allowsFieldFlow, Configuration config ) { flowIntoCallNodeCand2(call, node1, node2, allowsFieldFlow, config) and @@ -2300,10 +2305,10 @@ private module Stage4 { pragma[nomagic] private predicate fwdFlowIn( - DataFlowCall call, ParameterNode p, Cc outercc, Cc innercc, ApOption argAp, Ap ap, + DataFlowCall call, ParameterNodeExt p, Cc outercc, Cc innercc, ApOption argAp, Ap ap, Configuration config ) { - exists(ArgumentNode arg, boolean allowsFieldFlow | + exists(ArgumentNodeExt arg, boolean allowsFieldFlow | fwdFlow(arg, outercc, argAp, ap, config) and flowIntoCall(call, arg, p, allowsFieldFlow, config) and innercc = getCallContextCall(call, getNodeEnclosingCallable(p), outercc) @@ -2348,7 +2353,7 @@ private module Stage4 { private predicate fwdFlowIsEntered( DataFlowCall call, Cc cc, ApOption argAp, Ap ap, Configuration config ) { - exists(ParameterNode p | + exists(ParameterNodeExt p | fwdFlowIn(call, p, cc, _, argAp, ap, config) and PrevStage::parameterMayFlowThrough(p, _, unbindApa(getApprox(ap)), config) ) @@ -2489,10 +2494,10 @@ private module Stage4 { pragma[nomagic] private predicate revFlowIn( - DataFlowCall call, ArgumentNode arg, boolean toReturn, ApOption returnAp, Ap ap, + DataFlowCall call, ArgumentNodeExt arg, boolean toReturn, ApOption returnAp, Ap ap, Configuration config ) { - exists(ParameterNode p, boolean allowsFieldFlow | + exists(ParameterNodeExt p, boolean allowsFieldFlow | revFlow(p, toReturn, returnAp, ap, config) and flowIntoCall(call, arg, p, allowsFieldFlow, config) | @@ -2502,7 +2507,7 @@ private module Stage4 { pragma[nomagic] private predicate revFlowInToReturn( - DataFlowCall call, ArgumentNode arg, Ap returnAp, Ap ap, Configuration config + DataFlowCall call, ArgumentNodeExt arg, Ap returnAp, Ap ap, Configuration config ) { revFlowIn(call, arg, true, apSome(returnAp), ap, config) } @@ -2555,13 +2560,15 @@ private module Stage4 { pragma[noinline] private predicate parameterFlow( - ParameterNode p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config + ParameterNodeExt p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config ) { revFlow(p, true, apSome(ap0), ap, config) and c = getNodeEnclosingCallable(p) } - predicate parameterMayFlowThrough(ParameterNode p, DataFlowCallable c, Ap ap, Configuration config) { + predicate parameterMayFlowThrough( + ParameterNodeExt p, DataFlowCallable c, Ap ap, Configuration config + ) { exists(ReturnNodeExt ret, Ap ap0, ReturnKindExt kind, int pos | parameterFlow(p, ap, ap0, c, config) and c = getNodeEnclosingCallable(ret) and @@ -2606,7 +2613,7 @@ private predicate nodeMayUseSummary(Node n, AccessPathApprox apa, Configuration private newtype TSummaryCtx = TSummaryCtxNone() or - TSummaryCtxSome(ParameterNode p, AccessPath ap) { + TSummaryCtxSome(ParameterNodeExt p, AccessPath ap) { Stage4::parameterMayFlowThrough(p, _, ap.getApprox(), _) } @@ -2627,7 +2634,7 @@ private class SummaryCtxNone extends SummaryCtx, TSummaryCtxNone { /** A summary context from which a flow summary can be generated. */ private class SummaryCtxSome extends SummaryCtx, TSummaryCtxSome { - private ParameterNode p; + private ParameterNodeExt p; private AccessPath ap; SummaryCtxSome() { this = TSummaryCtxSome(p, ap) } @@ -2758,7 +2765,7 @@ private newtype TPathNode = config.isSource(node) and cc instanceof CallContextAny and sc instanceof SummaryCtxNone and - ap = TAccessPathNil(getNodeType(node)) + ap = TAccessPathNil(getNodeDataFlowType(node)) or // ... or a step from an existing PathNode to another node. exists(PathNodeMid mid | @@ -2979,7 +2986,7 @@ class PathNode extends TPathNode { Configuration getConfiguration() { none() } private predicate isHidden() { - nodeIsHidden(this.getNode()) and + hiddenNode(this.getNode()) and not this.isSource() and not this instanceof PathNodeSink } @@ -3148,7 +3155,7 @@ private predicate pathStep(PathNodeMid mid, Node node, CallContext cc, SummaryCt cc instanceof CallContextAny and sc instanceof SummaryCtxNone and mid.getAp() instanceof AccessPathNil and - ap = TAccessPathNil(getNodeType(node)) + ap = TAccessPathNil(getNodeDataFlowType(node)) or exists(TypedContent tc | pathStoreStep(mid, node, ap.pop(tc), tc, cc)) and sc = mid.getSummaryCtx() @@ -3235,7 +3242,7 @@ pragma[noinline] private predicate pathIntoArg( PathNodeMid mid, int i, CallContext cc, DataFlowCall call, AccessPath ap, AccessPathApprox apa ) { - exists(ArgumentNode arg | + exists(ArgumentNodeExt arg | arg = mid.getNode() and cc = mid.getCallContext() and arg.argumentOf(call, i) and @@ -3248,7 +3255,7 @@ pragma[noinline] private predicate parameterCand( DataFlowCallable callable, int i, AccessPathApprox apa, Configuration config ) { - exists(ParameterNode p | + exists(ParameterNodeExt p | Stage4::revFlow(p, _, _, apa, config) and p.isParameterOf(callable, i) ) @@ -3272,7 +3279,7 @@ private predicate pathIntoCallable0( * respectively. */ private predicate pathIntoCallable( - PathNodeMid mid, ParameterNode p, CallContext outercc, CallContextCall innercc, SummaryCtx sc, + PathNodeMid mid, ParameterNodeExt p, CallContext outercc, CallContextCall innercc, SummaryCtx sc, DataFlowCall call ) { exists(int i, DataFlowCallable callable, AccessPath ap | @@ -3568,7 +3575,7 @@ private module FlowExploration { private newtype TSummaryCtx1 = TSummaryCtx1None() or - TSummaryCtx1Param(ParameterNode p) + TSummaryCtx1Param(ParameterNodeExt p) private newtype TSummaryCtx2 = TSummaryCtx2None() or @@ -3591,7 +3598,7 @@ private module FlowExploration { cc instanceof CallContextAny and sc1 = TSummaryCtx1None() and sc2 = TSummaryCtx2None() and - ap = TPartialNil(getNodeType(node)) and + ap = TPartialNil(getNodeDataFlowType(node)) and not fullBarrier(node, config) and exists(config.explorationLimit()) or @@ -3611,7 +3618,7 @@ private module FlowExploration { or exists(PartialPathNodeRev mid | revPartialPathStep(mid, node, sc1, sc2, ap, config) and - not clearsContent(node, ap.getHead()) and + not clearsContentCached(node, ap.getHead()) and not fullBarrier(node, config) and distSink(getNodeEnclosingCallable(node), config) <= config.explorationLimit() ) @@ -3625,9 +3632,9 @@ private module FlowExploration { exists(PartialPathNodeFwd mid | partialPathStep(mid, node, cc, sc1, sc2, ap, config) and not fullBarrier(node, config) and - not clearsContent(node, ap.getHead().getContent()) and + not clearsContentCached(node, ap.getHead().getContent()) and if node instanceof CastingNode - then compatibleTypes(getNodeType(node), ap.getType()) + then compatibleTypes(getNodeDataFlowType(node), ap.getType()) else any() ) } @@ -3783,7 +3790,7 @@ private module FlowExploration { PartialPathNodeFwd mid, Node node, CallContext cc, TSummaryCtx1 sc1, TSummaryCtx2 sc2, PartialAccessPath ap, Configuration config ) { - not isUnreachableInCall(node, cc.(CallContextSpecificCall).getCall()) and + not isUnreachableInCallCached(node, cc.(CallContextSpecificCall).getCall()) and ( localFlowStep(mid.getNode(), node, config) and cc = mid.getCallContext() and @@ -3797,7 +3804,7 @@ private module FlowExploration { sc1 = mid.getSummaryCtx1() and sc2 = mid.getSummaryCtx2() and mid.getAp() instanceof PartialAccessPathNil and - ap = TPartialNil(getNodeType(node)) and + ap = TPartialNil(getNodeDataFlowType(node)) and config = mid.getConfiguration() ) or @@ -3813,7 +3820,7 @@ private module FlowExploration { sc1 = TSummaryCtx1None() and sc2 = TSummaryCtx2None() and mid.getAp() instanceof PartialAccessPathNil and - ap = TPartialNil(getNodeType(node)) and + ap = TPartialNil(getNodeDataFlowType(node)) and config = mid.getConfiguration() or partialPathStoreStep(mid, _, _, node, ap) and @@ -3827,7 +3834,7 @@ private module FlowExploration { sc1 = mid.getSummaryCtx1() and sc2 = mid.getSummaryCtx2() and apConsFwd(ap, tc, ap0, config) and - compatibleTypes(ap.getType(), getNodeType(node)) + compatibleTypes(ap.getType(), getNodeDataFlowType(node)) ) or partialPathIntoCallable(mid, node, _, cc, sc1, sc2, _, ap, config) @@ -3924,7 +3931,7 @@ private module FlowExploration { PartialPathNodeFwd mid, int i, CallContext cc, DataFlowCall call, PartialAccessPath ap, Configuration config ) { - exists(ArgumentNode arg | + exists(ArgumentNodeExt arg | arg = mid.getNode() and cc = mid.getCallContext() and arg.argumentOf(call, i) and @@ -3943,7 +3950,7 @@ private module FlowExploration { } private predicate partialPathIntoCallable( - PartialPathNodeFwd mid, ParameterNode p, CallContext outercc, CallContextCall innercc, + PartialPathNodeFwd mid, ParameterNodeExt p, CallContext outercc, CallContextCall innercc, TSummaryCtx1 sc1, TSummaryCtx2 sc2, DataFlowCall call, PartialAccessPath ap, Configuration config ) { @@ -3980,7 +3987,7 @@ private module FlowExploration { DataFlowCall call, PartialPathNodeFwd mid, ReturnKindExt kind, CallContext cc, PartialAccessPath ap, Configuration config ) { - exists(ParameterNode p, CallContext innercc, TSummaryCtx1 sc1, TSummaryCtx2 sc2 | + exists(ParameterNodeExt p, CallContext innercc, TSummaryCtx1 sc1, TSummaryCtx2 sc2 | partialPathIntoCallable(mid, p, cc, innercc, sc1, sc2, call, _, config) and paramFlowsThroughInPartialPath(kind, innercc, sc1, sc2, ap, config) ) @@ -4037,7 +4044,7 @@ private module FlowExploration { apConsRev(ap, c, ap0, config) ) or - exists(ParameterNode p | + exists(ParameterNodeExt p | mid.getNode() = p and viableParamArg(_, p, node) and sc1 = mid.getSummaryCtx1() and @@ -4115,7 +4122,7 @@ private module FlowExploration { int pos, TRevSummaryCtx1Some sc1, TRevSummaryCtx2Some sc2, RevPartialAccessPath ap, Configuration config ) { - exists(PartialPathNodeRev mid, ParameterNode p | + exists(PartialPathNodeRev mid, ParameterNodeExt p | mid.getNode() = p and p.isParameterOf(_, pos) and sc1 = mid.getSummaryCtx1() and @@ -4138,7 +4145,7 @@ private module FlowExploration { pragma[nomagic] private predicate revPartialPathThroughCallable( - PartialPathNodeRev mid, ArgumentNode node, RevPartialAccessPath ap, Configuration config + PartialPathNodeRev mid, ArgumentNodeExt node, RevPartialAccessPath ap, Configuration config ) { exists(DataFlowCall call, int pos | revPartialPathThroughCallable0(call, mid, pos, ap, config) and 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 9498e51e7e6..cf83530bcbd 100644 --- a/java/ql/src/semmle/code/java/dataflow/internal/DataFlowImpl2.qll +++ b/java/ql/src/semmle/code/java/dataflow/internal/DataFlowImpl2.qll @@ -211,10 +211,7 @@ private predicate fullBarrier(Node node, Configuration config) { * Holds if data can flow in one local step from `node1` to `node2`. */ private predicate localFlowStep(Node node1, Node node2, Configuration config) { - ( - simpleLocalFlowStep(node1, node2) or - reverseStepThroughInputOutputAlias(node1, node2) - ) and + simpleLocalFlowStepExt(node1, node2) and not outBarrier(node1, config) and not inBarrier(node2, config) and not fullBarrier(node1, config) and @@ -237,7 +234,7 @@ private predicate additionalLocalFlowStep(Node node1, Node node2, Configuration * Holds if data can flow from `node1` to `node2` in a way that discards call contexts. */ private predicate jumpStep(Node node1, Node node2, Configuration config) { - jumpStep(node1, node2) and + jumpStepCached(node1, node2) and not outBarrier(node1, config) and not inBarrier(node2, config) and not fullBarrier(node1, config) and @@ -388,7 +385,7 @@ private module Stage1 { */ pragma[nomagic] private predicate fwdFlowIsEntered(DataFlowCall call, Cc cc, Configuration config) { - exists(ArgumentNode arg | + exists(ArgumentNodeExt arg | fwdFlow(arg, cc, config) and viableParamArg(call, _, arg) ) @@ -515,7 +512,7 @@ private module Stage1 { pragma[nomagic] predicate viableParamArgNodeCandFwd1( - DataFlowCall call, ParameterNode p, ArgumentNode arg, Configuration config + DataFlowCall call, ParameterNodeExt p, ArgumentNodeExt arg, Configuration config ) { viableParamArg(call, p, arg) and fwdFlow(arg, config) @@ -523,16 +520,16 @@ private module Stage1 { pragma[nomagic] private predicate revFlowIn( - DataFlowCall call, ArgumentNode arg, boolean toReturn, Configuration config + DataFlowCall call, ArgumentNodeExt arg, boolean toReturn, Configuration config ) { - exists(ParameterNode p | + exists(ParameterNodeExt p | revFlow(p, toReturn, config) and viableParamArgNodeCandFwd1(call, p, arg, config) ) } pragma[nomagic] - private predicate revFlowInToReturn(DataFlowCall call, ArgumentNode arg, Configuration config) { + private predicate revFlowInToReturn(DataFlowCall call, ArgumentNodeExt arg, Configuration config) { revFlowIn(call, arg, true, config) } @@ -597,7 +594,9 @@ private module Stage1 { * Holds if flow may enter through `p` and reach a return node making `p` a * candidate for the origin of a summary. */ - predicate parameterMayFlowThrough(ParameterNode p, DataFlowCallable c, Ap ap, Configuration config) { + predicate parameterMayFlowThrough( + ParameterNodeExt p, DataFlowCallable c, Ap ap, Configuration config + ) { exists(ReturnKindExt kind | throughFlowNodeCand(p, config) and returnFlowCallableNodeCand(c, kind, config) and @@ -663,7 +662,7 @@ private predicate flowOutOfCallNodeCand1( pragma[nomagic] private predicate viableParamArgNodeCand1( - DataFlowCall call, ParameterNode p, ArgumentNode arg, Configuration config + DataFlowCall call, ParameterNodeExt p, ArgumentNodeExt arg, Configuration config ) { Stage1::viableParamArgNodeCandFwd1(call, p, arg, config) and Stage1::revFlow(arg, config) @@ -675,7 +674,7 @@ private predicate viableParamArgNodeCand1( */ pragma[nomagic] private predicate flowIntoCallNodeCand1( - DataFlowCall call, ArgumentNode arg, ParameterNode p, Configuration config + DataFlowCall call, ArgumentNodeExt arg, ParameterNodeExt p, Configuration config ) { viableParamArgNodeCand1(call, p, arg, config) and Stage1::revFlow(p, config) and @@ -735,7 +734,7 @@ private predicate flowOutOfCallNodeCand1( */ pragma[nomagic] private predicate flowIntoCallNodeCand1( - DataFlowCall call, ArgumentNode arg, ParameterNode p, boolean allowsFieldFlow, + DataFlowCall call, ArgumentNodeExt arg, ParameterNodeExt p, boolean allowsFieldFlow, Configuration config ) { flowIntoCallNodeCand1(call, arg, p, config) and @@ -944,10 +943,10 @@ private module Stage2 { pragma[nomagic] private predicate fwdFlowIn( - DataFlowCall call, ParameterNode p, Cc outercc, Cc innercc, ApOption argAp, Ap ap, + DataFlowCall call, ParameterNodeExt p, Cc outercc, Cc innercc, ApOption argAp, Ap ap, Configuration config ) { - exists(ArgumentNode arg, boolean allowsFieldFlow | + exists(ArgumentNodeExt arg, boolean allowsFieldFlow | fwdFlow(arg, outercc, argAp, ap, config) and flowIntoCall(call, arg, p, allowsFieldFlow, config) and innercc = getCallContextCall(call, getNodeEnclosingCallable(p), outercc) @@ -992,7 +991,7 @@ private module Stage2 { private predicate fwdFlowIsEntered( DataFlowCall call, Cc cc, ApOption argAp, Ap ap, Configuration config ) { - exists(ParameterNode p | + exists(ParameterNodeExt p | fwdFlowIn(call, p, cc, _, argAp, ap, config) and PrevStage::parameterMayFlowThrough(p, _, getApprox(ap), config) ) @@ -1133,10 +1132,10 @@ private module Stage2 { pragma[nomagic] private predicate revFlowIn( - DataFlowCall call, ArgumentNode arg, boolean toReturn, ApOption returnAp, Ap ap, + DataFlowCall call, ArgumentNodeExt arg, boolean toReturn, ApOption returnAp, Ap ap, Configuration config ) { - exists(ParameterNode p, boolean allowsFieldFlow | + exists(ParameterNodeExt p, boolean allowsFieldFlow | revFlow(p, toReturn, returnAp, ap, config) and flowIntoCall(call, arg, p, allowsFieldFlow, config) | @@ -1146,7 +1145,7 @@ private module Stage2 { pragma[nomagic] private predicate revFlowInToReturn( - DataFlowCall call, ArgumentNode arg, Ap returnAp, Ap ap, Configuration config + DataFlowCall call, ArgumentNodeExt arg, Ap returnAp, Ap ap, Configuration config ) { revFlowIn(call, arg, true, apSome(returnAp), ap, config) } @@ -1199,13 +1198,15 @@ private module Stage2 { pragma[noinline] private predicate parameterFlow( - ParameterNode p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config + ParameterNodeExt p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config ) { revFlow(p, true, apSome(ap0), ap, config) and c = getNodeEnclosingCallable(p) } - predicate parameterMayFlowThrough(ParameterNode p, DataFlowCallable c, Ap ap, Configuration config) { + predicate parameterMayFlowThrough( + ParameterNodeExt p, DataFlowCallable c, Ap ap, Configuration config + ) { exists(ReturnNodeExt ret, Ap ap0, ReturnKindExt kind, int pos | parameterFlow(p, ap, ap0, c, config) and c = getNodeEnclosingCallable(ret) and @@ -1245,7 +1246,7 @@ private predicate flowOutOfCallNodeCand2( pragma[nomagic] private predicate flowIntoCallNodeCand2( - DataFlowCall call, ArgumentNode node1, ParameterNode node2, boolean allowsFieldFlow, + DataFlowCall call, ArgumentNodeExt node1, ParameterNodeExt node2, boolean allowsFieldFlow, Configuration config ) { flowIntoCallNodeCand1(call, node1, node2, allowsFieldFlow, config) and @@ -1260,8 +1261,8 @@ private module LocalFlowBigStep { */ private class FlowCheckNode extends Node { FlowCheckNode() { - this instanceof CastNode or - clearsContent(this, _) + castNode(this) or + clearsContentCached(this, _) } } @@ -1275,7 +1276,7 @@ private module LocalFlowBigStep { config.isSource(node) or jumpStep(_, node, config) or additionalJumpStep(_, node, config) or - node instanceof ParameterNode or + node instanceof ParameterNodeExt or node instanceof OutNodeExt or store(_, _, node, _) or read(_, _, node) or @@ -1321,21 +1322,21 @@ private module LocalFlowBigStep { Node node1, Node node2, boolean preservesValue, DataFlowType t, Configuration config, LocalCallContext cc ) { - not isUnreachableInCall(node2, cc.(LocalCallContextSpecificCall).getCall()) and + not isUnreachableInCallCached(node2, cc.(LocalCallContextSpecificCall).getCall()) and ( localFlowEntry(node1, pragma[only_bind_into](config)) and ( localFlowStepNodeCand1(node1, node2, config) and preservesValue = true and - t = getNodeType(node1) + t = getNodeDataFlowType(node1) or additionalLocalFlowStepNodeCand2(node1, node2, config) and preservesValue = false and - t = getNodeType(node2) + t = getNodeDataFlowType(node2) ) and node1 != node2 and cc.relevantFor(getNodeEnclosingCallable(node1)) and - not isUnreachableInCall(node1, cc.(LocalCallContextSpecificCall).getCall()) and + not isUnreachableInCallCached(node1, cc.(LocalCallContextSpecificCall).getCall()) and Stage2::revFlow(node2, pragma[only_bind_into](config)) or exists(Node mid | @@ -1350,7 +1351,7 @@ private module LocalFlowBigStep { additionalLocalFlowStepNodeCand2(mid, node2, config) and not mid instanceof FlowCheckNode and preservesValue = false and - t = getNodeType(node2) and + t = getNodeDataFlowType(node2) and Stage2::revFlow(node2, pragma[only_bind_into](config)) ) ) @@ -1384,7 +1385,7 @@ private module Stage3 { private ApApprox getApprox(Ap ap) { result = ap.toBoolNonEmpty() } private ApNil getApNil(Node node) { - PrevStage::revFlow(node, _) and result = TFrontNil(getNodeType(node)) + PrevStage::revFlow(node, _) and result = TFrontNil(getNodeDataFlowType(node)) } bindingset[tc, tail] @@ -1443,7 +1444,9 @@ private module Stage3 { bindingset[node, ap] private predicate filter(Node node, Ap ap) { not ap.isClearedAt(node) and - if node instanceof CastingNode then compatibleTypes(getNodeType(node), ap.getType()) else any() + if node instanceof CastingNode + then compatibleTypes(getNodeDataFlowType(node), ap.getType()) + else any() } bindingset[ap, contentType] @@ -1583,10 +1586,10 @@ private module Stage3 { pragma[nomagic] private predicate fwdFlowIn( - DataFlowCall call, ParameterNode p, Cc outercc, Cc innercc, ApOption argAp, Ap ap, + DataFlowCall call, ParameterNodeExt p, Cc outercc, Cc innercc, ApOption argAp, Ap ap, Configuration config ) { - exists(ArgumentNode arg, boolean allowsFieldFlow | + exists(ArgumentNodeExt arg, boolean allowsFieldFlow | fwdFlow(arg, outercc, argAp, ap, config) and flowIntoCall(call, arg, p, allowsFieldFlow, config) and innercc = getCallContextCall(call, getNodeEnclosingCallable(p), outercc) @@ -1631,7 +1634,7 @@ private module Stage3 { private predicate fwdFlowIsEntered( DataFlowCall call, Cc cc, ApOption argAp, Ap ap, Configuration config ) { - exists(ParameterNode p | + exists(ParameterNodeExt p | fwdFlowIn(call, p, cc, _, argAp, ap, config) and PrevStage::parameterMayFlowThrough(p, _, unbindApa(getApprox(ap)), config) ) @@ -1772,10 +1775,10 @@ private module Stage3 { pragma[nomagic] private predicate revFlowIn( - DataFlowCall call, ArgumentNode arg, boolean toReturn, ApOption returnAp, Ap ap, + DataFlowCall call, ArgumentNodeExt arg, boolean toReturn, ApOption returnAp, Ap ap, Configuration config ) { - exists(ParameterNode p, boolean allowsFieldFlow | + exists(ParameterNodeExt p, boolean allowsFieldFlow | revFlow(p, toReturn, returnAp, ap, config) and flowIntoCall(call, arg, p, allowsFieldFlow, config) | @@ -1785,7 +1788,7 @@ private module Stage3 { pragma[nomagic] private predicate revFlowInToReturn( - DataFlowCall call, ArgumentNode arg, Ap returnAp, Ap ap, Configuration config + DataFlowCall call, ArgumentNodeExt arg, Ap returnAp, Ap ap, Configuration config ) { revFlowIn(call, arg, true, apSome(returnAp), ap, config) } @@ -1838,13 +1841,15 @@ private module Stage3 { pragma[noinline] private predicate parameterFlow( - ParameterNode p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config + ParameterNodeExt p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config ) { revFlow(p, true, apSome(ap0), ap, config) and c = getNodeEnclosingCallable(p) } - predicate parameterMayFlowThrough(ParameterNode p, DataFlowCallable c, Ap ap, Configuration config) { + predicate parameterMayFlowThrough( + ParameterNodeExt p, DataFlowCallable c, Ap ap, Configuration config + ) { exists(ReturnNodeExt ret, Ap ap0, ReturnKindExt kind, int pos | parameterFlow(p, ap, ap0, c, config) and c = getNodeEnclosingCallable(ret) and @@ -2088,7 +2093,7 @@ private module Stage4 { private ApApprox getApprox(Ap ap) { result = ap.getFront() } private ApNil getApNil(Node node) { - PrevStage::revFlow(node, _) and result = TNil(getNodeType(node)) + PrevStage::revFlow(node, _) and result = TNil(getNodeDataFlowType(node)) } bindingset[tc, tail] @@ -2155,7 +2160,7 @@ private module Stage4 { pragma[nomagic] private predicate flowIntoCall( - DataFlowCall call, ArgumentNode node1, ParameterNode node2, boolean allowsFieldFlow, + DataFlowCall call, ArgumentNodeExt node1, ParameterNodeExt node2, boolean allowsFieldFlow, Configuration config ) { flowIntoCallNodeCand2(call, node1, node2, allowsFieldFlow, config) and @@ -2300,10 +2305,10 @@ private module Stage4 { pragma[nomagic] private predicate fwdFlowIn( - DataFlowCall call, ParameterNode p, Cc outercc, Cc innercc, ApOption argAp, Ap ap, + DataFlowCall call, ParameterNodeExt p, Cc outercc, Cc innercc, ApOption argAp, Ap ap, Configuration config ) { - exists(ArgumentNode arg, boolean allowsFieldFlow | + exists(ArgumentNodeExt arg, boolean allowsFieldFlow | fwdFlow(arg, outercc, argAp, ap, config) and flowIntoCall(call, arg, p, allowsFieldFlow, config) and innercc = getCallContextCall(call, getNodeEnclosingCallable(p), outercc) @@ -2348,7 +2353,7 @@ private module Stage4 { private predicate fwdFlowIsEntered( DataFlowCall call, Cc cc, ApOption argAp, Ap ap, Configuration config ) { - exists(ParameterNode p | + exists(ParameterNodeExt p | fwdFlowIn(call, p, cc, _, argAp, ap, config) and PrevStage::parameterMayFlowThrough(p, _, unbindApa(getApprox(ap)), config) ) @@ -2489,10 +2494,10 @@ private module Stage4 { pragma[nomagic] private predicate revFlowIn( - DataFlowCall call, ArgumentNode arg, boolean toReturn, ApOption returnAp, Ap ap, + DataFlowCall call, ArgumentNodeExt arg, boolean toReturn, ApOption returnAp, Ap ap, Configuration config ) { - exists(ParameterNode p, boolean allowsFieldFlow | + exists(ParameterNodeExt p, boolean allowsFieldFlow | revFlow(p, toReturn, returnAp, ap, config) and flowIntoCall(call, arg, p, allowsFieldFlow, config) | @@ -2502,7 +2507,7 @@ private module Stage4 { pragma[nomagic] private predicate revFlowInToReturn( - DataFlowCall call, ArgumentNode arg, Ap returnAp, Ap ap, Configuration config + DataFlowCall call, ArgumentNodeExt arg, Ap returnAp, Ap ap, Configuration config ) { revFlowIn(call, arg, true, apSome(returnAp), ap, config) } @@ -2555,13 +2560,15 @@ private module Stage4 { pragma[noinline] private predicate parameterFlow( - ParameterNode p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config + ParameterNodeExt p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config ) { revFlow(p, true, apSome(ap0), ap, config) and c = getNodeEnclosingCallable(p) } - predicate parameterMayFlowThrough(ParameterNode p, DataFlowCallable c, Ap ap, Configuration config) { + predicate parameterMayFlowThrough( + ParameterNodeExt p, DataFlowCallable c, Ap ap, Configuration config + ) { exists(ReturnNodeExt ret, Ap ap0, ReturnKindExt kind, int pos | parameterFlow(p, ap, ap0, c, config) and c = getNodeEnclosingCallable(ret) and @@ -2606,7 +2613,7 @@ private predicate nodeMayUseSummary(Node n, AccessPathApprox apa, Configuration private newtype TSummaryCtx = TSummaryCtxNone() or - TSummaryCtxSome(ParameterNode p, AccessPath ap) { + TSummaryCtxSome(ParameterNodeExt p, AccessPath ap) { Stage4::parameterMayFlowThrough(p, _, ap.getApprox(), _) } @@ -2627,7 +2634,7 @@ private class SummaryCtxNone extends SummaryCtx, TSummaryCtxNone { /** A summary context from which a flow summary can be generated. */ private class SummaryCtxSome extends SummaryCtx, TSummaryCtxSome { - private ParameterNode p; + private ParameterNodeExt p; private AccessPath ap; SummaryCtxSome() { this = TSummaryCtxSome(p, ap) } @@ -2758,7 +2765,7 @@ private newtype TPathNode = config.isSource(node) and cc instanceof CallContextAny and sc instanceof SummaryCtxNone and - ap = TAccessPathNil(getNodeType(node)) + ap = TAccessPathNil(getNodeDataFlowType(node)) or // ... or a step from an existing PathNode to another node. exists(PathNodeMid mid | @@ -2979,7 +2986,7 @@ class PathNode extends TPathNode { Configuration getConfiguration() { none() } private predicate isHidden() { - nodeIsHidden(this.getNode()) and + hiddenNode(this.getNode()) and not this.isSource() and not this instanceof PathNodeSink } @@ -3148,7 +3155,7 @@ private predicate pathStep(PathNodeMid mid, Node node, CallContext cc, SummaryCt cc instanceof CallContextAny and sc instanceof SummaryCtxNone and mid.getAp() instanceof AccessPathNil and - ap = TAccessPathNil(getNodeType(node)) + ap = TAccessPathNil(getNodeDataFlowType(node)) or exists(TypedContent tc | pathStoreStep(mid, node, ap.pop(tc), tc, cc)) and sc = mid.getSummaryCtx() @@ -3235,7 +3242,7 @@ pragma[noinline] private predicate pathIntoArg( PathNodeMid mid, int i, CallContext cc, DataFlowCall call, AccessPath ap, AccessPathApprox apa ) { - exists(ArgumentNode arg | + exists(ArgumentNodeExt arg | arg = mid.getNode() and cc = mid.getCallContext() and arg.argumentOf(call, i) and @@ -3248,7 +3255,7 @@ pragma[noinline] private predicate parameterCand( DataFlowCallable callable, int i, AccessPathApprox apa, Configuration config ) { - exists(ParameterNode p | + exists(ParameterNodeExt p | Stage4::revFlow(p, _, _, apa, config) and p.isParameterOf(callable, i) ) @@ -3272,7 +3279,7 @@ private predicate pathIntoCallable0( * respectively. */ private predicate pathIntoCallable( - PathNodeMid mid, ParameterNode p, CallContext outercc, CallContextCall innercc, SummaryCtx sc, + PathNodeMid mid, ParameterNodeExt p, CallContext outercc, CallContextCall innercc, SummaryCtx sc, DataFlowCall call ) { exists(int i, DataFlowCallable callable, AccessPath ap | @@ -3568,7 +3575,7 @@ private module FlowExploration { private newtype TSummaryCtx1 = TSummaryCtx1None() or - TSummaryCtx1Param(ParameterNode p) + TSummaryCtx1Param(ParameterNodeExt p) private newtype TSummaryCtx2 = TSummaryCtx2None() or @@ -3591,7 +3598,7 @@ private module FlowExploration { cc instanceof CallContextAny and sc1 = TSummaryCtx1None() and sc2 = TSummaryCtx2None() and - ap = TPartialNil(getNodeType(node)) and + ap = TPartialNil(getNodeDataFlowType(node)) and not fullBarrier(node, config) and exists(config.explorationLimit()) or @@ -3611,7 +3618,7 @@ private module FlowExploration { or exists(PartialPathNodeRev mid | revPartialPathStep(mid, node, sc1, sc2, ap, config) and - not clearsContent(node, ap.getHead()) and + not clearsContentCached(node, ap.getHead()) and not fullBarrier(node, config) and distSink(getNodeEnclosingCallable(node), config) <= config.explorationLimit() ) @@ -3625,9 +3632,9 @@ private module FlowExploration { exists(PartialPathNodeFwd mid | partialPathStep(mid, node, cc, sc1, sc2, ap, config) and not fullBarrier(node, config) and - not clearsContent(node, ap.getHead().getContent()) and + not clearsContentCached(node, ap.getHead().getContent()) and if node instanceof CastingNode - then compatibleTypes(getNodeType(node), ap.getType()) + then compatibleTypes(getNodeDataFlowType(node), ap.getType()) else any() ) } @@ -3783,7 +3790,7 @@ private module FlowExploration { PartialPathNodeFwd mid, Node node, CallContext cc, TSummaryCtx1 sc1, TSummaryCtx2 sc2, PartialAccessPath ap, Configuration config ) { - not isUnreachableInCall(node, cc.(CallContextSpecificCall).getCall()) and + not isUnreachableInCallCached(node, cc.(CallContextSpecificCall).getCall()) and ( localFlowStep(mid.getNode(), node, config) and cc = mid.getCallContext() and @@ -3797,7 +3804,7 @@ private module FlowExploration { sc1 = mid.getSummaryCtx1() and sc2 = mid.getSummaryCtx2() and mid.getAp() instanceof PartialAccessPathNil and - ap = TPartialNil(getNodeType(node)) and + ap = TPartialNil(getNodeDataFlowType(node)) and config = mid.getConfiguration() ) or @@ -3813,7 +3820,7 @@ private module FlowExploration { sc1 = TSummaryCtx1None() and sc2 = TSummaryCtx2None() and mid.getAp() instanceof PartialAccessPathNil and - ap = TPartialNil(getNodeType(node)) and + ap = TPartialNil(getNodeDataFlowType(node)) and config = mid.getConfiguration() or partialPathStoreStep(mid, _, _, node, ap) and @@ -3827,7 +3834,7 @@ private module FlowExploration { sc1 = mid.getSummaryCtx1() and sc2 = mid.getSummaryCtx2() and apConsFwd(ap, tc, ap0, config) and - compatibleTypes(ap.getType(), getNodeType(node)) + compatibleTypes(ap.getType(), getNodeDataFlowType(node)) ) or partialPathIntoCallable(mid, node, _, cc, sc1, sc2, _, ap, config) @@ -3924,7 +3931,7 @@ private module FlowExploration { PartialPathNodeFwd mid, int i, CallContext cc, DataFlowCall call, PartialAccessPath ap, Configuration config ) { - exists(ArgumentNode arg | + exists(ArgumentNodeExt arg | arg = mid.getNode() and cc = mid.getCallContext() and arg.argumentOf(call, i) and @@ -3943,7 +3950,7 @@ private module FlowExploration { } private predicate partialPathIntoCallable( - PartialPathNodeFwd mid, ParameterNode p, CallContext outercc, CallContextCall innercc, + PartialPathNodeFwd mid, ParameterNodeExt p, CallContext outercc, CallContextCall innercc, TSummaryCtx1 sc1, TSummaryCtx2 sc2, DataFlowCall call, PartialAccessPath ap, Configuration config ) { @@ -3980,7 +3987,7 @@ private module FlowExploration { DataFlowCall call, PartialPathNodeFwd mid, ReturnKindExt kind, CallContext cc, PartialAccessPath ap, Configuration config ) { - exists(ParameterNode p, CallContext innercc, TSummaryCtx1 sc1, TSummaryCtx2 sc2 | + exists(ParameterNodeExt p, CallContext innercc, TSummaryCtx1 sc1, TSummaryCtx2 sc2 | partialPathIntoCallable(mid, p, cc, innercc, sc1, sc2, call, _, config) and paramFlowsThroughInPartialPath(kind, innercc, sc1, sc2, ap, config) ) @@ -4037,7 +4044,7 @@ private module FlowExploration { apConsRev(ap, c, ap0, config) ) or - exists(ParameterNode p | + exists(ParameterNodeExt p | mid.getNode() = p and viableParamArg(_, p, node) and sc1 = mid.getSummaryCtx1() and @@ -4115,7 +4122,7 @@ private module FlowExploration { int pos, TRevSummaryCtx1Some sc1, TRevSummaryCtx2Some sc2, RevPartialAccessPath ap, Configuration config ) { - exists(PartialPathNodeRev mid, ParameterNode p | + exists(PartialPathNodeRev mid, ParameterNodeExt p | mid.getNode() = p and p.isParameterOf(_, pos) and sc1 = mid.getSummaryCtx1() and @@ -4138,7 +4145,7 @@ private module FlowExploration { pragma[nomagic] private predicate revPartialPathThroughCallable( - PartialPathNodeRev mid, ArgumentNode node, RevPartialAccessPath ap, Configuration config + PartialPathNodeRev mid, ArgumentNodeExt node, RevPartialAccessPath ap, Configuration config ) { exists(DataFlowCall call, int pos | revPartialPathThroughCallable0(call, mid, pos, ap, config) and 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 9498e51e7e6..cf83530bcbd 100644 --- a/java/ql/src/semmle/code/java/dataflow/internal/DataFlowImpl3.qll +++ b/java/ql/src/semmle/code/java/dataflow/internal/DataFlowImpl3.qll @@ -211,10 +211,7 @@ private predicate fullBarrier(Node node, Configuration config) { * Holds if data can flow in one local step from `node1` to `node2`. */ private predicate localFlowStep(Node node1, Node node2, Configuration config) { - ( - simpleLocalFlowStep(node1, node2) or - reverseStepThroughInputOutputAlias(node1, node2) - ) and + simpleLocalFlowStepExt(node1, node2) and not outBarrier(node1, config) and not inBarrier(node2, config) and not fullBarrier(node1, config) and @@ -237,7 +234,7 @@ private predicate additionalLocalFlowStep(Node node1, Node node2, Configuration * Holds if data can flow from `node1` to `node2` in a way that discards call contexts. */ private predicate jumpStep(Node node1, Node node2, Configuration config) { - jumpStep(node1, node2) and + jumpStepCached(node1, node2) and not outBarrier(node1, config) and not inBarrier(node2, config) and not fullBarrier(node1, config) and @@ -388,7 +385,7 @@ private module Stage1 { */ pragma[nomagic] private predicate fwdFlowIsEntered(DataFlowCall call, Cc cc, Configuration config) { - exists(ArgumentNode arg | + exists(ArgumentNodeExt arg | fwdFlow(arg, cc, config) and viableParamArg(call, _, arg) ) @@ -515,7 +512,7 @@ private module Stage1 { pragma[nomagic] predicate viableParamArgNodeCandFwd1( - DataFlowCall call, ParameterNode p, ArgumentNode arg, Configuration config + DataFlowCall call, ParameterNodeExt p, ArgumentNodeExt arg, Configuration config ) { viableParamArg(call, p, arg) and fwdFlow(arg, config) @@ -523,16 +520,16 @@ private module Stage1 { pragma[nomagic] private predicate revFlowIn( - DataFlowCall call, ArgumentNode arg, boolean toReturn, Configuration config + DataFlowCall call, ArgumentNodeExt arg, boolean toReturn, Configuration config ) { - exists(ParameterNode p | + exists(ParameterNodeExt p | revFlow(p, toReturn, config) and viableParamArgNodeCandFwd1(call, p, arg, config) ) } pragma[nomagic] - private predicate revFlowInToReturn(DataFlowCall call, ArgumentNode arg, Configuration config) { + private predicate revFlowInToReturn(DataFlowCall call, ArgumentNodeExt arg, Configuration config) { revFlowIn(call, arg, true, config) } @@ -597,7 +594,9 @@ private module Stage1 { * Holds if flow may enter through `p` and reach a return node making `p` a * candidate for the origin of a summary. */ - predicate parameterMayFlowThrough(ParameterNode p, DataFlowCallable c, Ap ap, Configuration config) { + predicate parameterMayFlowThrough( + ParameterNodeExt p, DataFlowCallable c, Ap ap, Configuration config + ) { exists(ReturnKindExt kind | throughFlowNodeCand(p, config) and returnFlowCallableNodeCand(c, kind, config) and @@ -663,7 +662,7 @@ private predicate flowOutOfCallNodeCand1( pragma[nomagic] private predicate viableParamArgNodeCand1( - DataFlowCall call, ParameterNode p, ArgumentNode arg, Configuration config + DataFlowCall call, ParameterNodeExt p, ArgumentNodeExt arg, Configuration config ) { Stage1::viableParamArgNodeCandFwd1(call, p, arg, config) and Stage1::revFlow(arg, config) @@ -675,7 +674,7 @@ private predicate viableParamArgNodeCand1( */ pragma[nomagic] private predicate flowIntoCallNodeCand1( - DataFlowCall call, ArgumentNode arg, ParameterNode p, Configuration config + DataFlowCall call, ArgumentNodeExt arg, ParameterNodeExt p, Configuration config ) { viableParamArgNodeCand1(call, p, arg, config) and Stage1::revFlow(p, config) and @@ -735,7 +734,7 @@ private predicate flowOutOfCallNodeCand1( */ pragma[nomagic] private predicate flowIntoCallNodeCand1( - DataFlowCall call, ArgumentNode arg, ParameterNode p, boolean allowsFieldFlow, + DataFlowCall call, ArgumentNodeExt arg, ParameterNodeExt p, boolean allowsFieldFlow, Configuration config ) { flowIntoCallNodeCand1(call, arg, p, config) and @@ -944,10 +943,10 @@ private module Stage2 { pragma[nomagic] private predicate fwdFlowIn( - DataFlowCall call, ParameterNode p, Cc outercc, Cc innercc, ApOption argAp, Ap ap, + DataFlowCall call, ParameterNodeExt p, Cc outercc, Cc innercc, ApOption argAp, Ap ap, Configuration config ) { - exists(ArgumentNode arg, boolean allowsFieldFlow | + exists(ArgumentNodeExt arg, boolean allowsFieldFlow | fwdFlow(arg, outercc, argAp, ap, config) and flowIntoCall(call, arg, p, allowsFieldFlow, config) and innercc = getCallContextCall(call, getNodeEnclosingCallable(p), outercc) @@ -992,7 +991,7 @@ private module Stage2 { private predicate fwdFlowIsEntered( DataFlowCall call, Cc cc, ApOption argAp, Ap ap, Configuration config ) { - exists(ParameterNode p | + exists(ParameterNodeExt p | fwdFlowIn(call, p, cc, _, argAp, ap, config) and PrevStage::parameterMayFlowThrough(p, _, getApprox(ap), config) ) @@ -1133,10 +1132,10 @@ private module Stage2 { pragma[nomagic] private predicate revFlowIn( - DataFlowCall call, ArgumentNode arg, boolean toReturn, ApOption returnAp, Ap ap, + DataFlowCall call, ArgumentNodeExt arg, boolean toReturn, ApOption returnAp, Ap ap, Configuration config ) { - exists(ParameterNode p, boolean allowsFieldFlow | + exists(ParameterNodeExt p, boolean allowsFieldFlow | revFlow(p, toReturn, returnAp, ap, config) and flowIntoCall(call, arg, p, allowsFieldFlow, config) | @@ -1146,7 +1145,7 @@ private module Stage2 { pragma[nomagic] private predicate revFlowInToReturn( - DataFlowCall call, ArgumentNode arg, Ap returnAp, Ap ap, Configuration config + DataFlowCall call, ArgumentNodeExt arg, Ap returnAp, Ap ap, Configuration config ) { revFlowIn(call, arg, true, apSome(returnAp), ap, config) } @@ -1199,13 +1198,15 @@ private module Stage2 { pragma[noinline] private predicate parameterFlow( - ParameterNode p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config + ParameterNodeExt p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config ) { revFlow(p, true, apSome(ap0), ap, config) and c = getNodeEnclosingCallable(p) } - predicate parameterMayFlowThrough(ParameterNode p, DataFlowCallable c, Ap ap, Configuration config) { + predicate parameterMayFlowThrough( + ParameterNodeExt p, DataFlowCallable c, Ap ap, Configuration config + ) { exists(ReturnNodeExt ret, Ap ap0, ReturnKindExt kind, int pos | parameterFlow(p, ap, ap0, c, config) and c = getNodeEnclosingCallable(ret) and @@ -1245,7 +1246,7 @@ private predicate flowOutOfCallNodeCand2( pragma[nomagic] private predicate flowIntoCallNodeCand2( - DataFlowCall call, ArgumentNode node1, ParameterNode node2, boolean allowsFieldFlow, + DataFlowCall call, ArgumentNodeExt node1, ParameterNodeExt node2, boolean allowsFieldFlow, Configuration config ) { flowIntoCallNodeCand1(call, node1, node2, allowsFieldFlow, config) and @@ -1260,8 +1261,8 @@ private module LocalFlowBigStep { */ private class FlowCheckNode extends Node { FlowCheckNode() { - this instanceof CastNode or - clearsContent(this, _) + castNode(this) or + clearsContentCached(this, _) } } @@ -1275,7 +1276,7 @@ private module LocalFlowBigStep { config.isSource(node) or jumpStep(_, node, config) or additionalJumpStep(_, node, config) or - node instanceof ParameterNode or + node instanceof ParameterNodeExt or node instanceof OutNodeExt or store(_, _, node, _) or read(_, _, node) or @@ -1321,21 +1322,21 @@ private module LocalFlowBigStep { Node node1, Node node2, boolean preservesValue, DataFlowType t, Configuration config, LocalCallContext cc ) { - not isUnreachableInCall(node2, cc.(LocalCallContextSpecificCall).getCall()) and + not isUnreachableInCallCached(node2, cc.(LocalCallContextSpecificCall).getCall()) and ( localFlowEntry(node1, pragma[only_bind_into](config)) and ( localFlowStepNodeCand1(node1, node2, config) and preservesValue = true and - t = getNodeType(node1) + t = getNodeDataFlowType(node1) or additionalLocalFlowStepNodeCand2(node1, node2, config) and preservesValue = false and - t = getNodeType(node2) + t = getNodeDataFlowType(node2) ) and node1 != node2 and cc.relevantFor(getNodeEnclosingCallable(node1)) and - not isUnreachableInCall(node1, cc.(LocalCallContextSpecificCall).getCall()) and + not isUnreachableInCallCached(node1, cc.(LocalCallContextSpecificCall).getCall()) and Stage2::revFlow(node2, pragma[only_bind_into](config)) or exists(Node mid | @@ -1350,7 +1351,7 @@ private module LocalFlowBigStep { additionalLocalFlowStepNodeCand2(mid, node2, config) and not mid instanceof FlowCheckNode and preservesValue = false and - t = getNodeType(node2) and + t = getNodeDataFlowType(node2) and Stage2::revFlow(node2, pragma[only_bind_into](config)) ) ) @@ -1384,7 +1385,7 @@ private module Stage3 { private ApApprox getApprox(Ap ap) { result = ap.toBoolNonEmpty() } private ApNil getApNil(Node node) { - PrevStage::revFlow(node, _) and result = TFrontNil(getNodeType(node)) + PrevStage::revFlow(node, _) and result = TFrontNil(getNodeDataFlowType(node)) } bindingset[tc, tail] @@ -1443,7 +1444,9 @@ private module Stage3 { bindingset[node, ap] private predicate filter(Node node, Ap ap) { not ap.isClearedAt(node) and - if node instanceof CastingNode then compatibleTypes(getNodeType(node), ap.getType()) else any() + if node instanceof CastingNode + then compatibleTypes(getNodeDataFlowType(node), ap.getType()) + else any() } bindingset[ap, contentType] @@ -1583,10 +1586,10 @@ private module Stage3 { pragma[nomagic] private predicate fwdFlowIn( - DataFlowCall call, ParameterNode p, Cc outercc, Cc innercc, ApOption argAp, Ap ap, + DataFlowCall call, ParameterNodeExt p, Cc outercc, Cc innercc, ApOption argAp, Ap ap, Configuration config ) { - exists(ArgumentNode arg, boolean allowsFieldFlow | + exists(ArgumentNodeExt arg, boolean allowsFieldFlow | fwdFlow(arg, outercc, argAp, ap, config) and flowIntoCall(call, arg, p, allowsFieldFlow, config) and innercc = getCallContextCall(call, getNodeEnclosingCallable(p), outercc) @@ -1631,7 +1634,7 @@ private module Stage3 { private predicate fwdFlowIsEntered( DataFlowCall call, Cc cc, ApOption argAp, Ap ap, Configuration config ) { - exists(ParameterNode p | + exists(ParameterNodeExt p | fwdFlowIn(call, p, cc, _, argAp, ap, config) and PrevStage::parameterMayFlowThrough(p, _, unbindApa(getApprox(ap)), config) ) @@ -1772,10 +1775,10 @@ private module Stage3 { pragma[nomagic] private predicate revFlowIn( - DataFlowCall call, ArgumentNode arg, boolean toReturn, ApOption returnAp, Ap ap, + DataFlowCall call, ArgumentNodeExt arg, boolean toReturn, ApOption returnAp, Ap ap, Configuration config ) { - exists(ParameterNode p, boolean allowsFieldFlow | + exists(ParameterNodeExt p, boolean allowsFieldFlow | revFlow(p, toReturn, returnAp, ap, config) and flowIntoCall(call, arg, p, allowsFieldFlow, config) | @@ -1785,7 +1788,7 @@ private module Stage3 { pragma[nomagic] private predicate revFlowInToReturn( - DataFlowCall call, ArgumentNode arg, Ap returnAp, Ap ap, Configuration config + DataFlowCall call, ArgumentNodeExt arg, Ap returnAp, Ap ap, Configuration config ) { revFlowIn(call, arg, true, apSome(returnAp), ap, config) } @@ -1838,13 +1841,15 @@ private module Stage3 { pragma[noinline] private predicate parameterFlow( - ParameterNode p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config + ParameterNodeExt p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config ) { revFlow(p, true, apSome(ap0), ap, config) and c = getNodeEnclosingCallable(p) } - predicate parameterMayFlowThrough(ParameterNode p, DataFlowCallable c, Ap ap, Configuration config) { + predicate parameterMayFlowThrough( + ParameterNodeExt p, DataFlowCallable c, Ap ap, Configuration config + ) { exists(ReturnNodeExt ret, Ap ap0, ReturnKindExt kind, int pos | parameterFlow(p, ap, ap0, c, config) and c = getNodeEnclosingCallable(ret) and @@ -2088,7 +2093,7 @@ private module Stage4 { private ApApprox getApprox(Ap ap) { result = ap.getFront() } private ApNil getApNil(Node node) { - PrevStage::revFlow(node, _) and result = TNil(getNodeType(node)) + PrevStage::revFlow(node, _) and result = TNil(getNodeDataFlowType(node)) } bindingset[tc, tail] @@ -2155,7 +2160,7 @@ private module Stage4 { pragma[nomagic] private predicate flowIntoCall( - DataFlowCall call, ArgumentNode node1, ParameterNode node2, boolean allowsFieldFlow, + DataFlowCall call, ArgumentNodeExt node1, ParameterNodeExt node2, boolean allowsFieldFlow, Configuration config ) { flowIntoCallNodeCand2(call, node1, node2, allowsFieldFlow, config) and @@ -2300,10 +2305,10 @@ private module Stage4 { pragma[nomagic] private predicate fwdFlowIn( - DataFlowCall call, ParameterNode p, Cc outercc, Cc innercc, ApOption argAp, Ap ap, + DataFlowCall call, ParameterNodeExt p, Cc outercc, Cc innercc, ApOption argAp, Ap ap, Configuration config ) { - exists(ArgumentNode arg, boolean allowsFieldFlow | + exists(ArgumentNodeExt arg, boolean allowsFieldFlow | fwdFlow(arg, outercc, argAp, ap, config) and flowIntoCall(call, arg, p, allowsFieldFlow, config) and innercc = getCallContextCall(call, getNodeEnclosingCallable(p), outercc) @@ -2348,7 +2353,7 @@ private module Stage4 { private predicate fwdFlowIsEntered( DataFlowCall call, Cc cc, ApOption argAp, Ap ap, Configuration config ) { - exists(ParameterNode p | + exists(ParameterNodeExt p | fwdFlowIn(call, p, cc, _, argAp, ap, config) and PrevStage::parameterMayFlowThrough(p, _, unbindApa(getApprox(ap)), config) ) @@ -2489,10 +2494,10 @@ private module Stage4 { pragma[nomagic] private predicate revFlowIn( - DataFlowCall call, ArgumentNode arg, boolean toReturn, ApOption returnAp, Ap ap, + DataFlowCall call, ArgumentNodeExt arg, boolean toReturn, ApOption returnAp, Ap ap, Configuration config ) { - exists(ParameterNode p, boolean allowsFieldFlow | + exists(ParameterNodeExt p, boolean allowsFieldFlow | revFlow(p, toReturn, returnAp, ap, config) and flowIntoCall(call, arg, p, allowsFieldFlow, config) | @@ -2502,7 +2507,7 @@ private module Stage4 { pragma[nomagic] private predicate revFlowInToReturn( - DataFlowCall call, ArgumentNode arg, Ap returnAp, Ap ap, Configuration config + DataFlowCall call, ArgumentNodeExt arg, Ap returnAp, Ap ap, Configuration config ) { revFlowIn(call, arg, true, apSome(returnAp), ap, config) } @@ -2555,13 +2560,15 @@ private module Stage4 { pragma[noinline] private predicate parameterFlow( - ParameterNode p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config + ParameterNodeExt p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config ) { revFlow(p, true, apSome(ap0), ap, config) and c = getNodeEnclosingCallable(p) } - predicate parameterMayFlowThrough(ParameterNode p, DataFlowCallable c, Ap ap, Configuration config) { + predicate parameterMayFlowThrough( + ParameterNodeExt p, DataFlowCallable c, Ap ap, Configuration config + ) { exists(ReturnNodeExt ret, Ap ap0, ReturnKindExt kind, int pos | parameterFlow(p, ap, ap0, c, config) and c = getNodeEnclosingCallable(ret) and @@ -2606,7 +2613,7 @@ private predicate nodeMayUseSummary(Node n, AccessPathApprox apa, Configuration private newtype TSummaryCtx = TSummaryCtxNone() or - TSummaryCtxSome(ParameterNode p, AccessPath ap) { + TSummaryCtxSome(ParameterNodeExt p, AccessPath ap) { Stage4::parameterMayFlowThrough(p, _, ap.getApprox(), _) } @@ -2627,7 +2634,7 @@ private class SummaryCtxNone extends SummaryCtx, TSummaryCtxNone { /** A summary context from which a flow summary can be generated. */ private class SummaryCtxSome extends SummaryCtx, TSummaryCtxSome { - private ParameterNode p; + private ParameterNodeExt p; private AccessPath ap; SummaryCtxSome() { this = TSummaryCtxSome(p, ap) } @@ -2758,7 +2765,7 @@ private newtype TPathNode = config.isSource(node) and cc instanceof CallContextAny and sc instanceof SummaryCtxNone and - ap = TAccessPathNil(getNodeType(node)) + ap = TAccessPathNil(getNodeDataFlowType(node)) or // ... or a step from an existing PathNode to another node. exists(PathNodeMid mid | @@ -2979,7 +2986,7 @@ class PathNode extends TPathNode { Configuration getConfiguration() { none() } private predicate isHidden() { - nodeIsHidden(this.getNode()) and + hiddenNode(this.getNode()) and not this.isSource() and not this instanceof PathNodeSink } @@ -3148,7 +3155,7 @@ private predicate pathStep(PathNodeMid mid, Node node, CallContext cc, SummaryCt cc instanceof CallContextAny and sc instanceof SummaryCtxNone and mid.getAp() instanceof AccessPathNil and - ap = TAccessPathNil(getNodeType(node)) + ap = TAccessPathNil(getNodeDataFlowType(node)) or exists(TypedContent tc | pathStoreStep(mid, node, ap.pop(tc), tc, cc)) and sc = mid.getSummaryCtx() @@ -3235,7 +3242,7 @@ pragma[noinline] private predicate pathIntoArg( PathNodeMid mid, int i, CallContext cc, DataFlowCall call, AccessPath ap, AccessPathApprox apa ) { - exists(ArgumentNode arg | + exists(ArgumentNodeExt arg | arg = mid.getNode() and cc = mid.getCallContext() and arg.argumentOf(call, i) and @@ -3248,7 +3255,7 @@ pragma[noinline] private predicate parameterCand( DataFlowCallable callable, int i, AccessPathApprox apa, Configuration config ) { - exists(ParameterNode p | + exists(ParameterNodeExt p | Stage4::revFlow(p, _, _, apa, config) and p.isParameterOf(callable, i) ) @@ -3272,7 +3279,7 @@ private predicate pathIntoCallable0( * respectively. */ private predicate pathIntoCallable( - PathNodeMid mid, ParameterNode p, CallContext outercc, CallContextCall innercc, SummaryCtx sc, + PathNodeMid mid, ParameterNodeExt p, CallContext outercc, CallContextCall innercc, SummaryCtx sc, DataFlowCall call ) { exists(int i, DataFlowCallable callable, AccessPath ap | @@ -3568,7 +3575,7 @@ private module FlowExploration { private newtype TSummaryCtx1 = TSummaryCtx1None() or - TSummaryCtx1Param(ParameterNode p) + TSummaryCtx1Param(ParameterNodeExt p) private newtype TSummaryCtx2 = TSummaryCtx2None() or @@ -3591,7 +3598,7 @@ private module FlowExploration { cc instanceof CallContextAny and sc1 = TSummaryCtx1None() and sc2 = TSummaryCtx2None() and - ap = TPartialNil(getNodeType(node)) and + ap = TPartialNil(getNodeDataFlowType(node)) and not fullBarrier(node, config) and exists(config.explorationLimit()) or @@ -3611,7 +3618,7 @@ private module FlowExploration { or exists(PartialPathNodeRev mid | revPartialPathStep(mid, node, sc1, sc2, ap, config) and - not clearsContent(node, ap.getHead()) and + not clearsContentCached(node, ap.getHead()) and not fullBarrier(node, config) and distSink(getNodeEnclosingCallable(node), config) <= config.explorationLimit() ) @@ -3625,9 +3632,9 @@ private module FlowExploration { exists(PartialPathNodeFwd mid | partialPathStep(mid, node, cc, sc1, sc2, ap, config) and not fullBarrier(node, config) and - not clearsContent(node, ap.getHead().getContent()) and + not clearsContentCached(node, ap.getHead().getContent()) and if node instanceof CastingNode - then compatibleTypes(getNodeType(node), ap.getType()) + then compatibleTypes(getNodeDataFlowType(node), ap.getType()) else any() ) } @@ -3783,7 +3790,7 @@ private module FlowExploration { PartialPathNodeFwd mid, Node node, CallContext cc, TSummaryCtx1 sc1, TSummaryCtx2 sc2, PartialAccessPath ap, Configuration config ) { - not isUnreachableInCall(node, cc.(CallContextSpecificCall).getCall()) and + not isUnreachableInCallCached(node, cc.(CallContextSpecificCall).getCall()) and ( localFlowStep(mid.getNode(), node, config) and cc = mid.getCallContext() and @@ -3797,7 +3804,7 @@ private module FlowExploration { sc1 = mid.getSummaryCtx1() and sc2 = mid.getSummaryCtx2() and mid.getAp() instanceof PartialAccessPathNil and - ap = TPartialNil(getNodeType(node)) and + ap = TPartialNil(getNodeDataFlowType(node)) and config = mid.getConfiguration() ) or @@ -3813,7 +3820,7 @@ private module FlowExploration { sc1 = TSummaryCtx1None() and sc2 = TSummaryCtx2None() and mid.getAp() instanceof PartialAccessPathNil and - ap = TPartialNil(getNodeType(node)) and + ap = TPartialNil(getNodeDataFlowType(node)) and config = mid.getConfiguration() or partialPathStoreStep(mid, _, _, node, ap) and @@ -3827,7 +3834,7 @@ private module FlowExploration { sc1 = mid.getSummaryCtx1() and sc2 = mid.getSummaryCtx2() and apConsFwd(ap, tc, ap0, config) and - compatibleTypes(ap.getType(), getNodeType(node)) + compatibleTypes(ap.getType(), getNodeDataFlowType(node)) ) or partialPathIntoCallable(mid, node, _, cc, sc1, sc2, _, ap, config) @@ -3924,7 +3931,7 @@ private module FlowExploration { PartialPathNodeFwd mid, int i, CallContext cc, DataFlowCall call, PartialAccessPath ap, Configuration config ) { - exists(ArgumentNode arg | + exists(ArgumentNodeExt arg | arg = mid.getNode() and cc = mid.getCallContext() and arg.argumentOf(call, i) and @@ -3943,7 +3950,7 @@ private module FlowExploration { } private predicate partialPathIntoCallable( - PartialPathNodeFwd mid, ParameterNode p, CallContext outercc, CallContextCall innercc, + PartialPathNodeFwd mid, ParameterNodeExt p, CallContext outercc, CallContextCall innercc, TSummaryCtx1 sc1, TSummaryCtx2 sc2, DataFlowCall call, PartialAccessPath ap, Configuration config ) { @@ -3980,7 +3987,7 @@ private module FlowExploration { DataFlowCall call, PartialPathNodeFwd mid, ReturnKindExt kind, CallContext cc, PartialAccessPath ap, Configuration config ) { - exists(ParameterNode p, CallContext innercc, TSummaryCtx1 sc1, TSummaryCtx2 sc2 | + exists(ParameterNodeExt p, CallContext innercc, TSummaryCtx1 sc1, TSummaryCtx2 sc2 | partialPathIntoCallable(mid, p, cc, innercc, sc1, sc2, call, _, config) and paramFlowsThroughInPartialPath(kind, innercc, sc1, sc2, ap, config) ) @@ -4037,7 +4044,7 @@ private module FlowExploration { apConsRev(ap, c, ap0, config) ) or - exists(ParameterNode p | + exists(ParameterNodeExt p | mid.getNode() = p and viableParamArg(_, p, node) and sc1 = mid.getSummaryCtx1() and @@ -4115,7 +4122,7 @@ private module FlowExploration { int pos, TRevSummaryCtx1Some sc1, TRevSummaryCtx2Some sc2, RevPartialAccessPath ap, Configuration config ) { - exists(PartialPathNodeRev mid, ParameterNode p | + exists(PartialPathNodeRev mid, ParameterNodeExt p | mid.getNode() = p and p.isParameterOf(_, pos) and sc1 = mid.getSummaryCtx1() and @@ -4138,7 +4145,7 @@ private module FlowExploration { pragma[nomagic] private predicate revPartialPathThroughCallable( - PartialPathNodeRev mid, ArgumentNode node, RevPartialAccessPath ap, Configuration config + PartialPathNodeRev mid, ArgumentNodeExt node, RevPartialAccessPath ap, Configuration config ) { exists(DataFlowCall call, int pos | revPartialPathThroughCallable0(call, mid, pos, ap, config) and 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 9498e51e7e6..cf83530bcbd 100644 --- a/java/ql/src/semmle/code/java/dataflow/internal/DataFlowImpl4.qll +++ b/java/ql/src/semmle/code/java/dataflow/internal/DataFlowImpl4.qll @@ -211,10 +211,7 @@ private predicate fullBarrier(Node node, Configuration config) { * Holds if data can flow in one local step from `node1` to `node2`. */ private predicate localFlowStep(Node node1, Node node2, Configuration config) { - ( - simpleLocalFlowStep(node1, node2) or - reverseStepThroughInputOutputAlias(node1, node2) - ) and + simpleLocalFlowStepExt(node1, node2) and not outBarrier(node1, config) and not inBarrier(node2, config) and not fullBarrier(node1, config) and @@ -237,7 +234,7 @@ private predicate additionalLocalFlowStep(Node node1, Node node2, Configuration * Holds if data can flow from `node1` to `node2` in a way that discards call contexts. */ private predicate jumpStep(Node node1, Node node2, Configuration config) { - jumpStep(node1, node2) and + jumpStepCached(node1, node2) and not outBarrier(node1, config) and not inBarrier(node2, config) and not fullBarrier(node1, config) and @@ -388,7 +385,7 @@ private module Stage1 { */ pragma[nomagic] private predicate fwdFlowIsEntered(DataFlowCall call, Cc cc, Configuration config) { - exists(ArgumentNode arg | + exists(ArgumentNodeExt arg | fwdFlow(arg, cc, config) and viableParamArg(call, _, arg) ) @@ -515,7 +512,7 @@ private module Stage1 { pragma[nomagic] predicate viableParamArgNodeCandFwd1( - DataFlowCall call, ParameterNode p, ArgumentNode arg, Configuration config + DataFlowCall call, ParameterNodeExt p, ArgumentNodeExt arg, Configuration config ) { viableParamArg(call, p, arg) and fwdFlow(arg, config) @@ -523,16 +520,16 @@ private module Stage1 { pragma[nomagic] private predicate revFlowIn( - DataFlowCall call, ArgumentNode arg, boolean toReturn, Configuration config + DataFlowCall call, ArgumentNodeExt arg, boolean toReturn, Configuration config ) { - exists(ParameterNode p | + exists(ParameterNodeExt p | revFlow(p, toReturn, config) and viableParamArgNodeCandFwd1(call, p, arg, config) ) } pragma[nomagic] - private predicate revFlowInToReturn(DataFlowCall call, ArgumentNode arg, Configuration config) { + private predicate revFlowInToReturn(DataFlowCall call, ArgumentNodeExt arg, Configuration config) { revFlowIn(call, arg, true, config) } @@ -597,7 +594,9 @@ private module Stage1 { * Holds if flow may enter through `p` and reach a return node making `p` a * candidate for the origin of a summary. */ - predicate parameterMayFlowThrough(ParameterNode p, DataFlowCallable c, Ap ap, Configuration config) { + predicate parameterMayFlowThrough( + ParameterNodeExt p, DataFlowCallable c, Ap ap, Configuration config + ) { exists(ReturnKindExt kind | throughFlowNodeCand(p, config) and returnFlowCallableNodeCand(c, kind, config) and @@ -663,7 +662,7 @@ private predicate flowOutOfCallNodeCand1( pragma[nomagic] private predicate viableParamArgNodeCand1( - DataFlowCall call, ParameterNode p, ArgumentNode arg, Configuration config + DataFlowCall call, ParameterNodeExt p, ArgumentNodeExt arg, Configuration config ) { Stage1::viableParamArgNodeCandFwd1(call, p, arg, config) and Stage1::revFlow(arg, config) @@ -675,7 +674,7 @@ private predicate viableParamArgNodeCand1( */ pragma[nomagic] private predicate flowIntoCallNodeCand1( - DataFlowCall call, ArgumentNode arg, ParameterNode p, Configuration config + DataFlowCall call, ArgumentNodeExt arg, ParameterNodeExt p, Configuration config ) { viableParamArgNodeCand1(call, p, arg, config) and Stage1::revFlow(p, config) and @@ -735,7 +734,7 @@ private predicate flowOutOfCallNodeCand1( */ pragma[nomagic] private predicate flowIntoCallNodeCand1( - DataFlowCall call, ArgumentNode arg, ParameterNode p, boolean allowsFieldFlow, + DataFlowCall call, ArgumentNodeExt arg, ParameterNodeExt p, boolean allowsFieldFlow, Configuration config ) { flowIntoCallNodeCand1(call, arg, p, config) and @@ -944,10 +943,10 @@ private module Stage2 { pragma[nomagic] private predicate fwdFlowIn( - DataFlowCall call, ParameterNode p, Cc outercc, Cc innercc, ApOption argAp, Ap ap, + DataFlowCall call, ParameterNodeExt p, Cc outercc, Cc innercc, ApOption argAp, Ap ap, Configuration config ) { - exists(ArgumentNode arg, boolean allowsFieldFlow | + exists(ArgumentNodeExt arg, boolean allowsFieldFlow | fwdFlow(arg, outercc, argAp, ap, config) and flowIntoCall(call, arg, p, allowsFieldFlow, config) and innercc = getCallContextCall(call, getNodeEnclosingCallable(p), outercc) @@ -992,7 +991,7 @@ private module Stage2 { private predicate fwdFlowIsEntered( DataFlowCall call, Cc cc, ApOption argAp, Ap ap, Configuration config ) { - exists(ParameterNode p | + exists(ParameterNodeExt p | fwdFlowIn(call, p, cc, _, argAp, ap, config) and PrevStage::parameterMayFlowThrough(p, _, getApprox(ap), config) ) @@ -1133,10 +1132,10 @@ private module Stage2 { pragma[nomagic] private predicate revFlowIn( - DataFlowCall call, ArgumentNode arg, boolean toReturn, ApOption returnAp, Ap ap, + DataFlowCall call, ArgumentNodeExt arg, boolean toReturn, ApOption returnAp, Ap ap, Configuration config ) { - exists(ParameterNode p, boolean allowsFieldFlow | + exists(ParameterNodeExt p, boolean allowsFieldFlow | revFlow(p, toReturn, returnAp, ap, config) and flowIntoCall(call, arg, p, allowsFieldFlow, config) | @@ -1146,7 +1145,7 @@ private module Stage2 { pragma[nomagic] private predicate revFlowInToReturn( - DataFlowCall call, ArgumentNode arg, Ap returnAp, Ap ap, Configuration config + DataFlowCall call, ArgumentNodeExt arg, Ap returnAp, Ap ap, Configuration config ) { revFlowIn(call, arg, true, apSome(returnAp), ap, config) } @@ -1199,13 +1198,15 @@ private module Stage2 { pragma[noinline] private predicate parameterFlow( - ParameterNode p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config + ParameterNodeExt p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config ) { revFlow(p, true, apSome(ap0), ap, config) and c = getNodeEnclosingCallable(p) } - predicate parameterMayFlowThrough(ParameterNode p, DataFlowCallable c, Ap ap, Configuration config) { + predicate parameterMayFlowThrough( + ParameterNodeExt p, DataFlowCallable c, Ap ap, Configuration config + ) { exists(ReturnNodeExt ret, Ap ap0, ReturnKindExt kind, int pos | parameterFlow(p, ap, ap0, c, config) and c = getNodeEnclosingCallable(ret) and @@ -1245,7 +1246,7 @@ private predicate flowOutOfCallNodeCand2( pragma[nomagic] private predicate flowIntoCallNodeCand2( - DataFlowCall call, ArgumentNode node1, ParameterNode node2, boolean allowsFieldFlow, + DataFlowCall call, ArgumentNodeExt node1, ParameterNodeExt node2, boolean allowsFieldFlow, Configuration config ) { flowIntoCallNodeCand1(call, node1, node2, allowsFieldFlow, config) and @@ -1260,8 +1261,8 @@ private module LocalFlowBigStep { */ private class FlowCheckNode extends Node { FlowCheckNode() { - this instanceof CastNode or - clearsContent(this, _) + castNode(this) or + clearsContentCached(this, _) } } @@ -1275,7 +1276,7 @@ private module LocalFlowBigStep { config.isSource(node) or jumpStep(_, node, config) or additionalJumpStep(_, node, config) or - node instanceof ParameterNode or + node instanceof ParameterNodeExt or node instanceof OutNodeExt or store(_, _, node, _) or read(_, _, node) or @@ -1321,21 +1322,21 @@ private module LocalFlowBigStep { Node node1, Node node2, boolean preservesValue, DataFlowType t, Configuration config, LocalCallContext cc ) { - not isUnreachableInCall(node2, cc.(LocalCallContextSpecificCall).getCall()) and + not isUnreachableInCallCached(node2, cc.(LocalCallContextSpecificCall).getCall()) and ( localFlowEntry(node1, pragma[only_bind_into](config)) and ( localFlowStepNodeCand1(node1, node2, config) and preservesValue = true and - t = getNodeType(node1) + t = getNodeDataFlowType(node1) or additionalLocalFlowStepNodeCand2(node1, node2, config) and preservesValue = false and - t = getNodeType(node2) + t = getNodeDataFlowType(node2) ) and node1 != node2 and cc.relevantFor(getNodeEnclosingCallable(node1)) and - not isUnreachableInCall(node1, cc.(LocalCallContextSpecificCall).getCall()) and + not isUnreachableInCallCached(node1, cc.(LocalCallContextSpecificCall).getCall()) and Stage2::revFlow(node2, pragma[only_bind_into](config)) or exists(Node mid | @@ -1350,7 +1351,7 @@ private module LocalFlowBigStep { additionalLocalFlowStepNodeCand2(mid, node2, config) and not mid instanceof FlowCheckNode and preservesValue = false and - t = getNodeType(node2) and + t = getNodeDataFlowType(node2) and Stage2::revFlow(node2, pragma[only_bind_into](config)) ) ) @@ -1384,7 +1385,7 @@ private module Stage3 { private ApApprox getApprox(Ap ap) { result = ap.toBoolNonEmpty() } private ApNil getApNil(Node node) { - PrevStage::revFlow(node, _) and result = TFrontNil(getNodeType(node)) + PrevStage::revFlow(node, _) and result = TFrontNil(getNodeDataFlowType(node)) } bindingset[tc, tail] @@ -1443,7 +1444,9 @@ private module Stage3 { bindingset[node, ap] private predicate filter(Node node, Ap ap) { not ap.isClearedAt(node) and - if node instanceof CastingNode then compatibleTypes(getNodeType(node), ap.getType()) else any() + if node instanceof CastingNode + then compatibleTypes(getNodeDataFlowType(node), ap.getType()) + else any() } bindingset[ap, contentType] @@ -1583,10 +1586,10 @@ private module Stage3 { pragma[nomagic] private predicate fwdFlowIn( - DataFlowCall call, ParameterNode p, Cc outercc, Cc innercc, ApOption argAp, Ap ap, + DataFlowCall call, ParameterNodeExt p, Cc outercc, Cc innercc, ApOption argAp, Ap ap, Configuration config ) { - exists(ArgumentNode arg, boolean allowsFieldFlow | + exists(ArgumentNodeExt arg, boolean allowsFieldFlow | fwdFlow(arg, outercc, argAp, ap, config) and flowIntoCall(call, arg, p, allowsFieldFlow, config) and innercc = getCallContextCall(call, getNodeEnclosingCallable(p), outercc) @@ -1631,7 +1634,7 @@ private module Stage3 { private predicate fwdFlowIsEntered( DataFlowCall call, Cc cc, ApOption argAp, Ap ap, Configuration config ) { - exists(ParameterNode p | + exists(ParameterNodeExt p | fwdFlowIn(call, p, cc, _, argAp, ap, config) and PrevStage::parameterMayFlowThrough(p, _, unbindApa(getApprox(ap)), config) ) @@ -1772,10 +1775,10 @@ private module Stage3 { pragma[nomagic] private predicate revFlowIn( - DataFlowCall call, ArgumentNode arg, boolean toReturn, ApOption returnAp, Ap ap, + DataFlowCall call, ArgumentNodeExt arg, boolean toReturn, ApOption returnAp, Ap ap, Configuration config ) { - exists(ParameterNode p, boolean allowsFieldFlow | + exists(ParameterNodeExt p, boolean allowsFieldFlow | revFlow(p, toReturn, returnAp, ap, config) and flowIntoCall(call, arg, p, allowsFieldFlow, config) | @@ -1785,7 +1788,7 @@ private module Stage3 { pragma[nomagic] private predicate revFlowInToReturn( - DataFlowCall call, ArgumentNode arg, Ap returnAp, Ap ap, Configuration config + DataFlowCall call, ArgumentNodeExt arg, Ap returnAp, Ap ap, Configuration config ) { revFlowIn(call, arg, true, apSome(returnAp), ap, config) } @@ -1838,13 +1841,15 @@ private module Stage3 { pragma[noinline] private predicate parameterFlow( - ParameterNode p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config + ParameterNodeExt p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config ) { revFlow(p, true, apSome(ap0), ap, config) and c = getNodeEnclosingCallable(p) } - predicate parameterMayFlowThrough(ParameterNode p, DataFlowCallable c, Ap ap, Configuration config) { + predicate parameterMayFlowThrough( + ParameterNodeExt p, DataFlowCallable c, Ap ap, Configuration config + ) { exists(ReturnNodeExt ret, Ap ap0, ReturnKindExt kind, int pos | parameterFlow(p, ap, ap0, c, config) and c = getNodeEnclosingCallable(ret) and @@ -2088,7 +2093,7 @@ private module Stage4 { private ApApprox getApprox(Ap ap) { result = ap.getFront() } private ApNil getApNil(Node node) { - PrevStage::revFlow(node, _) and result = TNil(getNodeType(node)) + PrevStage::revFlow(node, _) and result = TNil(getNodeDataFlowType(node)) } bindingset[tc, tail] @@ -2155,7 +2160,7 @@ private module Stage4 { pragma[nomagic] private predicate flowIntoCall( - DataFlowCall call, ArgumentNode node1, ParameterNode node2, boolean allowsFieldFlow, + DataFlowCall call, ArgumentNodeExt node1, ParameterNodeExt node2, boolean allowsFieldFlow, Configuration config ) { flowIntoCallNodeCand2(call, node1, node2, allowsFieldFlow, config) and @@ -2300,10 +2305,10 @@ private module Stage4 { pragma[nomagic] private predicate fwdFlowIn( - DataFlowCall call, ParameterNode p, Cc outercc, Cc innercc, ApOption argAp, Ap ap, + DataFlowCall call, ParameterNodeExt p, Cc outercc, Cc innercc, ApOption argAp, Ap ap, Configuration config ) { - exists(ArgumentNode arg, boolean allowsFieldFlow | + exists(ArgumentNodeExt arg, boolean allowsFieldFlow | fwdFlow(arg, outercc, argAp, ap, config) and flowIntoCall(call, arg, p, allowsFieldFlow, config) and innercc = getCallContextCall(call, getNodeEnclosingCallable(p), outercc) @@ -2348,7 +2353,7 @@ private module Stage4 { private predicate fwdFlowIsEntered( DataFlowCall call, Cc cc, ApOption argAp, Ap ap, Configuration config ) { - exists(ParameterNode p | + exists(ParameterNodeExt p | fwdFlowIn(call, p, cc, _, argAp, ap, config) and PrevStage::parameterMayFlowThrough(p, _, unbindApa(getApprox(ap)), config) ) @@ -2489,10 +2494,10 @@ private module Stage4 { pragma[nomagic] private predicate revFlowIn( - DataFlowCall call, ArgumentNode arg, boolean toReturn, ApOption returnAp, Ap ap, + DataFlowCall call, ArgumentNodeExt arg, boolean toReturn, ApOption returnAp, Ap ap, Configuration config ) { - exists(ParameterNode p, boolean allowsFieldFlow | + exists(ParameterNodeExt p, boolean allowsFieldFlow | revFlow(p, toReturn, returnAp, ap, config) and flowIntoCall(call, arg, p, allowsFieldFlow, config) | @@ -2502,7 +2507,7 @@ private module Stage4 { pragma[nomagic] private predicate revFlowInToReturn( - DataFlowCall call, ArgumentNode arg, Ap returnAp, Ap ap, Configuration config + DataFlowCall call, ArgumentNodeExt arg, Ap returnAp, Ap ap, Configuration config ) { revFlowIn(call, arg, true, apSome(returnAp), ap, config) } @@ -2555,13 +2560,15 @@ private module Stage4 { pragma[noinline] private predicate parameterFlow( - ParameterNode p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config + ParameterNodeExt p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config ) { revFlow(p, true, apSome(ap0), ap, config) and c = getNodeEnclosingCallable(p) } - predicate parameterMayFlowThrough(ParameterNode p, DataFlowCallable c, Ap ap, Configuration config) { + predicate parameterMayFlowThrough( + ParameterNodeExt p, DataFlowCallable c, Ap ap, Configuration config + ) { exists(ReturnNodeExt ret, Ap ap0, ReturnKindExt kind, int pos | parameterFlow(p, ap, ap0, c, config) and c = getNodeEnclosingCallable(ret) and @@ -2606,7 +2613,7 @@ private predicate nodeMayUseSummary(Node n, AccessPathApprox apa, Configuration private newtype TSummaryCtx = TSummaryCtxNone() or - TSummaryCtxSome(ParameterNode p, AccessPath ap) { + TSummaryCtxSome(ParameterNodeExt p, AccessPath ap) { Stage4::parameterMayFlowThrough(p, _, ap.getApprox(), _) } @@ -2627,7 +2634,7 @@ private class SummaryCtxNone extends SummaryCtx, TSummaryCtxNone { /** A summary context from which a flow summary can be generated. */ private class SummaryCtxSome extends SummaryCtx, TSummaryCtxSome { - private ParameterNode p; + private ParameterNodeExt p; private AccessPath ap; SummaryCtxSome() { this = TSummaryCtxSome(p, ap) } @@ -2758,7 +2765,7 @@ private newtype TPathNode = config.isSource(node) and cc instanceof CallContextAny and sc instanceof SummaryCtxNone and - ap = TAccessPathNil(getNodeType(node)) + ap = TAccessPathNil(getNodeDataFlowType(node)) or // ... or a step from an existing PathNode to another node. exists(PathNodeMid mid | @@ -2979,7 +2986,7 @@ class PathNode extends TPathNode { Configuration getConfiguration() { none() } private predicate isHidden() { - nodeIsHidden(this.getNode()) and + hiddenNode(this.getNode()) and not this.isSource() and not this instanceof PathNodeSink } @@ -3148,7 +3155,7 @@ private predicate pathStep(PathNodeMid mid, Node node, CallContext cc, SummaryCt cc instanceof CallContextAny and sc instanceof SummaryCtxNone and mid.getAp() instanceof AccessPathNil and - ap = TAccessPathNil(getNodeType(node)) + ap = TAccessPathNil(getNodeDataFlowType(node)) or exists(TypedContent tc | pathStoreStep(mid, node, ap.pop(tc), tc, cc)) and sc = mid.getSummaryCtx() @@ -3235,7 +3242,7 @@ pragma[noinline] private predicate pathIntoArg( PathNodeMid mid, int i, CallContext cc, DataFlowCall call, AccessPath ap, AccessPathApprox apa ) { - exists(ArgumentNode arg | + exists(ArgumentNodeExt arg | arg = mid.getNode() and cc = mid.getCallContext() and arg.argumentOf(call, i) and @@ -3248,7 +3255,7 @@ pragma[noinline] private predicate parameterCand( DataFlowCallable callable, int i, AccessPathApprox apa, Configuration config ) { - exists(ParameterNode p | + exists(ParameterNodeExt p | Stage4::revFlow(p, _, _, apa, config) and p.isParameterOf(callable, i) ) @@ -3272,7 +3279,7 @@ private predicate pathIntoCallable0( * respectively. */ private predicate pathIntoCallable( - PathNodeMid mid, ParameterNode p, CallContext outercc, CallContextCall innercc, SummaryCtx sc, + PathNodeMid mid, ParameterNodeExt p, CallContext outercc, CallContextCall innercc, SummaryCtx sc, DataFlowCall call ) { exists(int i, DataFlowCallable callable, AccessPath ap | @@ -3568,7 +3575,7 @@ private module FlowExploration { private newtype TSummaryCtx1 = TSummaryCtx1None() or - TSummaryCtx1Param(ParameterNode p) + TSummaryCtx1Param(ParameterNodeExt p) private newtype TSummaryCtx2 = TSummaryCtx2None() or @@ -3591,7 +3598,7 @@ private module FlowExploration { cc instanceof CallContextAny and sc1 = TSummaryCtx1None() and sc2 = TSummaryCtx2None() and - ap = TPartialNil(getNodeType(node)) and + ap = TPartialNil(getNodeDataFlowType(node)) and not fullBarrier(node, config) and exists(config.explorationLimit()) or @@ -3611,7 +3618,7 @@ private module FlowExploration { or exists(PartialPathNodeRev mid | revPartialPathStep(mid, node, sc1, sc2, ap, config) and - not clearsContent(node, ap.getHead()) and + not clearsContentCached(node, ap.getHead()) and not fullBarrier(node, config) and distSink(getNodeEnclosingCallable(node), config) <= config.explorationLimit() ) @@ -3625,9 +3632,9 @@ private module FlowExploration { exists(PartialPathNodeFwd mid | partialPathStep(mid, node, cc, sc1, sc2, ap, config) and not fullBarrier(node, config) and - not clearsContent(node, ap.getHead().getContent()) and + not clearsContentCached(node, ap.getHead().getContent()) and if node instanceof CastingNode - then compatibleTypes(getNodeType(node), ap.getType()) + then compatibleTypes(getNodeDataFlowType(node), ap.getType()) else any() ) } @@ -3783,7 +3790,7 @@ private module FlowExploration { PartialPathNodeFwd mid, Node node, CallContext cc, TSummaryCtx1 sc1, TSummaryCtx2 sc2, PartialAccessPath ap, Configuration config ) { - not isUnreachableInCall(node, cc.(CallContextSpecificCall).getCall()) and + not isUnreachableInCallCached(node, cc.(CallContextSpecificCall).getCall()) and ( localFlowStep(mid.getNode(), node, config) and cc = mid.getCallContext() and @@ -3797,7 +3804,7 @@ private module FlowExploration { sc1 = mid.getSummaryCtx1() and sc2 = mid.getSummaryCtx2() and mid.getAp() instanceof PartialAccessPathNil and - ap = TPartialNil(getNodeType(node)) and + ap = TPartialNil(getNodeDataFlowType(node)) and config = mid.getConfiguration() ) or @@ -3813,7 +3820,7 @@ private module FlowExploration { sc1 = TSummaryCtx1None() and sc2 = TSummaryCtx2None() and mid.getAp() instanceof PartialAccessPathNil and - ap = TPartialNil(getNodeType(node)) and + ap = TPartialNil(getNodeDataFlowType(node)) and config = mid.getConfiguration() or partialPathStoreStep(mid, _, _, node, ap) and @@ -3827,7 +3834,7 @@ private module FlowExploration { sc1 = mid.getSummaryCtx1() and sc2 = mid.getSummaryCtx2() and apConsFwd(ap, tc, ap0, config) and - compatibleTypes(ap.getType(), getNodeType(node)) + compatibleTypes(ap.getType(), getNodeDataFlowType(node)) ) or partialPathIntoCallable(mid, node, _, cc, sc1, sc2, _, ap, config) @@ -3924,7 +3931,7 @@ private module FlowExploration { PartialPathNodeFwd mid, int i, CallContext cc, DataFlowCall call, PartialAccessPath ap, Configuration config ) { - exists(ArgumentNode arg | + exists(ArgumentNodeExt arg | arg = mid.getNode() and cc = mid.getCallContext() and arg.argumentOf(call, i) and @@ -3943,7 +3950,7 @@ private module FlowExploration { } private predicate partialPathIntoCallable( - PartialPathNodeFwd mid, ParameterNode p, CallContext outercc, CallContextCall innercc, + PartialPathNodeFwd mid, ParameterNodeExt p, CallContext outercc, CallContextCall innercc, TSummaryCtx1 sc1, TSummaryCtx2 sc2, DataFlowCall call, PartialAccessPath ap, Configuration config ) { @@ -3980,7 +3987,7 @@ private module FlowExploration { DataFlowCall call, PartialPathNodeFwd mid, ReturnKindExt kind, CallContext cc, PartialAccessPath ap, Configuration config ) { - exists(ParameterNode p, CallContext innercc, TSummaryCtx1 sc1, TSummaryCtx2 sc2 | + exists(ParameterNodeExt p, CallContext innercc, TSummaryCtx1 sc1, TSummaryCtx2 sc2 | partialPathIntoCallable(mid, p, cc, innercc, sc1, sc2, call, _, config) and paramFlowsThroughInPartialPath(kind, innercc, sc1, sc2, ap, config) ) @@ -4037,7 +4044,7 @@ private module FlowExploration { apConsRev(ap, c, ap0, config) ) or - exists(ParameterNode p | + exists(ParameterNodeExt p | mid.getNode() = p and viableParamArg(_, p, node) and sc1 = mid.getSummaryCtx1() and @@ -4115,7 +4122,7 @@ private module FlowExploration { int pos, TRevSummaryCtx1Some sc1, TRevSummaryCtx2Some sc2, RevPartialAccessPath ap, Configuration config ) { - exists(PartialPathNodeRev mid, ParameterNode p | + exists(PartialPathNodeRev mid, ParameterNodeExt p | mid.getNode() = p and p.isParameterOf(_, pos) and sc1 = mid.getSummaryCtx1() and @@ -4138,7 +4145,7 @@ private module FlowExploration { pragma[nomagic] private predicate revPartialPathThroughCallable( - PartialPathNodeRev mid, ArgumentNode node, RevPartialAccessPath ap, Configuration config + PartialPathNodeRev mid, ArgumentNodeExt node, RevPartialAccessPath ap, Configuration config ) { exists(DataFlowCall call, int pos | revPartialPathThroughCallable0(call, mid, pos, ap, config) and 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 9498e51e7e6..cf83530bcbd 100644 --- a/java/ql/src/semmle/code/java/dataflow/internal/DataFlowImpl5.qll +++ b/java/ql/src/semmle/code/java/dataflow/internal/DataFlowImpl5.qll @@ -211,10 +211,7 @@ private predicate fullBarrier(Node node, Configuration config) { * Holds if data can flow in one local step from `node1` to `node2`. */ private predicate localFlowStep(Node node1, Node node2, Configuration config) { - ( - simpleLocalFlowStep(node1, node2) or - reverseStepThroughInputOutputAlias(node1, node2) - ) and + simpleLocalFlowStepExt(node1, node2) and not outBarrier(node1, config) and not inBarrier(node2, config) and not fullBarrier(node1, config) and @@ -237,7 +234,7 @@ private predicate additionalLocalFlowStep(Node node1, Node node2, Configuration * Holds if data can flow from `node1` to `node2` in a way that discards call contexts. */ private predicate jumpStep(Node node1, Node node2, Configuration config) { - jumpStep(node1, node2) and + jumpStepCached(node1, node2) and not outBarrier(node1, config) and not inBarrier(node2, config) and not fullBarrier(node1, config) and @@ -388,7 +385,7 @@ private module Stage1 { */ pragma[nomagic] private predicate fwdFlowIsEntered(DataFlowCall call, Cc cc, Configuration config) { - exists(ArgumentNode arg | + exists(ArgumentNodeExt arg | fwdFlow(arg, cc, config) and viableParamArg(call, _, arg) ) @@ -515,7 +512,7 @@ private module Stage1 { pragma[nomagic] predicate viableParamArgNodeCandFwd1( - DataFlowCall call, ParameterNode p, ArgumentNode arg, Configuration config + DataFlowCall call, ParameterNodeExt p, ArgumentNodeExt arg, Configuration config ) { viableParamArg(call, p, arg) and fwdFlow(arg, config) @@ -523,16 +520,16 @@ private module Stage1 { pragma[nomagic] private predicate revFlowIn( - DataFlowCall call, ArgumentNode arg, boolean toReturn, Configuration config + DataFlowCall call, ArgumentNodeExt arg, boolean toReturn, Configuration config ) { - exists(ParameterNode p | + exists(ParameterNodeExt p | revFlow(p, toReturn, config) and viableParamArgNodeCandFwd1(call, p, arg, config) ) } pragma[nomagic] - private predicate revFlowInToReturn(DataFlowCall call, ArgumentNode arg, Configuration config) { + private predicate revFlowInToReturn(DataFlowCall call, ArgumentNodeExt arg, Configuration config) { revFlowIn(call, arg, true, config) } @@ -597,7 +594,9 @@ private module Stage1 { * Holds if flow may enter through `p` and reach a return node making `p` a * candidate for the origin of a summary. */ - predicate parameterMayFlowThrough(ParameterNode p, DataFlowCallable c, Ap ap, Configuration config) { + predicate parameterMayFlowThrough( + ParameterNodeExt p, DataFlowCallable c, Ap ap, Configuration config + ) { exists(ReturnKindExt kind | throughFlowNodeCand(p, config) and returnFlowCallableNodeCand(c, kind, config) and @@ -663,7 +662,7 @@ private predicate flowOutOfCallNodeCand1( pragma[nomagic] private predicate viableParamArgNodeCand1( - DataFlowCall call, ParameterNode p, ArgumentNode arg, Configuration config + DataFlowCall call, ParameterNodeExt p, ArgumentNodeExt arg, Configuration config ) { Stage1::viableParamArgNodeCandFwd1(call, p, arg, config) and Stage1::revFlow(arg, config) @@ -675,7 +674,7 @@ private predicate viableParamArgNodeCand1( */ pragma[nomagic] private predicate flowIntoCallNodeCand1( - DataFlowCall call, ArgumentNode arg, ParameterNode p, Configuration config + DataFlowCall call, ArgumentNodeExt arg, ParameterNodeExt p, Configuration config ) { viableParamArgNodeCand1(call, p, arg, config) and Stage1::revFlow(p, config) and @@ -735,7 +734,7 @@ private predicate flowOutOfCallNodeCand1( */ pragma[nomagic] private predicate flowIntoCallNodeCand1( - DataFlowCall call, ArgumentNode arg, ParameterNode p, boolean allowsFieldFlow, + DataFlowCall call, ArgumentNodeExt arg, ParameterNodeExt p, boolean allowsFieldFlow, Configuration config ) { flowIntoCallNodeCand1(call, arg, p, config) and @@ -944,10 +943,10 @@ private module Stage2 { pragma[nomagic] private predicate fwdFlowIn( - DataFlowCall call, ParameterNode p, Cc outercc, Cc innercc, ApOption argAp, Ap ap, + DataFlowCall call, ParameterNodeExt p, Cc outercc, Cc innercc, ApOption argAp, Ap ap, Configuration config ) { - exists(ArgumentNode arg, boolean allowsFieldFlow | + exists(ArgumentNodeExt arg, boolean allowsFieldFlow | fwdFlow(arg, outercc, argAp, ap, config) and flowIntoCall(call, arg, p, allowsFieldFlow, config) and innercc = getCallContextCall(call, getNodeEnclosingCallable(p), outercc) @@ -992,7 +991,7 @@ private module Stage2 { private predicate fwdFlowIsEntered( DataFlowCall call, Cc cc, ApOption argAp, Ap ap, Configuration config ) { - exists(ParameterNode p | + exists(ParameterNodeExt p | fwdFlowIn(call, p, cc, _, argAp, ap, config) and PrevStage::parameterMayFlowThrough(p, _, getApprox(ap), config) ) @@ -1133,10 +1132,10 @@ private module Stage2 { pragma[nomagic] private predicate revFlowIn( - DataFlowCall call, ArgumentNode arg, boolean toReturn, ApOption returnAp, Ap ap, + DataFlowCall call, ArgumentNodeExt arg, boolean toReturn, ApOption returnAp, Ap ap, Configuration config ) { - exists(ParameterNode p, boolean allowsFieldFlow | + exists(ParameterNodeExt p, boolean allowsFieldFlow | revFlow(p, toReturn, returnAp, ap, config) and flowIntoCall(call, arg, p, allowsFieldFlow, config) | @@ -1146,7 +1145,7 @@ private module Stage2 { pragma[nomagic] private predicate revFlowInToReturn( - DataFlowCall call, ArgumentNode arg, Ap returnAp, Ap ap, Configuration config + DataFlowCall call, ArgumentNodeExt arg, Ap returnAp, Ap ap, Configuration config ) { revFlowIn(call, arg, true, apSome(returnAp), ap, config) } @@ -1199,13 +1198,15 @@ private module Stage2 { pragma[noinline] private predicate parameterFlow( - ParameterNode p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config + ParameterNodeExt p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config ) { revFlow(p, true, apSome(ap0), ap, config) and c = getNodeEnclosingCallable(p) } - predicate parameterMayFlowThrough(ParameterNode p, DataFlowCallable c, Ap ap, Configuration config) { + predicate parameterMayFlowThrough( + ParameterNodeExt p, DataFlowCallable c, Ap ap, Configuration config + ) { exists(ReturnNodeExt ret, Ap ap0, ReturnKindExt kind, int pos | parameterFlow(p, ap, ap0, c, config) and c = getNodeEnclosingCallable(ret) and @@ -1245,7 +1246,7 @@ private predicate flowOutOfCallNodeCand2( pragma[nomagic] private predicate flowIntoCallNodeCand2( - DataFlowCall call, ArgumentNode node1, ParameterNode node2, boolean allowsFieldFlow, + DataFlowCall call, ArgumentNodeExt node1, ParameterNodeExt node2, boolean allowsFieldFlow, Configuration config ) { flowIntoCallNodeCand1(call, node1, node2, allowsFieldFlow, config) and @@ -1260,8 +1261,8 @@ private module LocalFlowBigStep { */ private class FlowCheckNode extends Node { FlowCheckNode() { - this instanceof CastNode or - clearsContent(this, _) + castNode(this) or + clearsContentCached(this, _) } } @@ -1275,7 +1276,7 @@ private module LocalFlowBigStep { config.isSource(node) or jumpStep(_, node, config) or additionalJumpStep(_, node, config) or - node instanceof ParameterNode or + node instanceof ParameterNodeExt or node instanceof OutNodeExt or store(_, _, node, _) or read(_, _, node) or @@ -1321,21 +1322,21 @@ private module LocalFlowBigStep { Node node1, Node node2, boolean preservesValue, DataFlowType t, Configuration config, LocalCallContext cc ) { - not isUnreachableInCall(node2, cc.(LocalCallContextSpecificCall).getCall()) and + not isUnreachableInCallCached(node2, cc.(LocalCallContextSpecificCall).getCall()) and ( localFlowEntry(node1, pragma[only_bind_into](config)) and ( localFlowStepNodeCand1(node1, node2, config) and preservesValue = true and - t = getNodeType(node1) + t = getNodeDataFlowType(node1) or additionalLocalFlowStepNodeCand2(node1, node2, config) and preservesValue = false and - t = getNodeType(node2) + t = getNodeDataFlowType(node2) ) and node1 != node2 and cc.relevantFor(getNodeEnclosingCallable(node1)) and - not isUnreachableInCall(node1, cc.(LocalCallContextSpecificCall).getCall()) and + not isUnreachableInCallCached(node1, cc.(LocalCallContextSpecificCall).getCall()) and Stage2::revFlow(node2, pragma[only_bind_into](config)) or exists(Node mid | @@ -1350,7 +1351,7 @@ private module LocalFlowBigStep { additionalLocalFlowStepNodeCand2(mid, node2, config) and not mid instanceof FlowCheckNode and preservesValue = false and - t = getNodeType(node2) and + t = getNodeDataFlowType(node2) and Stage2::revFlow(node2, pragma[only_bind_into](config)) ) ) @@ -1384,7 +1385,7 @@ private module Stage3 { private ApApprox getApprox(Ap ap) { result = ap.toBoolNonEmpty() } private ApNil getApNil(Node node) { - PrevStage::revFlow(node, _) and result = TFrontNil(getNodeType(node)) + PrevStage::revFlow(node, _) and result = TFrontNil(getNodeDataFlowType(node)) } bindingset[tc, tail] @@ -1443,7 +1444,9 @@ private module Stage3 { bindingset[node, ap] private predicate filter(Node node, Ap ap) { not ap.isClearedAt(node) and - if node instanceof CastingNode then compatibleTypes(getNodeType(node), ap.getType()) else any() + if node instanceof CastingNode + then compatibleTypes(getNodeDataFlowType(node), ap.getType()) + else any() } bindingset[ap, contentType] @@ -1583,10 +1586,10 @@ private module Stage3 { pragma[nomagic] private predicate fwdFlowIn( - DataFlowCall call, ParameterNode p, Cc outercc, Cc innercc, ApOption argAp, Ap ap, + DataFlowCall call, ParameterNodeExt p, Cc outercc, Cc innercc, ApOption argAp, Ap ap, Configuration config ) { - exists(ArgumentNode arg, boolean allowsFieldFlow | + exists(ArgumentNodeExt arg, boolean allowsFieldFlow | fwdFlow(arg, outercc, argAp, ap, config) and flowIntoCall(call, arg, p, allowsFieldFlow, config) and innercc = getCallContextCall(call, getNodeEnclosingCallable(p), outercc) @@ -1631,7 +1634,7 @@ private module Stage3 { private predicate fwdFlowIsEntered( DataFlowCall call, Cc cc, ApOption argAp, Ap ap, Configuration config ) { - exists(ParameterNode p | + exists(ParameterNodeExt p | fwdFlowIn(call, p, cc, _, argAp, ap, config) and PrevStage::parameterMayFlowThrough(p, _, unbindApa(getApprox(ap)), config) ) @@ -1772,10 +1775,10 @@ private module Stage3 { pragma[nomagic] private predicate revFlowIn( - DataFlowCall call, ArgumentNode arg, boolean toReturn, ApOption returnAp, Ap ap, + DataFlowCall call, ArgumentNodeExt arg, boolean toReturn, ApOption returnAp, Ap ap, Configuration config ) { - exists(ParameterNode p, boolean allowsFieldFlow | + exists(ParameterNodeExt p, boolean allowsFieldFlow | revFlow(p, toReturn, returnAp, ap, config) and flowIntoCall(call, arg, p, allowsFieldFlow, config) | @@ -1785,7 +1788,7 @@ private module Stage3 { pragma[nomagic] private predicate revFlowInToReturn( - DataFlowCall call, ArgumentNode arg, Ap returnAp, Ap ap, Configuration config + DataFlowCall call, ArgumentNodeExt arg, Ap returnAp, Ap ap, Configuration config ) { revFlowIn(call, arg, true, apSome(returnAp), ap, config) } @@ -1838,13 +1841,15 @@ private module Stage3 { pragma[noinline] private predicate parameterFlow( - ParameterNode p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config + ParameterNodeExt p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config ) { revFlow(p, true, apSome(ap0), ap, config) and c = getNodeEnclosingCallable(p) } - predicate parameterMayFlowThrough(ParameterNode p, DataFlowCallable c, Ap ap, Configuration config) { + predicate parameterMayFlowThrough( + ParameterNodeExt p, DataFlowCallable c, Ap ap, Configuration config + ) { exists(ReturnNodeExt ret, Ap ap0, ReturnKindExt kind, int pos | parameterFlow(p, ap, ap0, c, config) and c = getNodeEnclosingCallable(ret) and @@ -2088,7 +2093,7 @@ private module Stage4 { private ApApprox getApprox(Ap ap) { result = ap.getFront() } private ApNil getApNil(Node node) { - PrevStage::revFlow(node, _) and result = TNil(getNodeType(node)) + PrevStage::revFlow(node, _) and result = TNil(getNodeDataFlowType(node)) } bindingset[tc, tail] @@ -2155,7 +2160,7 @@ private module Stage4 { pragma[nomagic] private predicate flowIntoCall( - DataFlowCall call, ArgumentNode node1, ParameterNode node2, boolean allowsFieldFlow, + DataFlowCall call, ArgumentNodeExt node1, ParameterNodeExt node2, boolean allowsFieldFlow, Configuration config ) { flowIntoCallNodeCand2(call, node1, node2, allowsFieldFlow, config) and @@ -2300,10 +2305,10 @@ private module Stage4 { pragma[nomagic] private predicate fwdFlowIn( - DataFlowCall call, ParameterNode p, Cc outercc, Cc innercc, ApOption argAp, Ap ap, + DataFlowCall call, ParameterNodeExt p, Cc outercc, Cc innercc, ApOption argAp, Ap ap, Configuration config ) { - exists(ArgumentNode arg, boolean allowsFieldFlow | + exists(ArgumentNodeExt arg, boolean allowsFieldFlow | fwdFlow(arg, outercc, argAp, ap, config) and flowIntoCall(call, arg, p, allowsFieldFlow, config) and innercc = getCallContextCall(call, getNodeEnclosingCallable(p), outercc) @@ -2348,7 +2353,7 @@ private module Stage4 { private predicate fwdFlowIsEntered( DataFlowCall call, Cc cc, ApOption argAp, Ap ap, Configuration config ) { - exists(ParameterNode p | + exists(ParameterNodeExt p | fwdFlowIn(call, p, cc, _, argAp, ap, config) and PrevStage::parameterMayFlowThrough(p, _, unbindApa(getApprox(ap)), config) ) @@ -2489,10 +2494,10 @@ private module Stage4 { pragma[nomagic] private predicate revFlowIn( - DataFlowCall call, ArgumentNode arg, boolean toReturn, ApOption returnAp, Ap ap, + DataFlowCall call, ArgumentNodeExt arg, boolean toReturn, ApOption returnAp, Ap ap, Configuration config ) { - exists(ParameterNode p, boolean allowsFieldFlow | + exists(ParameterNodeExt p, boolean allowsFieldFlow | revFlow(p, toReturn, returnAp, ap, config) and flowIntoCall(call, arg, p, allowsFieldFlow, config) | @@ -2502,7 +2507,7 @@ private module Stage4 { pragma[nomagic] private predicate revFlowInToReturn( - DataFlowCall call, ArgumentNode arg, Ap returnAp, Ap ap, Configuration config + DataFlowCall call, ArgumentNodeExt arg, Ap returnAp, Ap ap, Configuration config ) { revFlowIn(call, arg, true, apSome(returnAp), ap, config) } @@ -2555,13 +2560,15 @@ private module Stage4 { pragma[noinline] private predicate parameterFlow( - ParameterNode p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config + ParameterNodeExt p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config ) { revFlow(p, true, apSome(ap0), ap, config) and c = getNodeEnclosingCallable(p) } - predicate parameterMayFlowThrough(ParameterNode p, DataFlowCallable c, Ap ap, Configuration config) { + predicate parameterMayFlowThrough( + ParameterNodeExt p, DataFlowCallable c, Ap ap, Configuration config + ) { exists(ReturnNodeExt ret, Ap ap0, ReturnKindExt kind, int pos | parameterFlow(p, ap, ap0, c, config) and c = getNodeEnclosingCallable(ret) and @@ -2606,7 +2613,7 @@ private predicate nodeMayUseSummary(Node n, AccessPathApprox apa, Configuration private newtype TSummaryCtx = TSummaryCtxNone() or - TSummaryCtxSome(ParameterNode p, AccessPath ap) { + TSummaryCtxSome(ParameterNodeExt p, AccessPath ap) { Stage4::parameterMayFlowThrough(p, _, ap.getApprox(), _) } @@ -2627,7 +2634,7 @@ private class SummaryCtxNone extends SummaryCtx, TSummaryCtxNone { /** A summary context from which a flow summary can be generated. */ private class SummaryCtxSome extends SummaryCtx, TSummaryCtxSome { - private ParameterNode p; + private ParameterNodeExt p; private AccessPath ap; SummaryCtxSome() { this = TSummaryCtxSome(p, ap) } @@ -2758,7 +2765,7 @@ private newtype TPathNode = config.isSource(node) and cc instanceof CallContextAny and sc instanceof SummaryCtxNone and - ap = TAccessPathNil(getNodeType(node)) + ap = TAccessPathNil(getNodeDataFlowType(node)) or // ... or a step from an existing PathNode to another node. exists(PathNodeMid mid | @@ -2979,7 +2986,7 @@ class PathNode extends TPathNode { Configuration getConfiguration() { none() } private predicate isHidden() { - nodeIsHidden(this.getNode()) and + hiddenNode(this.getNode()) and not this.isSource() and not this instanceof PathNodeSink } @@ -3148,7 +3155,7 @@ private predicate pathStep(PathNodeMid mid, Node node, CallContext cc, SummaryCt cc instanceof CallContextAny and sc instanceof SummaryCtxNone and mid.getAp() instanceof AccessPathNil and - ap = TAccessPathNil(getNodeType(node)) + ap = TAccessPathNil(getNodeDataFlowType(node)) or exists(TypedContent tc | pathStoreStep(mid, node, ap.pop(tc), tc, cc)) and sc = mid.getSummaryCtx() @@ -3235,7 +3242,7 @@ pragma[noinline] private predicate pathIntoArg( PathNodeMid mid, int i, CallContext cc, DataFlowCall call, AccessPath ap, AccessPathApprox apa ) { - exists(ArgumentNode arg | + exists(ArgumentNodeExt arg | arg = mid.getNode() and cc = mid.getCallContext() and arg.argumentOf(call, i) and @@ -3248,7 +3255,7 @@ pragma[noinline] private predicate parameterCand( DataFlowCallable callable, int i, AccessPathApprox apa, Configuration config ) { - exists(ParameterNode p | + exists(ParameterNodeExt p | Stage4::revFlow(p, _, _, apa, config) and p.isParameterOf(callable, i) ) @@ -3272,7 +3279,7 @@ private predicate pathIntoCallable0( * respectively. */ private predicate pathIntoCallable( - PathNodeMid mid, ParameterNode p, CallContext outercc, CallContextCall innercc, SummaryCtx sc, + PathNodeMid mid, ParameterNodeExt p, CallContext outercc, CallContextCall innercc, SummaryCtx sc, DataFlowCall call ) { exists(int i, DataFlowCallable callable, AccessPath ap | @@ -3568,7 +3575,7 @@ private module FlowExploration { private newtype TSummaryCtx1 = TSummaryCtx1None() or - TSummaryCtx1Param(ParameterNode p) + TSummaryCtx1Param(ParameterNodeExt p) private newtype TSummaryCtx2 = TSummaryCtx2None() or @@ -3591,7 +3598,7 @@ private module FlowExploration { cc instanceof CallContextAny and sc1 = TSummaryCtx1None() and sc2 = TSummaryCtx2None() and - ap = TPartialNil(getNodeType(node)) and + ap = TPartialNil(getNodeDataFlowType(node)) and not fullBarrier(node, config) and exists(config.explorationLimit()) or @@ -3611,7 +3618,7 @@ private module FlowExploration { or exists(PartialPathNodeRev mid | revPartialPathStep(mid, node, sc1, sc2, ap, config) and - not clearsContent(node, ap.getHead()) and + not clearsContentCached(node, ap.getHead()) and not fullBarrier(node, config) and distSink(getNodeEnclosingCallable(node), config) <= config.explorationLimit() ) @@ -3625,9 +3632,9 @@ private module FlowExploration { exists(PartialPathNodeFwd mid | partialPathStep(mid, node, cc, sc1, sc2, ap, config) and not fullBarrier(node, config) and - not clearsContent(node, ap.getHead().getContent()) and + not clearsContentCached(node, ap.getHead().getContent()) and if node instanceof CastingNode - then compatibleTypes(getNodeType(node), ap.getType()) + then compatibleTypes(getNodeDataFlowType(node), ap.getType()) else any() ) } @@ -3783,7 +3790,7 @@ private module FlowExploration { PartialPathNodeFwd mid, Node node, CallContext cc, TSummaryCtx1 sc1, TSummaryCtx2 sc2, PartialAccessPath ap, Configuration config ) { - not isUnreachableInCall(node, cc.(CallContextSpecificCall).getCall()) and + not isUnreachableInCallCached(node, cc.(CallContextSpecificCall).getCall()) and ( localFlowStep(mid.getNode(), node, config) and cc = mid.getCallContext() and @@ -3797,7 +3804,7 @@ private module FlowExploration { sc1 = mid.getSummaryCtx1() and sc2 = mid.getSummaryCtx2() and mid.getAp() instanceof PartialAccessPathNil and - ap = TPartialNil(getNodeType(node)) and + ap = TPartialNil(getNodeDataFlowType(node)) and config = mid.getConfiguration() ) or @@ -3813,7 +3820,7 @@ private module FlowExploration { sc1 = TSummaryCtx1None() and sc2 = TSummaryCtx2None() and mid.getAp() instanceof PartialAccessPathNil and - ap = TPartialNil(getNodeType(node)) and + ap = TPartialNil(getNodeDataFlowType(node)) and config = mid.getConfiguration() or partialPathStoreStep(mid, _, _, node, ap) and @@ -3827,7 +3834,7 @@ private module FlowExploration { sc1 = mid.getSummaryCtx1() and sc2 = mid.getSummaryCtx2() and apConsFwd(ap, tc, ap0, config) and - compatibleTypes(ap.getType(), getNodeType(node)) + compatibleTypes(ap.getType(), getNodeDataFlowType(node)) ) or partialPathIntoCallable(mid, node, _, cc, sc1, sc2, _, ap, config) @@ -3924,7 +3931,7 @@ private module FlowExploration { PartialPathNodeFwd mid, int i, CallContext cc, DataFlowCall call, PartialAccessPath ap, Configuration config ) { - exists(ArgumentNode arg | + exists(ArgumentNodeExt arg | arg = mid.getNode() and cc = mid.getCallContext() and arg.argumentOf(call, i) and @@ -3943,7 +3950,7 @@ private module FlowExploration { } private predicate partialPathIntoCallable( - PartialPathNodeFwd mid, ParameterNode p, CallContext outercc, CallContextCall innercc, + PartialPathNodeFwd mid, ParameterNodeExt p, CallContext outercc, CallContextCall innercc, TSummaryCtx1 sc1, TSummaryCtx2 sc2, DataFlowCall call, PartialAccessPath ap, Configuration config ) { @@ -3980,7 +3987,7 @@ private module FlowExploration { DataFlowCall call, PartialPathNodeFwd mid, ReturnKindExt kind, CallContext cc, PartialAccessPath ap, Configuration config ) { - exists(ParameterNode p, CallContext innercc, TSummaryCtx1 sc1, TSummaryCtx2 sc2 | + exists(ParameterNodeExt p, CallContext innercc, TSummaryCtx1 sc1, TSummaryCtx2 sc2 | partialPathIntoCallable(mid, p, cc, innercc, sc1, sc2, call, _, config) and paramFlowsThroughInPartialPath(kind, innercc, sc1, sc2, ap, config) ) @@ -4037,7 +4044,7 @@ private module FlowExploration { apConsRev(ap, c, ap0, config) ) or - exists(ParameterNode p | + exists(ParameterNodeExt p | mid.getNode() = p and viableParamArg(_, p, node) and sc1 = mid.getSummaryCtx1() and @@ -4115,7 +4122,7 @@ private module FlowExploration { int pos, TRevSummaryCtx1Some sc1, TRevSummaryCtx2Some sc2, RevPartialAccessPath ap, Configuration config ) { - exists(PartialPathNodeRev mid, ParameterNode p | + exists(PartialPathNodeRev mid, ParameterNodeExt p | mid.getNode() = p and p.isParameterOf(_, pos) and sc1 = mid.getSummaryCtx1() and @@ -4138,7 +4145,7 @@ private module FlowExploration { pragma[nomagic] private predicate revPartialPathThroughCallable( - PartialPathNodeRev mid, ArgumentNode node, RevPartialAccessPath ap, Configuration config + PartialPathNodeRev mid, ArgumentNodeExt node, RevPartialAccessPath ap, Configuration config ) { exists(DataFlowCall call, int pos | revPartialPathThroughCallable0(call, mid, pos, ap, config) and diff --git a/java/ql/src/semmle/code/java/dataflow/internal/DataFlowImpl6.qll b/java/ql/src/semmle/code/java/dataflow/internal/DataFlowImpl6.qll index 9498e51e7e6..cf83530bcbd 100644 --- a/java/ql/src/semmle/code/java/dataflow/internal/DataFlowImpl6.qll +++ b/java/ql/src/semmle/code/java/dataflow/internal/DataFlowImpl6.qll @@ -211,10 +211,7 @@ private predicate fullBarrier(Node node, Configuration config) { * Holds if data can flow in one local step from `node1` to `node2`. */ private predicate localFlowStep(Node node1, Node node2, Configuration config) { - ( - simpleLocalFlowStep(node1, node2) or - reverseStepThroughInputOutputAlias(node1, node2) - ) and + simpleLocalFlowStepExt(node1, node2) and not outBarrier(node1, config) and not inBarrier(node2, config) and not fullBarrier(node1, config) and @@ -237,7 +234,7 @@ private predicate additionalLocalFlowStep(Node node1, Node node2, Configuration * Holds if data can flow from `node1` to `node2` in a way that discards call contexts. */ private predicate jumpStep(Node node1, Node node2, Configuration config) { - jumpStep(node1, node2) and + jumpStepCached(node1, node2) and not outBarrier(node1, config) and not inBarrier(node2, config) and not fullBarrier(node1, config) and @@ -388,7 +385,7 @@ private module Stage1 { */ pragma[nomagic] private predicate fwdFlowIsEntered(DataFlowCall call, Cc cc, Configuration config) { - exists(ArgumentNode arg | + exists(ArgumentNodeExt arg | fwdFlow(arg, cc, config) and viableParamArg(call, _, arg) ) @@ -515,7 +512,7 @@ private module Stage1 { pragma[nomagic] predicate viableParamArgNodeCandFwd1( - DataFlowCall call, ParameterNode p, ArgumentNode arg, Configuration config + DataFlowCall call, ParameterNodeExt p, ArgumentNodeExt arg, Configuration config ) { viableParamArg(call, p, arg) and fwdFlow(arg, config) @@ -523,16 +520,16 @@ private module Stage1 { pragma[nomagic] private predicate revFlowIn( - DataFlowCall call, ArgumentNode arg, boolean toReturn, Configuration config + DataFlowCall call, ArgumentNodeExt arg, boolean toReturn, Configuration config ) { - exists(ParameterNode p | + exists(ParameterNodeExt p | revFlow(p, toReturn, config) and viableParamArgNodeCandFwd1(call, p, arg, config) ) } pragma[nomagic] - private predicate revFlowInToReturn(DataFlowCall call, ArgumentNode arg, Configuration config) { + private predicate revFlowInToReturn(DataFlowCall call, ArgumentNodeExt arg, Configuration config) { revFlowIn(call, arg, true, config) } @@ -597,7 +594,9 @@ private module Stage1 { * Holds if flow may enter through `p` and reach a return node making `p` a * candidate for the origin of a summary. */ - predicate parameterMayFlowThrough(ParameterNode p, DataFlowCallable c, Ap ap, Configuration config) { + predicate parameterMayFlowThrough( + ParameterNodeExt p, DataFlowCallable c, Ap ap, Configuration config + ) { exists(ReturnKindExt kind | throughFlowNodeCand(p, config) and returnFlowCallableNodeCand(c, kind, config) and @@ -663,7 +662,7 @@ private predicate flowOutOfCallNodeCand1( pragma[nomagic] private predicate viableParamArgNodeCand1( - DataFlowCall call, ParameterNode p, ArgumentNode arg, Configuration config + DataFlowCall call, ParameterNodeExt p, ArgumentNodeExt arg, Configuration config ) { Stage1::viableParamArgNodeCandFwd1(call, p, arg, config) and Stage1::revFlow(arg, config) @@ -675,7 +674,7 @@ private predicate viableParamArgNodeCand1( */ pragma[nomagic] private predicate flowIntoCallNodeCand1( - DataFlowCall call, ArgumentNode arg, ParameterNode p, Configuration config + DataFlowCall call, ArgumentNodeExt arg, ParameterNodeExt p, Configuration config ) { viableParamArgNodeCand1(call, p, arg, config) and Stage1::revFlow(p, config) and @@ -735,7 +734,7 @@ private predicate flowOutOfCallNodeCand1( */ pragma[nomagic] private predicate flowIntoCallNodeCand1( - DataFlowCall call, ArgumentNode arg, ParameterNode p, boolean allowsFieldFlow, + DataFlowCall call, ArgumentNodeExt arg, ParameterNodeExt p, boolean allowsFieldFlow, Configuration config ) { flowIntoCallNodeCand1(call, arg, p, config) and @@ -944,10 +943,10 @@ private module Stage2 { pragma[nomagic] private predicate fwdFlowIn( - DataFlowCall call, ParameterNode p, Cc outercc, Cc innercc, ApOption argAp, Ap ap, + DataFlowCall call, ParameterNodeExt p, Cc outercc, Cc innercc, ApOption argAp, Ap ap, Configuration config ) { - exists(ArgumentNode arg, boolean allowsFieldFlow | + exists(ArgumentNodeExt arg, boolean allowsFieldFlow | fwdFlow(arg, outercc, argAp, ap, config) and flowIntoCall(call, arg, p, allowsFieldFlow, config) and innercc = getCallContextCall(call, getNodeEnclosingCallable(p), outercc) @@ -992,7 +991,7 @@ private module Stage2 { private predicate fwdFlowIsEntered( DataFlowCall call, Cc cc, ApOption argAp, Ap ap, Configuration config ) { - exists(ParameterNode p | + exists(ParameterNodeExt p | fwdFlowIn(call, p, cc, _, argAp, ap, config) and PrevStage::parameterMayFlowThrough(p, _, getApprox(ap), config) ) @@ -1133,10 +1132,10 @@ private module Stage2 { pragma[nomagic] private predicate revFlowIn( - DataFlowCall call, ArgumentNode arg, boolean toReturn, ApOption returnAp, Ap ap, + DataFlowCall call, ArgumentNodeExt arg, boolean toReturn, ApOption returnAp, Ap ap, Configuration config ) { - exists(ParameterNode p, boolean allowsFieldFlow | + exists(ParameterNodeExt p, boolean allowsFieldFlow | revFlow(p, toReturn, returnAp, ap, config) and flowIntoCall(call, arg, p, allowsFieldFlow, config) | @@ -1146,7 +1145,7 @@ private module Stage2 { pragma[nomagic] private predicate revFlowInToReturn( - DataFlowCall call, ArgumentNode arg, Ap returnAp, Ap ap, Configuration config + DataFlowCall call, ArgumentNodeExt arg, Ap returnAp, Ap ap, Configuration config ) { revFlowIn(call, arg, true, apSome(returnAp), ap, config) } @@ -1199,13 +1198,15 @@ private module Stage2 { pragma[noinline] private predicate parameterFlow( - ParameterNode p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config + ParameterNodeExt p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config ) { revFlow(p, true, apSome(ap0), ap, config) and c = getNodeEnclosingCallable(p) } - predicate parameterMayFlowThrough(ParameterNode p, DataFlowCallable c, Ap ap, Configuration config) { + predicate parameterMayFlowThrough( + ParameterNodeExt p, DataFlowCallable c, Ap ap, Configuration config + ) { exists(ReturnNodeExt ret, Ap ap0, ReturnKindExt kind, int pos | parameterFlow(p, ap, ap0, c, config) and c = getNodeEnclosingCallable(ret) and @@ -1245,7 +1246,7 @@ private predicate flowOutOfCallNodeCand2( pragma[nomagic] private predicate flowIntoCallNodeCand2( - DataFlowCall call, ArgumentNode node1, ParameterNode node2, boolean allowsFieldFlow, + DataFlowCall call, ArgumentNodeExt node1, ParameterNodeExt node2, boolean allowsFieldFlow, Configuration config ) { flowIntoCallNodeCand1(call, node1, node2, allowsFieldFlow, config) and @@ -1260,8 +1261,8 @@ private module LocalFlowBigStep { */ private class FlowCheckNode extends Node { FlowCheckNode() { - this instanceof CastNode or - clearsContent(this, _) + castNode(this) or + clearsContentCached(this, _) } } @@ -1275,7 +1276,7 @@ private module LocalFlowBigStep { config.isSource(node) or jumpStep(_, node, config) or additionalJumpStep(_, node, config) or - node instanceof ParameterNode or + node instanceof ParameterNodeExt or node instanceof OutNodeExt or store(_, _, node, _) or read(_, _, node) or @@ -1321,21 +1322,21 @@ private module LocalFlowBigStep { Node node1, Node node2, boolean preservesValue, DataFlowType t, Configuration config, LocalCallContext cc ) { - not isUnreachableInCall(node2, cc.(LocalCallContextSpecificCall).getCall()) and + not isUnreachableInCallCached(node2, cc.(LocalCallContextSpecificCall).getCall()) and ( localFlowEntry(node1, pragma[only_bind_into](config)) and ( localFlowStepNodeCand1(node1, node2, config) and preservesValue = true and - t = getNodeType(node1) + t = getNodeDataFlowType(node1) or additionalLocalFlowStepNodeCand2(node1, node2, config) and preservesValue = false and - t = getNodeType(node2) + t = getNodeDataFlowType(node2) ) and node1 != node2 and cc.relevantFor(getNodeEnclosingCallable(node1)) and - not isUnreachableInCall(node1, cc.(LocalCallContextSpecificCall).getCall()) and + not isUnreachableInCallCached(node1, cc.(LocalCallContextSpecificCall).getCall()) and Stage2::revFlow(node2, pragma[only_bind_into](config)) or exists(Node mid | @@ -1350,7 +1351,7 @@ private module LocalFlowBigStep { additionalLocalFlowStepNodeCand2(mid, node2, config) and not mid instanceof FlowCheckNode and preservesValue = false and - t = getNodeType(node2) and + t = getNodeDataFlowType(node2) and Stage2::revFlow(node2, pragma[only_bind_into](config)) ) ) @@ -1384,7 +1385,7 @@ private module Stage3 { private ApApprox getApprox(Ap ap) { result = ap.toBoolNonEmpty() } private ApNil getApNil(Node node) { - PrevStage::revFlow(node, _) and result = TFrontNil(getNodeType(node)) + PrevStage::revFlow(node, _) and result = TFrontNil(getNodeDataFlowType(node)) } bindingset[tc, tail] @@ -1443,7 +1444,9 @@ private module Stage3 { bindingset[node, ap] private predicate filter(Node node, Ap ap) { not ap.isClearedAt(node) and - if node instanceof CastingNode then compatibleTypes(getNodeType(node), ap.getType()) else any() + if node instanceof CastingNode + then compatibleTypes(getNodeDataFlowType(node), ap.getType()) + else any() } bindingset[ap, contentType] @@ -1583,10 +1586,10 @@ private module Stage3 { pragma[nomagic] private predicate fwdFlowIn( - DataFlowCall call, ParameterNode p, Cc outercc, Cc innercc, ApOption argAp, Ap ap, + DataFlowCall call, ParameterNodeExt p, Cc outercc, Cc innercc, ApOption argAp, Ap ap, Configuration config ) { - exists(ArgumentNode arg, boolean allowsFieldFlow | + exists(ArgumentNodeExt arg, boolean allowsFieldFlow | fwdFlow(arg, outercc, argAp, ap, config) and flowIntoCall(call, arg, p, allowsFieldFlow, config) and innercc = getCallContextCall(call, getNodeEnclosingCallable(p), outercc) @@ -1631,7 +1634,7 @@ private module Stage3 { private predicate fwdFlowIsEntered( DataFlowCall call, Cc cc, ApOption argAp, Ap ap, Configuration config ) { - exists(ParameterNode p | + exists(ParameterNodeExt p | fwdFlowIn(call, p, cc, _, argAp, ap, config) and PrevStage::parameterMayFlowThrough(p, _, unbindApa(getApprox(ap)), config) ) @@ -1772,10 +1775,10 @@ private module Stage3 { pragma[nomagic] private predicate revFlowIn( - DataFlowCall call, ArgumentNode arg, boolean toReturn, ApOption returnAp, Ap ap, + DataFlowCall call, ArgumentNodeExt arg, boolean toReturn, ApOption returnAp, Ap ap, Configuration config ) { - exists(ParameterNode p, boolean allowsFieldFlow | + exists(ParameterNodeExt p, boolean allowsFieldFlow | revFlow(p, toReturn, returnAp, ap, config) and flowIntoCall(call, arg, p, allowsFieldFlow, config) | @@ -1785,7 +1788,7 @@ private module Stage3 { pragma[nomagic] private predicate revFlowInToReturn( - DataFlowCall call, ArgumentNode arg, Ap returnAp, Ap ap, Configuration config + DataFlowCall call, ArgumentNodeExt arg, Ap returnAp, Ap ap, Configuration config ) { revFlowIn(call, arg, true, apSome(returnAp), ap, config) } @@ -1838,13 +1841,15 @@ private module Stage3 { pragma[noinline] private predicate parameterFlow( - ParameterNode p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config + ParameterNodeExt p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config ) { revFlow(p, true, apSome(ap0), ap, config) and c = getNodeEnclosingCallable(p) } - predicate parameterMayFlowThrough(ParameterNode p, DataFlowCallable c, Ap ap, Configuration config) { + predicate parameterMayFlowThrough( + ParameterNodeExt p, DataFlowCallable c, Ap ap, Configuration config + ) { exists(ReturnNodeExt ret, Ap ap0, ReturnKindExt kind, int pos | parameterFlow(p, ap, ap0, c, config) and c = getNodeEnclosingCallable(ret) and @@ -2088,7 +2093,7 @@ private module Stage4 { private ApApprox getApprox(Ap ap) { result = ap.getFront() } private ApNil getApNil(Node node) { - PrevStage::revFlow(node, _) and result = TNil(getNodeType(node)) + PrevStage::revFlow(node, _) and result = TNil(getNodeDataFlowType(node)) } bindingset[tc, tail] @@ -2155,7 +2160,7 @@ private module Stage4 { pragma[nomagic] private predicate flowIntoCall( - DataFlowCall call, ArgumentNode node1, ParameterNode node2, boolean allowsFieldFlow, + DataFlowCall call, ArgumentNodeExt node1, ParameterNodeExt node2, boolean allowsFieldFlow, Configuration config ) { flowIntoCallNodeCand2(call, node1, node2, allowsFieldFlow, config) and @@ -2300,10 +2305,10 @@ private module Stage4 { pragma[nomagic] private predicate fwdFlowIn( - DataFlowCall call, ParameterNode p, Cc outercc, Cc innercc, ApOption argAp, Ap ap, + DataFlowCall call, ParameterNodeExt p, Cc outercc, Cc innercc, ApOption argAp, Ap ap, Configuration config ) { - exists(ArgumentNode arg, boolean allowsFieldFlow | + exists(ArgumentNodeExt arg, boolean allowsFieldFlow | fwdFlow(arg, outercc, argAp, ap, config) and flowIntoCall(call, arg, p, allowsFieldFlow, config) and innercc = getCallContextCall(call, getNodeEnclosingCallable(p), outercc) @@ -2348,7 +2353,7 @@ private module Stage4 { private predicate fwdFlowIsEntered( DataFlowCall call, Cc cc, ApOption argAp, Ap ap, Configuration config ) { - exists(ParameterNode p | + exists(ParameterNodeExt p | fwdFlowIn(call, p, cc, _, argAp, ap, config) and PrevStage::parameterMayFlowThrough(p, _, unbindApa(getApprox(ap)), config) ) @@ -2489,10 +2494,10 @@ private module Stage4 { pragma[nomagic] private predicate revFlowIn( - DataFlowCall call, ArgumentNode arg, boolean toReturn, ApOption returnAp, Ap ap, + DataFlowCall call, ArgumentNodeExt arg, boolean toReturn, ApOption returnAp, Ap ap, Configuration config ) { - exists(ParameterNode p, boolean allowsFieldFlow | + exists(ParameterNodeExt p, boolean allowsFieldFlow | revFlow(p, toReturn, returnAp, ap, config) and flowIntoCall(call, arg, p, allowsFieldFlow, config) | @@ -2502,7 +2507,7 @@ private module Stage4 { pragma[nomagic] private predicate revFlowInToReturn( - DataFlowCall call, ArgumentNode arg, Ap returnAp, Ap ap, Configuration config + DataFlowCall call, ArgumentNodeExt arg, Ap returnAp, Ap ap, Configuration config ) { revFlowIn(call, arg, true, apSome(returnAp), ap, config) } @@ -2555,13 +2560,15 @@ private module Stage4 { pragma[noinline] private predicate parameterFlow( - ParameterNode p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config + ParameterNodeExt p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config ) { revFlow(p, true, apSome(ap0), ap, config) and c = getNodeEnclosingCallable(p) } - predicate parameterMayFlowThrough(ParameterNode p, DataFlowCallable c, Ap ap, Configuration config) { + predicate parameterMayFlowThrough( + ParameterNodeExt p, DataFlowCallable c, Ap ap, Configuration config + ) { exists(ReturnNodeExt ret, Ap ap0, ReturnKindExt kind, int pos | parameterFlow(p, ap, ap0, c, config) and c = getNodeEnclosingCallable(ret) and @@ -2606,7 +2613,7 @@ private predicate nodeMayUseSummary(Node n, AccessPathApprox apa, Configuration private newtype TSummaryCtx = TSummaryCtxNone() or - TSummaryCtxSome(ParameterNode p, AccessPath ap) { + TSummaryCtxSome(ParameterNodeExt p, AccessPath ap) { Stage4::parameterMayFlowThrough(p, _, ap.getApprox(), _) } @@ -2627,7 +2634,7 @@ private class SummaryCtxNone extends SummaryCtx, TSummaryCtxNone { /** A summary context from which a flow summary can be generated. */ private class SummaryCtxSome extends SummaryCtx, TSummaryCtxSome { - private ParameterNode p; + private ParameterNodeExt p; private AccessPath ap; SummaryCtxSome() { this = TSummaryCtxSome(p, ap) } @@ -2758,7 +2765,7 @@ private newtype TPathNode = config.isSource(node) and cc instanceof CallContextAny and sc instanceof SummaryCtxNone and - ap = TAccessPathNil(getNodeType(node)) + ap = TAccessPathNil(getNodeDataFlowType(node)) or // ... or a step from an existing PathNode to another node. exists(PathNodeMid mid | @@ -2979,7 +2986,7 @@ class PathNode extends TPathNode { Configuration getConfiguration() { none() } private predicate isHidden() { - nodeIsHidden(this.getNode()) and + hiddenNode(this.getNode()) and not this.isSource() and not this instanceof PathNodeSink } @@ -3148,7 +3155,7 @@ private predicate pathStep(PathNodeMid mid, Node node, CallContext cc, SummaryCt cc instanceof CallContextAny and sc instanceof SummaryCtxNone and mid.getAp() instanceof AccessPathNil and - ap = TAccessPathNil(getNodeType(node)) + ap = TAccessPathNil(getNodeDataFlowType(node)) or exists(TypedContent tc | pathStoreStep(mid, node, ap.pop(tc), tc, cc)) and sc = mid.getSummaryCtx() @@ -3235,7 +3242,7 @@ pragma[noinline] private predicate pathIntoArg( PathNodeMid mid, int i, CallContext cc, DataFlowCall call, AccessPath ap, AccessPathApprox apa ) { - exists(ArgumentNode arg | + exists(ArgumentNodeExt arg | arg = mid.getNode() and cc = mid.getCallContext() and arg.argumentOf(call, i) and @@ -3248,7 +3255,7 @@ pragma[noinline] private predicate parameterCand( DataFlowCallable callable, int i, AccessPathApprox apa, Configuration config ) { - exists(ParameterNode p | + exists(ParameterNodeExt p | Stage4::revFlow(p, _, _, apa, config) and p.isParameterOf(callable, i) ) @@ -3272,7 +3279,7 @@ private predicate pathIntoCallable0( * respectively. */ private predicate pathIntoCallable( - PathNodeMid mid, ParameterNode p, CallContext outercc, CallContextCall innercc, SummaryCtx sc, + PathNodeMid mid, ParameterNodeExt p, CallContext outercc, CallContextCall innercc, SummaryCtx sc, DataFlowCall call ) { exists(int i, DataFlowCallable callable, AccessPath ap | @@ -3568,7 +3575,7 @@ private module FlowExploration { private newtype TSummaryCtx1 = TSummaryCtx1None() or - TSummaryCtx1Param(ParameterNode p) + TSummaryCtx1Param(ParameterNodeExt p) private newtype TSummaryCtx2 = TSummaryCtx2None() or @@ -3591,7 +3598,7 @@ private module FlowExploration { cc instanceof CallContextAny and sc1 = TSummaryCtx1None() and sc2 = TSummaryCtx2None() and - ap = TPartialNil(getNodeType(node)) and + ap = TPartialNil(getNodeDataFlowType(node)) and not fullBarrier(node, config) and exists(config.explorationLimit()) or @@ -3611,7 +3618,7 @@ private module FlowExploration { or exists(PartialPathNodeRev mid | revPartialPathStep(mid, node, sc1, sc2, ap, config) and - not clearsContent(node, ap.getHead()) and + not clearsContentCached(node, ap.getHead()) and not fullBarrier(node, config) and distSink(getNodeEnclosingCallable(node), config) <= config.explorationLimit() ) @@ -3625,9 +3632,9 @@ private module FlowExploration { exists(PartialPathNodeFwd mid | partialPathStep(mid, node, cc, sc1, sc2, ap, config) and not fullBarrier(node, config) and - not clearsContent(node, ap.getHead().getContent()) and + not clearsContentCached(node, ap.getHead().getContent()) and if node instanceof CastingNode - then compatibleTypes(getNodeType(node), ap.getType()) + then compatibleTypes(getNodeDataFlowType(node), ap.getType()) else any() ) } @@ -3783,7 +3790,7 @@ private module FlowExploration { PartialPathNodeFwd mid, Node node, CallContext cc, TSummaryCtx1 sc1, TSummaryCtx2 sc2, PartialAccessPath ap, Configuration config ) { - not isUnreachableInCall(node, cc.(CallContextSpecificCall).getCall()) and + not isUnreachableInCallCached(node, cc.(CallContextSpecificCall).getCall()) and ( localFlowStep(mid.getNode(), node, config) and cc = mid.getCallContext() and @@ -3797,7 +3804,7 @@ private module FlowExploration { sc1 = mid.getSummaryCtx1() and sc2 = mid.getSummaryCtx2() and mid.getAp() instanceof PartialAccessPathNil and - ap = TPartialNil(getNodeType(node)) and + ap = TPartialNil(getNodeDataFlowType(node)) and config = mid.getConfiguration() ) or @@ -3813,7 +3820,7 @@ private module FlowExploration { sc1 = TSummaryCtx1None() and sc2 = TSummaryCtx2None() and mid.getAp() instanceof PartialAccessPathNil and - ap = TPartialNil(getNodeType(node)) and + ap = TPartialNil(getNodeDataFlowType(node)) and config = mid.getConfiguration() or partialPathStoreStep(mid, _, _, node, ap) and @@ -3827,7 +3834,7 @@ private module FlowExploration { sc1 = mid.getSummaryCtx1() and sc2 = mid.getSummaryCtx2() and apConsFwd(ap, tc, ap0, config) and - compatibleTypes(ap.getType(), getNodeType(node)) + compatibleTypes(ap.getType(), getNodeDataFlowType(node)) ) or partialPathIntoCallable(mid, node, _, cc, sc1, sc2, _, ap, config) @@ -3924,7 +3931,7 @@ private module FlowExploration { PartialPathNodeFwd mid, int i, CallContext cc, DataFlowCall call, PartialAccessPath ap, Configuration config ) { - exists(ArgumentNode arg | + exists(ArgumentNodeExt arg | arg = mid.getNode() and cc = mid.getCallContext() and arg.argumentOf(call, i) and @@ -3943,7 +3950,7 @@ private module FlowExploration { } private predicate partialPathIntoCallable( - PartialPathNodeFwd mid, ParameterNode p, CallContext outercc, CallContextCall innercc, + PartialPathNodeFwd mid, ParameterNodeExt p, CallContext outercc, CallContextCall innercc, TSummaryCtx1 sc1, TSummaryCtx2 sc2, DataFlowCall call, PartialAccessPath ap, Configuration config ) { @@ -3980,7 +3987,7 @@ private module FlowExploration { DataFlowCall call, PartialPathNodeFwd mid, ReturnKindExt kind, CallContext cc, PartialAccessPath ap, Configuration config ) { - exists(ParameterNode p, CallContext innercc, TSummaryCtx1 sc1, TSummaryCtx2 sc2 | + exists(ParameterNodeExt p, CallContext innercc, TSummaryCtx1 sc1, TSummaryCtx2 sc2 | partialPathIntoCallable(mid, p, cc, innercc, sc1, sc2, call, _, config) and paramFlowsThroughInPartialPath(kind, innercc, sc1, sc2, ap, config) ) @@ -4037,7 +4044,7 @@ private module FlowExploration { apConsRev(ap, c, ap0, config) ) or - exists(ParameterNode p | + exists(ParameterNodeExt p | mid.getNode() = p and viableParamArg(_, p, node) and sc1 = mid.getSummaryCtx1() and @@ -4115,7 +4122,7 @@ private module FlowExploration { int pos, TRevSummaryCtx1Some sc1, TRevSummaryCtx2Some sc2, RevPartialAccessPath ap, Configuration config ) { - exists(PartialPathNodeRev mid, ParameterNode p | + exists(PartialPathNodeRev mid, ParameterNodeExt p | mid.getNode() = p and p.isParameterOf(_, pos) and sc1 = mid.getSummaryCtx1() and @@ -4138,7 +4145,7 @@ private module FlowExploration { pragma[nomagic] private predicate revPartialPathThroughCallable( - PartialPathNodeRev mid, ArgumentNode node, RevPartialAccessPath ap, Configuration config + PartialPathNodeRev mid, ArgumentNodeExt node, RevPartialAccessPath ap, Configuration config ) { exists(DataFlowCall call, int pos | revPartialPathThroughCallable0(call, mid, pos, ap, config) and 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 a51c20c2288..b5ceabc605d 100644 --- a/java/ql/src/semmle/code/java/dataflow/internal/DataFlowImplCommon.qll +++ b/java/ql/src/semmle/code/java/dataflow/internal/DataFlowImplCommon.qll @@ -35,22 +35,24 @@ predicate accessPathCostLimits(int apLimit, int tupleLimit) { * calls. For this reason, we cannot reuse the code from `DataFlowImpl.qll` directly. */ private module LambdaFlow { - private predicate viableParamNonLambda(DataFlowCall call, int i, ParameterNode p) { + private predicate viableParamNonLambda(DataFlowCall call, int i, ParameterNodeExt p) { p.isParameterOf(viableCallable(call), i) } - private predicate viableParamLambda(DataFlowCall call, int i, ParameterNode p) { + private predicate viableParamLambda(DataFlowCall call, int i, ParameterNodeExt p) { p.isParameterOf(viableCallableLambda(call, _), i) } - private predicate viableParamArgNonLambda(DataFlowCall call, ParameterNode p, ArgumentNode arg) { + private predicate viableParamArgNonLambda( + DataFlowCall call, ParameterNodeExt p, ArgumentNodeExt arg + ) { exists(int i | viableParamNonLambda(call, i, p) and arg.argumentOf(call, i) ) } - private predicate viableParamArgLambda(DataFlowCall call, ParameterNode p, ArgumentNode arg) { + private predicate viableParamArgLambda(DataFlowCall call, ParameterNodeExt p, ArgumentNodeExt arg) { exists(int i | viableParamLambda(call, i, p) and arg.argumentOf(call, i) @@ -118,8 +120,8 @@ private module LambdaFlow { boolean toJump, DataFlowCallOption lastCall ) { revLambdaFlow0(lambdaCall, kind, node, t, toReturn, toJump, lastCall) and - if node instanceof CastNode or node instanceof ArgumentNode or node instanceof ReturnNode - then compatibleTypes(t, getNodeType(node)) + if castNode(node) or node instanceof ArgumentNodeExt or node instanceof ReturnNode + then compatibleTypes(t, getNodeDataFlowType(node)) else any() } @@ -129,7 +131,7 @@ private module LambdaFlow { boolean toJump, DataFlowCallOption lastCall ) { lambdaCall(lambdaCall, kind, node) and - t = getNodeType(node) and + t = getNodeDataFlowType(node) and toReturn = false and toJump = false and lastCall = TDataFlowCallNone() @@ -146,7 +148,7 @@ private module LambdaFlow { getNodeEnclosingCallable(node) = getNodeEnclosingCallable(mid) | preservesValue = false and - t = getNodeType(node) + t = getNodeDataFlowType(node) or preservesValue = true and t = t0 @@ -160,7 +162,7 @@ private module LambdaFlow { toJump = true and lastCall = TDataFlowCallNone() | - jumpStep(node, mid) and + jumpStepCached(node, mid) and t = t0 or exists(boolean preservesValue | @@ -168,7 +170,7 @@ private module LambdaFlow { getNodeEnclosingCallable(node) != getNodeEnclosingCallable(mid) | preservesValue = false and - t = getNodeType(node) + t = getNodeDataFlowType(node) or preservesValue = true and t = t0 @@ -176,7 +178,7 @@ private module LambdaFlow { ) or // flow into a callable - exists(ParameterNode p, DataFlowCallOption lastCall0, DataFlowCall call | + exists(ParameterNodeExt p, DataFlowCallOption lastCall0, DataFlowCall call | revLambdaFlowIn(lambdaCall, kind, p, t, toJump, lastCall0) and ( if lastCall0 = TDataFlowCallNone() and toJump = false @@ -227,8 +229,8 @@ private module LambdaFlow { pragma[nomagic] predicate revLambdaFlowIn( - DataFlowCall lambdaCall, LambdaCallKind kind, ParameterNode p, DataFlowType t, boolean toJump, - DataFlowCallOption lastCall + DataFlowCall lambdaCall, LambdaCallKind kind, ParameterNodeExt p, DataFlowType t, + boolean toJump, DataFlowCallOption lastCall ) { revLambdaFlow(lambdaCall, kind, p, t, false, toJump, lastCall) } @@ -242,6 +244,89 @@ private DataFlowCallable viableCallableExt(DataFlowCall call) { cached private module Cached { + /** + * If needed, call this predicate from `DataFlowImplSpecific.qll` in order to + * force a stage-dependency on the `DataFlowImplCommon.qll` stage and therby + * collapsing the two stages. + */ + cached + predicate forceCachingInSameStage() { any() } + + cached + predicate nodeEnclosingCallable(Node n, DataFlowCallable c) { c = n.getEnclosingCallable() } + + cached + predicate callEnclosingCallable(DataFlowCall call, DataFlowCallable c) { + c = call.getEnclosingCallable() + } + + cached + predicate nodeDataFlowType(Node n, DataFlowType t) { t = getNodeType(n) } + + cached + predicate jumpStepCached(Node node1, Node node2) { jumpStep(node1, node2) } + + cached + predicate clearsContentCached(Node n, Content c) { clearsContent(n, c) } + + cached + predicate isUnreachableInCallCached(Node n, DataFlowCall call) { isUnreachableInCall(n, call) } + + cached + predicate outNodeExt(Node n) { + n instanceof OutNode + or + n.(PostUpdateNode).getPreUpdateNode() instanceof ArgumentNodeExt + } + + cached + predicate hiddenNode(Node n) { nodeIsHidden(n) } + + cached + OutNodeExt getAnOutNodeExt(DataFlowCall call, ReturnKindExt k) { + result = getAnOutNode(call, k.(ValueReturnKind).getKind()) + or + exists(ArgumentNodeExt arg | + result.(PostUpdateNode).getPreUpdateNode() = arg and + arg.argumentOf(call, k.(ParamUpdateReturnKind).getPosition()) + ) + } + + cached + predicate returnNodeExt(Node n, ReturnKindExt k) { + k = TValueReturn(n.(ReturnNode).getKind()) + or + exists(ParameterNodeExt p, int pos | + parameterValueFlowsToPreUpdate(p, n) and + p.isParameterOf(_, pos) and + k = TParamUpdate(pos) + ) + } + + cached + predicate castNode(Node n) { n instanceof CastNode } + + cached + predicate castingNode(Node n) { + castNode(n) or + n instanceof ParameterNodeExt or + n instanceof OutNodeExt or + // For reads, `x.f`, we want to check that the tracked type after the read (which + // is obtained by popping the head of the access path stack) is compatible with + // the type of `x.f`. + read(_, _, n) + } + + cached + predicate parameterNode(Node n, DataFlowCallable c, int i) { + n.(ParameterNode).isParameterOf(c, i) + } + + cached + predicate argumentNode(Node n, DataFlowCall call, int pos) { + n.(ArgumentNode).argumentOf(call, pos) + } + /** * Gets a viable target for the lambda call `call`. * @@ -261,7 +346,7 @@ private module Cached { * The instance parameter is considered to have index `-1`. */ pragma[nomagic] - private predicate viableParam(DataFlowCall call, int i, ParameterNode p) { + private predicate viableParam(DataFlowCall call, int i, ParameterNodeExt p) { p.isParameterOf(viableCallableExt(call), i) } @@ -270,11 +355,11 @@ private module Cached { * dispatch into account. */ cached - predicate viableParamArg(DataFlowCall call, ParameterNode p, ArgumentNode arg) { + predicate viableParamArg(DataFlowCall call, ParameterNodeExt p, ArgumentNodeExt arg) { exists(int i | viableParam(call, i, p) and arg.argumentOf(call, i) and - compatibleTypes(getNodeType(arg), getNodeType(p)) + compatibleTypes(getNodeDataFlowType(arg), getNodeDataFlowType(p)) ) } @@ -312,7 +397,7 @@ private module Cached { * `read` indicates whether it is contents of `p` that can flow to `node`. */ pragma[nomagic] - private predicate parameterValueFlowCand(ParameterNode p, Node node, boolean read) { + private predicate parameterValueFlowCand(ParameterNodeExt p, Node node, boolean read) { p = node and read = false or @@ -325,30 +410,32 @@ private module Cached { // read exists(Node mid | parameterValueFlowCand(p, mid, false) and - readStep(mid, _, node) and + read(mid, _, node) and read = true ) or // flow through: no prior read - exists(ArgumentNode arg | + exists(ArgumentNodeExt arg | parameterValueFlowArgCand(p, arg, false) and argumentValueFlowsThroughCand(arg, node, read) ) or // flow through: no read inside method - exists(ArgumentNode arg | + exists(ArgumentNodeExt arg | parameterValueFlowArgCand(p, arg, read) and argumentValueFlowsThroughCand(arg, node, false) ) } pragma[nomagic] - private predicate parameterValueFlowArgCand(ParameterNode p, ArgumentNode arg, boolean read) { + private predicate parameterValueFlowArgCand( + ParameterNodeExt p, ArgumentNodeExt arg, boolean read + ) { parameterValueFlowCand(p, arg, read) } pragma[nomagic] - predicate parameterValueFlowsToPreUpdateCand(ParameterNode p, PostUpdateNode n) { + predicate parameterValueFlowsToPreUpdateCand(ParameterNodeExt p, PostUpdateNode n) { parameterValueFlowCand(p, n.getPreUpdateNode(), false) } @@ -360,7 +447,7 @@ private module Cached { * `read` indicates whether it is contents of `p` that can flow to the return * node. */ - predicate parameterValueFlowReturnCand(ParameterNode p, ReturnKind kind, boolean read) { + predicate parameterValueFlowReturnCand(ParameterNodeExt p, ReturnKind kind, boolean read) { exists(ReturnNode ret | parameterValueFlowCand(p, ret, read) and kind = ret.getKind() @@ -369,9 +456,9 @@ private module Cached { pragma[nomagic] private predicate argumentValueFlowsThroughCand0( - DataFlowCall call, ArgumentNode arg, ReturnKind kind, boolean read + DataFlowCall call, ArgumentNodeExt arg, ReturnKind kind, boolean read ) { - exists(ParameterNode param | viableParamArg(call, param, arg) | + exists(ParameterNodeExt param | viableParamArg(call, param, arg) | parameterValueFlowReturnCand(param, kind, read) ) } @@ -382,14 +469,14 @@ private module Cached { * * `read` indicates whether it is contents of `arg` that can flow to `out`. */ - predicate argumentValueFlowsThroughCand(ArgumentNode arg, Node out, boolean read) { + predicate argumentValueFlowsThroughCand(ArgumentNodeExt arg, Node out, boolean read) { exists(DataFlowCall call, ReturnKind kind | argumentValueFlowsThroughCand0(call, arg, kind, read) and out = getAnOutNode(call, kind) ) } - predicate cand(ParameterNode p, Node n) { + predicate cand(ParameterNodeExt p, Node n) { parameterValueFlowCand(p, n, _) and ( parameterValueFlowReturnCand(p, _, _) @@ -416,21 +503,21 @@ private module Cached { * If a read step was taken, then `read` captures the `Content`, the * container type, and the content type. */ - predicate parameterValueFlow(ParameterNode p, Node node, ReadStepTypesOption read) { + predicate parameterValueFlow(ParameterNodeExt p, Node node, ReadStepTypesOption read) { parameterValueFlow0(p, node, read) and if node instanceof CastingNode then // normal flow through read = TReadStepTypesNone() and - compatibleTypes(getNodeType(p), getNodeType(node)) + compatibleTypes(getNodeDataFlowType(p), getNodeDataFlowType(node)) or // getter - compatibleTypes(read.getContentType(), getNodeType(node)) + compatibleTypes(read.getContentType(), getNodeDataFlowType(node)) else any() } pragma[nomagic] - private predicate parameterValueFlow0(ParameterNode p, Node node, ReadStepTypesOption read) { + private predicate parameterValueFlow0(ParameterNodeExt p, Node node, ReadStepTypesOption read) { p = node and Cand::cand(p, _) and read = TReadStepTypesNone() @@ -447,7 +534,7 @@ private module Cached { readStepWithTypes(mid, read.getContainerType(), read.getContent(), node, read.getContentType()) and Cand::parameterValueFlowReturnCand(p, _, true) and - compatibleTypes(getNodeType(p), read.getContainerType()) + compatibleTypes(getNodeDataFlowType(p), read.getContainerType()) ) or parameterValueFlow0_0(TReadStepTypesNone(), p, node, read) @@ -455,16 +542,16 @@ private module Cached { pragma[nomagic] private predicate parameterValueFlow0_0( - ReadStepTypesOption mustBeNone, ParameterNode p, Node node, ReadStepTypesOption read + ReadStepTypesOption mustBeNone, ParameterNodeExt p, Node node, ReadStepTypesOption read ) { // flow through: no prior read - exists(ArgumentNode arg | + exists(ArgumentNodeExt arg | parameterValueFlowArg(p, arg, mustBeNone) and argumentValueFlowsThrough(arg, read, node) ) or // flow through: no read inside method - exists(ArgumentNode arg | + exists(ArgumentNodeExt arg | parameterValueFlowArg(p, arg, read) and argumentValueFlowsThrough(arg, mustBeNone, node) ) @@ -472,7 +559,7 @@ private module Cached { pragma[nomagic] private predicate parameterValueFlowArg( - ParameterNode p, ArgumentNode arg, ReadStepTypesOption read + ParameterNodeExt p, ArgumentNodeExt arg, ReadStepTypesOption read ) { parameterValueFlow(p, arg, read) and Cand::argumentValueFlowsThroughCand(arg, _, _) @@ -480,9 +567,9 @@ private module Cached { pragma[nomagic] private predicate argumentValueFlowsThrough0( - DataFlowCall call, ArgumentNode arg, ReturnKind kind, ReadStepTypesOption read + DataFlowCall call, ArgumentNodeExt arg, ReturnKind kind, ReadStepTypesOption read ) { - exists(ParameterNode param | viableParamArg(call, param, arg) | + exists(ParameterNodeExt param | viableParamArg(call, param, arg) | parameterValueFlowReturn(param, kind, read) ) } @@ -496,18 +583,18 @@ private module Cached { * container type, and the content type. */ pragma[nomagic] - predicate argumentValueFlowsThrough(ArgumentNode arg, ReadStepTypesOption read, Node out) { + predicate argumentValueFlowsThrough(ArgumentNodeExt arg, ReadStepTypesOption read, Node out) { exists(DataFlowCall call, ReturnKind kind | argumentValueFlowsThrough0(call, arg, kind, read) and out = getAnOutNode(call, kind) | // normal flow through read = TReadStepTypesNone() and - compatibleTypes(getNodeType(arg), getNodeType(out)) + compatibleTypes(getNodeDataFlowType(arg), getNodeDataFlowType(out)) or // getter - compatibleTypes(getNodeType(arg), read.getContainerType()) and - compatibleTypes(read.getContentType(), getNodeType(out)) + compatibleTypes(getNodeDataFlowType(arg), read.getContainerType()) and + compatibleTypes(read.getContentType(), getNodeDataFlowType(out)) ) } @@ -516,7 +603,7 @@ private module Cached { * value-preserving steps and a single read step, not taking call * contexts into account, thus representing a getter-step. */ - predicate getterStep(ArgumentNode arg, Content c, Node out) { + predicate getterStep(ArgumentNodeExt arg, Content c, Node out) { argumentValueFlowsThrough(arg, TReadStepTypesSome(_, c, _), out) } @@ -529,7 +616,7 @@ private module Cached { * container type, and the content type. */ private predicate parameterValueFlowReturn( - ParameterNode p, ReturnKind kind, ReadStepTypesOption read + ParameterNodeExt p, ReturnKind kind, ReadStepTypesOption read ) { exists(ReturnNode ret | parameterValueFlow(p, ret, read) and @@ -553,7 +640,7 @@ private module Cached { private predicate mayBenefitFromCallContextExt(DataFlowCall call, DataFlowCallable callable) { mayBenefitFromCallContext(call, callable) or - callable = call.getEnclosingCallable() and + callEnclosingCallable(call, callable) and exists(viableCallableLambda(call, TDataFlowCallSome(_))) } @@ -611,7 +698,7 @@ private module Cached { mayBenefitFromCallContextExt(call, _) and c = viableCallableExt(call) and ctxtgts = count(DataFlowCall ctx | c = viableImplInCallContextExt(call, ctx)) and - tgts = strictcount(DataFlowCall ctx | viableCallableExt(ctx) = call.getEnclosingCallable()) and + tgts = strictcount(DataFlowCall ctx | callEnclosingCallable(call, viableCallableExt(ctx))) and ctxtgts < tgts ) } @@ -635,8 +722,7 @@ private module Cached { * Holds if `p` can flow to the pre-update node associated with post-update * node `n`, in the same callable, using only value-preserving steps. */ - cached - predicate parameterValueFlowsToPreUpdate(ParameterNode p, PostUpdateNode n) { + private predicate parameterValueFlowsToPreUpdate(ParameterNodeExt p, PostUpdateNode n) { parameterValueFlow(p, n.getPreUpdateNode(), TReadStepTypesNone()) } @@ -644,9 +730,9 @@ private module Cached { Node node1, Content c, Node node2, DataFlowType contentType, DataFlowType containerType ) { storeStep(node1, c, node2) and - readStep(_, c, _) and - contentType = getNodeType(node1) and - containerType = getNodeType(node2) + read(_, c, _) and + contentType = getNodeDataFlowType(node1) and + containerType = getNodeDataFlowType(node2) or exists(Node n1, Node n2 | n1 = node1.(PostUpdateNode).getPreUpdateNode() and @@ -654,12 +740,15 @@ private module Cached { | argumentValueFlowsThrough(n2, TReadStepTypesSome(containerType, c, contentType), n1) or - readStep(n2, c, n1) and - contentType = getNodeType(n1) and - containerType = getNodeType(n2) + read(n2, c, n1) and + contentType = getNodeDataFlowType(n1) and + containerType = getNodeDataFlowType(n2) ) } + cached + predicate read(Node node1, Content c, Node node2) { readStep(node1, c, node2) } + /** * Holds if data can flow from `node1` to `node2` via a direct assignment to * `f`. @@ -678,8 +767,9 @@ private module Cached { * are aliases. A typical example is a function returning `this`, implementing a fluent * interface. */ - cached - predicate reverseStepThroughInputOutputAlias(PostUpdateNode fromNode, PostUpdateNode toNode) { + private predicate reverseStepThroughInputOutputAlias( + PostUpdateNode fromNode, PostUpdateNode toNode + ) { exists(Node fromPre, Node toPre | fromPre = fromNode.getPreUpdateNode() and toPre = toNode.getPreUpdateNode() @@ -688,14 +778,20 @@ private module Cached { // Does the language-specific simpleLocalFlowStep already model flow // from function input to output? fromPre = getAnOutNode(c, _) and - toPre.(ArgumentNode).argumentOf(c, _) and - simpleLocalFlowStep(toPre.(ArgumentNode), fromPre) + toPre.(ArgumentNodeExt).argumentOf(c, _) and + simpleLocalFlowStep(toPre.(ArgumentNodeExt), fromPre) ) or argumentValueFlowsThrough(toPre, TReadStepTypesNone(), fromPre) ) } + cached + predicate simpleLocalFlowStepExt(Node node1, Node node2) { + simpleLocalFlowStep(node1, node2) or + reverseStepThroughInputOutputAlias(node1, node2) + } + /** * Holds if the call context `call` either improves virtual dispatch in * `callable` or if it allows us to prune unreachable nodes in `callable`. @@ -704,7 +800,7 @@ private module Cached { predicate recordDataFlowCallSite(DataFlowCall call, DataFlowCallable callable) { reducedViableImplInCallContext(_, callable, call) or - exists(Node n | getNodeEnclosingCallable(n) = callable | isUnreachableInCall(n, call)) + exists(Node n | getNodeEnclosingCallable(n) = callable | isUnreachableInCallCached(n, call)) } cached @@ -726,12 +822,12 @@ private module Cached { cached newtype TLocalFlowCallContext = TAnyLocalCall() or - TSpecificLocalCall(DataFlowCall call) { isUnreachableInCall(_, call) } + TSpecificLocalCall(DataFlowCall call) { isUnreachableInCallCached(_, call) } cached newtype TReturnKindExt = TValueReturn(ReturnKind kind) or - TParamUpdate(int pos) { exists(ParameterNode p | p.isParameterOf(_, pos)) } + TParamUpdate(int pos) { exists(ParameterNodeExt p | p.isParameterOf(_, pos)) } cached newtype TBooleanOption = @@ -761,23 +857,15 @@ private module Cached { * A `Node` at which a cast can occur such that the type should be checked. */ class CastingNode extends Node { - CastingNode() { - this instanceof ParameterNode or - this instanceof CastNode or - this instanceof OutNodeExt or - // For reads, `x.f`, we want to check that the tracked type after the read (which - // is obtained by popping the head of the access path stack) is compatible with - // the type of `x.f`. - readStep(_, _, this) - } + CastingNode() { castingNode(this) } } private predicate readStepWithTypes( Node n1, DataFlowType container, Content c, Node n2, DataFlowType content ) { - readStep(n1, c, n2) and - container = getNodeType(n1) and - content = getNodeType(n2) + read(n1, c, n2) and + container = getNodeDataFlowType(n1) and + content = getNodeDataFlowType(n2) } private newtype TReadStepTypesOption = @@ -854,7 +942,7 @@ class CallContextSomeCall extends CallContextCall, TSomeCall { override string toString() { result = "CcSomeCall" } override predicate relevantFor(DataFlowCallable callable) { - exists(ParameterNode p | getNodeEnclosingCallable(p) = callable) + exists(ParameterNodeExt p | getNodeEnclosingCallable(p) = callable) } override predicate matchesCall(DataFlowCall call) { any() } @@ -866,7 +954,7 @@ class CallContextReturn extends CallContextNoCall, TReturn { } override predicate relevantFor(DataFlowCallable callable) { - exists(DataFlowCall call | this = TReturn(_, call) and call.getEnclosingCallable() = callable) + exists(DataFlowCall call | this = TReturn(_, call) and callEnclosingCallable(call, callable)) } } @@ -899,7 +987,7 @@ class LocalCallContextSpecificCall extends LocalCallContext, TSpecificLocalCall } private predicate relevantLocalCCtx(DataFlowCall call, DataFlowCallable callable) { - exists(Node n | getNodeEnclosingCallable(n) = callable and isUnreachableInCall(n, call)) + exists(Node n | getNodeEnclosingCallable(n) = callable and isUnreachableInCallCached(n, call)) } /** @@ -913,26 +1001,37 @@ LocalCallContext getLocalCallContext(CallContext ctx, DataFlowCallable callable) else result instanceof LocalCallContextAny } +/** + * The value of a parameter at function entry, viewed as a node in a data + * flow graph. + */ +class ParameterNodeExt extends Node { + ParameterNodeExt() { parameterNode(this, _, _) } + + /** + * Holds if this node is the parameter of callable `c` at the specified + * (zero-based) position. + */ + predicate isParameterOf(DataFlowCallable c, int i) { parameterNode(this, c, i) } +} + +/** A data-flow node that represents a call argument. */ +class ArgumentNodeExt extends Node { + ArgumentNodeExt() { argumentNode(this, _, _) } + + /** Holds if this argument occurs at the given position in the given call. */ + final predicate argumentOf(DataFlowCall call, int pos) { argumentNode(this, call, pos) } +} + /** * A node from which flow can return to the caller. This is either a regular * `ReturnNode` or a `PostUpdateNode` corresponding to the value of a parameter. */ class ReturnNodeExt extends Node { - ReturnNodeExt() { - this instanceof ReturnNode or - parameterValueFlowsToPreUpdate(_, this) - } + ReturnNodeExt() { returnNodeExt(this, _) } /** Gets the kind of this returned value. */ - ReturnKindExt getKind() { - result = TValueReturn(this.(ReturnNode).getKind()) - or - exists(ParameterNode p, int pos | - parameterValueFlowsToPreUpdate(p, this) and - p.isParameterOf(_, pos) and - result = TParamUpdate(pos) - ) - } + ReturnKindExt getKind() { returnNodeExt(this, result) } } /** @@ -940,11 +1039,7 @@ class ReturnNodeExt extends Node { * or a post-update node associated with a call argument. */ class OutNodeExt extends Node { - OutNodeExt() { - this instanceof OutNode - or - this.(PostUpdateNode).getPreUpdateNode() instanceof ArgumentNode - } + OutNodeExt() { outNodeExt(this) } } /** @@ -957,7 +1052,7 @@ abstract class ReturnKindExt extends TReturnKindExt { abstract string toString(); /** Gets a node corresponding to data flow out of `call`. */ - abstract OutNodeExt getAnOutNode(DataFlowCall call); + final OutNodeExt getAnOutNode(DataFlowCall call) { result = getAnOutNodeExt(call, this) } } class ValueReturnKind extends ReturnKindExt, TValueReturn { @@ -968,10 +1063,6 @@ class ValueReturnKind extends ReturnKindExt, TValueReturn { ReturnKind getKind() { result = kind } override string toString() { result = kind.toString() } - - override OutNodeExt getAnOutNode(DataFlowCall call) { - result = getAnOutNode(call, this.getKind()) - } } class ParamUpdateReturnKind extends ReturnKindExt, TParamUpdate { @@ -982,13 +1073,6 @@ class ParamUpdateReturnKind extends ReturnKindExt, TParamUpdate { int getPosition() { result = pos } override string toString() { result = "param update " + pos } - - override OutNodeExt getAnOutNode(DataFlowCall call) { - exists(ArgumentNode arg | - result.(PostUpdateNode).getPreUpdateNode() = arg and - arg.argumentOf(call, this.getPosition()) - ) - } } /** A callable tagged with a relevant return kind. */ @@ -1015,10 +1099,13 @@ class ReturnPosition extends TReturnPosition0 { */ pragma[inline] DataFlowCallable getNodeEnclosingCallable(Node n) { - exists(Node n0 | - pragma[only_bind_into](n0) = n and - pragma[only_bind_into](result) = n0.getEnclosingCallable() - ) + nodeEnclosingCallable(pragma[only_bind_out](n), pragma[only_bind_into](result)) +} + +/** Gets the type of `n` used for type pruning. */ +pragma[inline] +DataFlowType getNodeDataFlowType(Node n) { + nodeDataFlowType(pragma[only_bind_out](n), pragma[only_bind_into](result)) } pragma[noinline] @@ -1042,7 +1129,7 @@ predicate resolveReturn(CallContext cc, DataFlowCallable callable, DataFlowCall cc instanceof CallContextAny and callable = viableCallableExt(call) or exists(DataFlowCallable c0, DataFlowCall call0 | - call0.getEnclosingCallable() = callable and + callEnclosingCallable(call0, callable) and cc = TReturn(c0, call0) and c0 = prunedViableImplInCallContextReverse(call0, call) ) @@ -1063,8 +1150,6 @@ DataFlowCallable resolveCall(DataFlowCall call, CallContext cc) { result = viableCallableExt(call) and cc instanceof CallContextReturn } -predicate read = readStep/3; - /** An optional Boolean value. */ class BooleanOption extends TBooleanOption { string toString() { @@ -1116,7 +1201,7 @@ abstract class AccessPathFront extends TAccessPathFront { TypedContent getHead() { this = TFrontHead(result) } - predicate isClearedAt(Node n) { clearsContent(n, getHead().getContent()) } + predicate isClearedAt(Node n) { clearsContentCached(n, getHead().getContent()) } } class AccessPathFrontNil extends AccessPathFront, TFrontNil { diff --git a/python/ql/src/semmle/python/dataflow/new/internal/DataFlowImpl.qll b/python/ql/src/semmle/python/dataflow/new/internal/DataFlowImpl.qll index 9498e51e7e6..cf83530bcbd 100644 --- a/python/ql/src/semmle/python/dataflow/new/internal/DataFlowImpl.qll +++ b/python/ql/src/semmle/python/dataflow/new/internal/DataFlowImpl.qll @@ -211,10 +211,7 @@ private predicate fullBarrier(Node node, Configuration config) { * Holds if data can flow in one local step from `node1` to `node2`. */ private predicate localFlowStep(Node node1, Node node2, Configuration config) { - ( - simpleLocalFlowStep(node1, node2) or - reverseStepThroughInputOutputAlias(node1, node2) - ) and + simpleLocalFlowStepExt(node1, node2) and not outBarrier(node1, config) and not inBarrier(node2, config) and not fullBarrier(node1, config) and @@ -237,7 +234,7 @@ private predicate additionalLocalFlowStep(Node node1, Node node2, Configuration * Holds if data can flow from `node1` to `node2` in a way that discards call contexts. */ private predicate jumpStep(Node node1, Node node2, Configuration config) { - jumpStep(node1, node2) and + jumpStepCached(node1, node2) and not outBarrier(node1, config) and not inBarrier(node2, config) and not fullBarrier(node1, config) and @@ -388,7 +385,7 @@ private module Stage1 { */ pragma[nomagic] private predicate fwdFlowIsEntered(DataFlowCall call, Cc cc, Configuration config) { - exists(ArgumentNode arg | + exists(ArgumentNodeExt arg | fwdFlow(arg, cc, config) and viableParamArg(call, _, arg) ) @@ -515,7 +512,7 @@ private module Stage1 { pragma[nomagic] predicate viableParamArgNodeCandFwd1( - DataFlowCall call, ParameterNode p, ArgumentNode arg, Configuration config + DataFlowCall call, ParameterNodeExt p, ArgumentNodeExt arg, Configuration config ) { viableParamArg(call, p, arg) and fwdFlow(arg, config) @@ -523,16 +520,16 @@ private module Stage1 { pragma[nomagic] private predicate revFlowIn( - DataFlowCall call, ArgumentNode arg, boolean toReturn, Configuration config + DataFlowCall call, ArgumentNodeExt arg, boolean toReturn, Configuration config ) { - exists(ParameterNode p | + exists(ParameterNodeExt p | revFlow(p, toReturn, config) and viableParamArgNodeCandFwd1(call, p, arg, config) ) } pragma[nomagic] - private predicate revFlowInToReturn(DataFlowCall call, ArgumentNode arg, Configuration config) { + private predicate revFlowInToReturn(DataFlowCall call, ArgumentNodeExt arg, Configuration config) { revFlowIn(call, arg, true, config) } @@ -597,7 +594,9 @@ private module Stage1 { * Holds if flow may enter through `p` and reach a return node making `p` a * candidate for the origin of a summary. */ - predicate parameterMayFlowThrough(ParameterNode p, DataFlowCallable c, Ap ap, Configuration config) { + predicate parameterMayFlowThrough( + ParameterNodeExt p, DataFlowCallable c, Ap ap, Configuration config + ) { exists(ReturnKindExt kind | throughFlowNodeCand(p, config) and returnFlowCallableNodeCand(c, kind, config) and @@ -663,7 +662,7 @@ private predicate flowOutOfCallNodeCand1( pragma[nomagic] private predicate viableParamArgNodeCand1( - DataFlowCall call, ParameterNode p, ArgumentNode arg, Configuration config + DataFlowCall call, ParameterNodeExt p, ArgumentNodeExt arg, Configuration config ) { Stage1::viableParamArgNodeCandFwd1(call, p, arg, config) and Stage1::revFlow(arg, config) @@ -675,7 +674,7 @@ private predicate viableParamArgNodeCand1( */ pragma[nomagic] private predicate flowIntoCallNodeCand1( - DataFlowCall call, ArgumentNode arg, ParameterNode p, Configuration config + DataFlowCall call, ArgumentNodeExt arg, ParameterNodeExt p, Configuration config ) { viableParamArgNodeCand1(call, p, arg, config) and Stage1::revFlow(p, config) and @@ -735,7 +734,7 @@ private predicate flowOutOfCallNodeCand1( */ pragma[nomagic] private predicate flowIntoCallNodeCand1( - DataFlowCall call, ArgumentNode arg, ParameterNode p, boolean allowsFieldFlow, + DataFlowCall call, ArgumentNodeExt arg, ParameterNodeExt p, boolean allowsFieldFlow, Configuration config ) { flowIntoCallNodeCand1(call, arg, p, config) and @@ -944,10 +943,10 @@ private module Stage2 { pragma[nomagic] private predicate fwdFlowIn( - DataFlowCall call, ParameterNode p, Cc outercc, Cc innercc, ApOption argAp, Ap ap, + DataFlowCall call, ParameterNodeExt p, Cc outercc, Cc innercc, ApOption argAp, Ap ap, Configuration config ) { - exists(ArgumentNode arg, boolean allowsFieldFlow | + exists(ArgumentNodeExt arg, boolean allowsFieldFlow | fwdFlow(arg, outercc, argAp, ap, config) and flowIntoCall(call, arg, p, allowsFieldFlow, config) and innercc = getCallContextCall(call, getNodeEnclosingCallable(p), outercc) @@ -992,7 +991,7 @@ private module Stage2 { private predicate fwdFlowIsEntered( DataFlowCall call, Cc cc, ApOption argAp, Ap ap, Configuration config ) { - exists(ParameterNode p | + exists(ParameterNodeExt p | fwdFlowIn(call, p, cc, _, argAp, ap, config) and PrevStage::parameterMayFlowThrough(p, _, getApprox(ap), config) ) @@ -1133,10 +1132,10 @@ private module Stage2 { pragma[nomagic] private predicate revFlowIn( - DataFlowCall call, ArgumentNode arg, boolean toReturn, ApOption returnAp, Ap ap, + DataFlowCall call, ArgumentNodeExt arg, boolean toReturn, ApOption returnAp, Ap ap, Configuration config ) { - exists(ParameterNode p, boolean allowsFieldFlow | + exists(ParameterNodeExt p, boolean allowsFieldFlow | revFlow(p, toReturn, returnAp, ap, config) and flowIntoCall(call, arg, p, allowsFieldFlow, config) | @@ -1146,7 +1145,7 @@ private module Stage2 { pragma[nomagic] private predicate revFlowInToReturn( - DataFlowCall call, ArgumentNode arg, Ap returnAp, Ap ap, Configuration config + DataFlowCall call, ArgumentNodeExt arg, Ap returnAp, Ap ap, Configuration config ) { revFlowIn(call, arg, true, apSome(returnAp), ap, config) } @@ -1199,13 +1198,15 @@ private module Stage2 { pragma[noinline] private predicate parameterFlow( - ParameterNode p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config + ParameterNodeExt p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config ) { revFlow(p, true, apSome(ap0), ap, config) and c = getNodeEnclosingCallable(p) } - predicate parameterMayFlowThrough(ParameterNode p, DataFlowCallable c, Ap ap, Configuration config) { + predicate parameterMayFlowThrough( + ParameterNodeExt p, DataFlowCallable c, Ap ap, Configuration config + ) { exists(ReturnNodeExt ret, Ap ap0, ReturnKindExt kind, int pos | parameterFlow(p, ap, ap0, c, config) and c = getNodeEnclosingCallable(ret) and @@ -1245,7 +1246,7 @@ private predicate flowOutOfCallNodeCand2( pragma[nomagic] private predicate flowIntoCallNodeCand2( - DataFlowCall call, ArgumentNode node1, ParameterNode node2, boolean allowsFieldFlow, + DataFlowCall call, ArgumentNodeExt node1, ParameterNodeExt node2, boolean allowsFieldFlow, Configuration config ) { flowIntoCallNodeCand1(call, node1, node2, allowsFieldFlow, config) and @@ -1260,8 +1261,8 @@ private module LocalFlowBigStep { */ private class FlowCheckNode extends Node { FlowCheckNode() { - this instanceof CastNode or - clearsContent(this, _) + castNode(this) or + clearsContentCached(this, _) } } @@ -1275,7 +1276,7 @@ private module LocalFlowBigStep { config.isSource(node) or jumpStep(_, node, config) or additionalJumpStep(_, node, config) or - node instanceof ParameterNode or + node instanceof ParameterNodeExt or node instanceof OutNodeExt or store(_, _, node, _) or read(_, _, node) or @@ -1321,21 +1322,21 @@ private module LocalFlowBigStep { Node node1, Node node2, boolean preservesValue, DataFlowType t, Configuration config, LocalCallContext cc ) { - not isUnreachableInCall(node2, cc.(LocalCallContextSpecificCall).getCall()) and + not isUnreachableInCallCached(node2, cc.(LocalCallContextSpecificCall).getCall()) and ( localFlowEntry(node1, pragma[only_bind_into](config)) and ( localFlowStepNodeCand1(node1, node2, config) and preservesValue = true and - t = getNodeType(node1) + t = getNodeDataFlowType(node1) or additionalLocalFlowStepNodeCand2(node1, node2, config) and preservesValue = false and - t = getNodeType(node2) + t = getNodeDataFlowType(node2) ) and node1 != node2 and cc.relevantFor(getNodeEnclosingCallable(node1)) and - not isUnreachableInCall(node1, cc.(LocalCallContextSpecificCall).getCall()) and + not isUnreachableInCallCached(node1, cc.(LocalCallContextSpecificCall).getCall()) and Stage2::revFlow(node2, pragma[only_bind_into](config)) or exists(Node mid | @@ -1350,7 +1351,7 @@ private module LocalFlowBigStep { additionalLocalFlowStepNodeCand2(mid, node2, config) and not mid instanceof FlowCheckNode and preservesValue = false and - t = getNodeType(node2) and + t = getNodeDataFlowType(node2) and Stage2::revFlow(node2, pragma[only_bind_into](config)) ) ) @@ -1384,7 +1385,7 @@ private module Stage3 { private ApApprox getApprox(Ap ap) { result = ap.toBoolNonEmpty() } private ApNil getApNil(Node node) { - PrevStage::revFlow(node, _) and result = TFrontNil(getNodeType(node)) + PrevStage::revFlow(node, _) and result = TFrontNil(getNodeDataFlowType(node)) } bindingset[tc, tail] @@ -1443,7 +1444,9 @@ private module Stage3 { bindingset[node, ap] private predicate filter(Node node, Ap ap) { not ap.isClearedAt(node) and - if node instanceof CastingNode then compatibleTypes(getNodeType(node), ap.getType()) else any() + if node instanceof CastingNode + then compatibleTypes(getNodeDataFlowType(node), ap.getType()) + else any() } bindingset[ap, contentType] @@ -1583,10 +1586,10 @@ private module Stage3 { pragma[nomagic] private predicate fwdFlowIn( - DataFlowCall call, ParameterNode p, Cc outercc, Cc innercc, ApOption argAp, Ap ap, + DataFlowCall call, ParameterNodeExt p, Cc outercc, Cc innercc, ApOption argAp, Ap ap, Configuration config ) { - exists(ArgumentNode arg, boolean allowsFieldFlow | + exists(ArgumentNodeExt arg, boolean allowsFieldFlow | fwdFlow(arg, outercc, argAp, ap, config) and flowIntoCall(call, arg, p, allowsFieldFlow, config) and innercc = getCallContextCall(call, getNodeEnclosingCallable(p), outercc) @@ -1631,7 +1634,7 @@ private module Stage3 { private predicate fwdFlowIsEntered( DataFlowCall call, Cc cc, ApOption argAp, Ap ap, Configuration config ) { - exists(ParameterNode p | + exists(ParameterNodeExt p | fwdFlowIn(call, p, cc, _, argAp, ap, config) and PrevStage::parameterMayFlowThrough(p, _, unbindApa(getApprox(ap)), config) ) @@ -1772,10 +1775,10 @@ private module Stage3 { pragma[nomagic] private predicate revFlowIn( - DataFlowCall call, ArgumentNode arg, boolean toReturn, ApOption returnAp, Ap ap, + DataFlowCall call, ArgumentNodeExt arg, boolean toReturn, ApOption returnAp, Ap ap, Configuration config ) { - exists(ParameterNode p, boolean allowsFieldFlow | + exists(ParameterNodeExt p, boolean allowsFieldFlow | revFlow(p, toReturn, returnAp, ap, config) and flowIntoCall(call, arg, p, allowsFieldFlow, config) | @@ -1785,7 +1788,7 @@ private module Stage3 { pragma[nomagic] private predicate revFlowInToReturn( - DataFlowCall call, ArgumentNode arg, Ap returnAp, Ap ap, Configuration config + DataFlowCall call, ArgumentNodeExt arg, Ap returnAp, Ap ap, Configuration config ) { revFlowIn(call, arg, true, apSome(returnAp), ap, config) } @@ -1838,13 +1841,15 @@ private module Stage3 { pragma[noinline] private predicate parameterFlow( - ParameterNode p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config + ParameterNodeExt p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config ) { revFlow(p, true, apSome(ap0), ap, config) and c = getNodeEnclosingCallable(p) } - predicate parameterMayFlowThrough(ParameterNode p, DataFlowCallable c, Ap ap, Configuration config) { + predicate parameterMayFlowThrough( + ParameterNodeExt p, DataFlowCallable c, Ap ap, Configuration config + ) { exists(ReturnNodeExt ret, Ap ap0, ReturnKindExt kind, int pos | parameterFlow(p, ap, ap0, c, config) and c = getNodeEnclosingCallable(ret) and @@ -2088,7 +2093,7 @@ private module Stage4 { private ApApprox getApprox(Ap ap) { result = ap.getFront() } private ApNil getApNil(Node node) { - PrevStage::revFlow(node, _) and result = TNil(getNodeType(node)) + PrevStage::revFlow(node, _) and result = TNil(getNodeDataFlowType(node)) } bindingset[tc, tail] @@ -2155,7 +2160,7 @@ private module Stage4 { pragma[nomagic] private predicate flowIntoCall( - DataFlowCall call, ArgumentNode node1, ParameterNode node2, boolean allowsFieldFlow, + DataFlowCall call, ArgumentNodeExt node1, ParameterNodeExt node2, boolean allowsFieldFlow, Configuration config ) { flowIntoCallNodeCand2(call, node1, node2, allowsFieldFlow, config) and @@ -2300,10 +2305,10 @@ private module Stage4 { pragma[nomagic] private predicate fwdFlowIn( - DataFlowCall call, ParameterNode p, Cc outercc, Cc innercc, ApOption argAp, Ap ap, + DataFlowCall call, ParameterNodeExt p, Cc outercc, Cc innercc, ApOption argAp, Ap ap, Configuration config ) { - exists(ArgumentNode arg, boolean allowsFieldFlow | + exists(ArgumentNodeExt arg, boolean allowsFieldFlow | fwdFlow(arg, outercc, argAp, ap, config) and flowIntoCall(call, arg, p, allowsFieldFlow, config) and innercc = getCallContextCall(call, getNodeEnclosingCallable(p), outercc) @@ -2348,7 +2353,7 @@ private module Stage4 { private predicate fwdFlowIsEntered( DataFlowCall call, Cc cc, ApOption argAp, Ap ap, Configuration config ) { - exists(ParameterNode p | + exists(ParameterNodeExt p | fwdFlowIn(call, p, cc, _, argAp, ap, config) and PrevStage::parameterMayFlowThrough(p, _, unbindApa(getApprox(ap)), config) ) @@ -2489,10 +2494,10 @@ private module Stage4 { pragma[nomagic] private predicate revFlowIn( - DataFlowCall call, ArgumentNode arg, boolean toReturn, ApOption returnAp, Ap ap, + DataFlowCall call, ArgumentNodeExt arg, boolean toReturn, ApOption returnAp, Ap ap, Configuration config ) { - exists(ParameterNode p, boolean allowsFieldFlow | + exists(ParameterNodeExt p, boolean allowsFieldFlow | revFlow(p, toReturn, returnAp, ap, config) and flowIntoCall(call, arg, p, allowsFieldFlow, config) | @@ -2502,7 +2507,7 @@ private module Stage4 { pragma[nomagic] private predicate revFlowInToReturn( - DataFlowCall call, ArgumentNode arg, Ap returnAp, Ap ap, Configuration config + DataFlowCall call, ArgumentNodeExt arg, Ap returnAp, Ap ap, Configuration config ) { revFlowIn(call, arg, true, apSome(returnAp), ap, config) } @@ -2555,13 +2560,15 @@ private module Stage4 { pragma[noinline] private predicate parameterFlow( - ParameterNode p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config + ParameterNodeExt p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config ) { revFlow(p, true, apSome(ap0), ap, config) and c = getNodeEnclosingCallable(p) } - predicate parameterMayFlowThrough(ParameterNode p, DataFlowCallable c, Ap ap, Configuration config) { + predicate parameterMayFlowThrough( + ParameterNodeExt p, DataFlowCallable c, Ap ap, Configuration config + ) { exists(ReturnNodeExt ret, Ap ap0, ReturnKindExt kind, int pos | parameterFlow(p, ap, ap0, c, config) and c = getNodeEnclosingCallable(ret) and @@ -2606,7 +2613,7 @@ private predicate nodeMayUseSummary(Node n, AccessPathApprox apa, Configuration private newtype TSummaryCtx = TSummaryCtxNone() or - TSummaryCtxSome(ParameterNode p, AccessPath ap) { + TSummaryCtxSome(ParameterNodeExt p, AccessPath ap) { Stage4::parameterMayFlowThrough(p, _, ap.getApprox(), _) } @@ -2627,7 +2634,7 @@ private class SummaryCtxNone extends SummaryCtx, TSummaryCtxNone { /** A summary context from which a flow summary can be generated. */ private class SummaryCtxSome extends SummaryCtx, TSummaryCtxSome { - private ParameterNode p; + private ParameterNodeExt p; private AccessPath ap; SummaryCtxSome() { this = TSummaryCtxSome(p, ap) } @@ -2758,7 +2765,7 @@ private newtype TPathNode = config.isSource(node) and cc instanceof CallContextAny and sc instanceof SummaryCtxNone and - ap = TAccessPathNil(getNodeType(node)) + ap = TAccessPathNil(getNodeDataFlowType(node)) or // ... or a step from an existing PathNode to another node. exists(PathNodeMid mid | @@ -2979,7 +2986,7 @@ class PathNode extends TPathNode { Configuration getConfiguration() { none() } private predicate isHidden() { - nodeIsHidden(this.getNode()) and + hiddenNode(this.getNode()) and not this.isSource() and not this instanceof PathNodeSink } @@ -3148,7 +3155,7 @@ private predicate pathStep(PathNodeMid mid, Node node, CallContext cc, SummaryCt cc instanceof CallContextAny and sc instanceof SummaryCtxNone and mid.getAp() instanceof AccessPathNil and - ap = TAccessPathNil(getNodeType(node)) + ap = TAccessPathNil(getNodeDataFlowType(node)) or exists(TypedContent tc | pathStoreStep(mid, node, ap.pop(tc), tc, cc)) and sc = mid.getSummaryCtx() @@ -3235,7 +3242,7 @@ pragma[noinline] private predicate pathIntoArg( PathNodeMid mid, int i, CallContext cc, DataFlowCall call, AccessPath ap, AccessPathApprox apa ) { - exists(ArgumentNode arg | + exists(ArgumentNodeExt arg | arg = mid.getNode() and cc = mid.getCallContext() and arg.argumentOf(call, i) and @@ -3248,7 +3255,7 @@ pragma[noinline] private predicate parameterCand( DataFlowCallable callable, int i, AccessPathApprox apa, Configuration config ) { - exists(ParameterNode p | + exists(ParameterNodeExt p | Stage4::revFlow(p, _, _, apa, config) and p.isParameterOf(callable, i) ) @@ -3272,7 +3279,7 @@ private predicate pathIntoCallable0( * respectively. */ private predicate pathIntoCallable( - PathNodeMid mid, ParameterNode p, CallContext outercc, CallContextCall innercc, SummaryCtx sc, + PathNodeMid mid, ParameterNodeExt p, CallContext outercc, CallContextCall innercc, SummaryCtx sc, DataFlowCall call ) { exists(int i, DataFlowCallable callable, AccessPath ap | @@ -3568,7 +3575,7 @@ private module FlowExploration { private newtype TSummaryCtx1 = TSummaryCtx1None() or - TSummaryCtx1Param(ParameterNode p) + TSummaryCtx1Param(ParameterNodeExt p) private newtype TSummaryCtx2 = TSummaryCtx2None() or @@ -3591,7 +3598,7 @@ private module FlowExploration { cc instanceof CallContextAny and sc1 = TSummaryCtx1None() and sc2 = TSummaryCtx2None() and - ap = TPartialNil(getNodeType(node)) and + ap = TPartialNil(getNodeDataFlowType(node)) and not fullBarrier(node, config) and exists(config.explorationLimit()) or @@ -3611,7 +3618,7 @@ private module FlowExploration { or exists(PartialPathNodeRev mid | revPartialPathStep(mid, node, sc1, sc2, ap, config) and - not clearsContent(node, ap.getHead()) and + not clearsContentCached(node, ap.getHead()) and not fullBarrier(node, config) and distSink(getNodeEnclosingCallable(node), config) <= config.explorationLimit() ) @@ -3625,9 +3632,9 @@ private module FlowExploration { exists(PartialPathNodeFwd mid | partialPathStep(mid, node, cc, sc1, sc2, ap, config) and not fullBarrier(node, config) and - not clearsContent(node, ap.getHead().getContent()) and + not clearsContentCached(node, ap.getHead().getContent()) and if node instanceof CastingNode - then compatibleTypes(getNodeType(node), ap.getType()) + then compatibleTypes(getNodeDataFlowType(node), ap.getType()) else any() ) } @@ -3783,7 +3790,7 @@ private module FlowExploration { PartialPathNodeFwd mid, Node node, CallContext cc, TSummaryCtx1 sc1, TSummaryCtx2 sc2, PartialAccessPath ap, Configuration config ) { - not isUnreachableInCall(node, cc.(CallContextSpecificCall).getCall()) and + not isUnreachableInCallCached(node, cc.(CallContextSpecificCall).getCall()) and ( localFlowStep(mid.getNode(), node, config) and cc = mid.getCallContext() and @@ -3797,7 +3804,7 @@ private module FlowExploration { sc1 = mid.getSummaryCtx1() and sc2 = mid.getSummaryCtx2() and mid.getAp() instanceof PartialAccessPathNil and - ap = TPartialNil(getNodeType(node)) and + ap = TPartialNil(getNodeDataFlowType(node)) and config = mid.getConfiguration() ) or @@ -3813,7 +3820,7 @@ private module FlowExploration { sc1 = TSummaryCtx1None() and sc2 = TSummaryCtx2None() and mid.getAp() instanceof PartialAccessPathNil and - ap = TPartialNil(getNodeType(node)) and + ap = TPartialNil(getNodeDataFlowType(node)) and config = mid.getConfiguration() or partialPathStoreStep(mid, _, _, node, ap) and @@ -3827,7 +3834,7 @@ private module FlowExploration { sc1 = mid.getSummaryCtx1() and sc2 = mid.getSummaryCtx2() and apConsFwd(ap, tc, ap0, config) and - compatibleTypes(ap.getType(), getNodeType(node)) + compatibleTypes(ap.getType(), getNodeDataFlowType(node)) ) or partialPathIntoCallable(mid, node, _, cc, sc1, sc2, _, ap, config) @@ -3924,7 +3931,7 @@ private module FlowExploration { PartialPathNodeFwd mid, int i, CallContext cc, DataFlowCall call, PartialAccessPath ap, Configuration config ) { - exists(ArgumentNode arg | + exists(ArgumentNodeExt arg | arg = mid.getNode() and cc = mid.getCallContext() and arg.argumentOf(call, i) and @@ -3943,7 +3950,7 @@ private module FlowExploration { } private predicate partialPathIntoCallable( - PartialPathNodeFwd mid, ParameterNode p, CallContext outercc, CallContextCall innercc, + PartialPathNodeFwd mid, ParameterNodeExt p, CallContext outercc, CallContextCall innercc, TSummaryCtx1 sc1, TSummaryCtx2 sc2, DataFlowCall call, PartialAccessPath ap, Configuration config ) { @@ -3980,7 +3987,7 @@ private module FlowExploration { DataFlowCall call, PartialPathNodeFwd mid, ReturnKindExt kind, CallContext cc, PartialAccessPath ap, Configuration config ) { - exists(ParameterNode p, CallContext innercc, TSummaryCtx1 sc1, TSummaryCtx2 sc2 | + exists(ParameterNodeExt p, CallContext innercc, TSummaryCtx1 sc1, TSummaryCtx2 sc2 | partialPathIntoCallable(mid, p, cc, innercc, sc1, sc2, call, _, config) and paramFlowsThroughInPartialPath(kind, innercc, sc1, sc2, ap, config) ) @@ -4037,7 +4044,7 @@ private module FlowExploration { apConsRev(ap, c, ap0, config) ) or - exists(ParameterNode p | + exists(ParameterNodeExt p | mid.getNode() = p and viableParamArg(_, p, node) and sc1 = mid.getSummaryCtx1() and @@ -4115,7 +4122,7 @@ private module FlowExploration { int pos, TRevSummaryCtx1Some sc1, TRevSummaryCtx2Some sc2, RevPartialAccessPath ap, Configuration config ) { - exists(PartialPathNodeRev mid, ParameterNode p | + exists(PartialPathNodeRev mid, ParameterNodeExt p | mid.getNode() = p and p.isParameterOf(_, pos) and sc1 = mid.getSummaryCtx1() and @@ -4138,7 +4145,7 @@ private module FlowExploration { pragma[nomagic] private predicate revPartialPathThroughCallable( - PartialPathNodeRev mid, ArgumentNode node, RevPartialAccessPath ap, Configuration config + PartialPathNodeRev mid, ArgumentNodeExt node, RevPartialAccessPath ap, Configuration config ) { exists(DataFlowCall call, int pos | revPartialPathThroughCallable0(call, mid, pos, ap, config) and diff --git a/python/ql/src/semmle/python/dataflow/new/internal/DataFlowImpl2.qll b/python/ql/src/semmle/python/dataflow/new/internal/DataFlowImpl2.qll index 9498e51e7e6..cf83530bcbd 100644 --- a/python/ql/src/semmle/python/dataflow/new/internal/DataFlowImpl2.qll +++ b/python/ql/src/semmle/python/dataflow/new/internal/DataFlowImpl2.qll @@ -211,10 +211,7 @@ private predicate fullBarrier(Node node, Configuration config) { * Holds if data can flow in one local step from `node1` to `node2`. */ private predicate localFlowStep(Node node1, Node node2, Configuration config) { - ( - simpleLocalFlowStep(node1, node2) or - reverseStepThroughInputOutputAlias(node1, node2) - ) and + simpleLocalFlowStepExt(node1, node2) and not outBarrier(node1, config) and not inBarrier(node2, config) and not fullBarrier(node1, config) and @@ -237,7 +234,7 @@ private predicate additionalLocalFlowStep(Node node1, Node node2, Configuration * Holds if data can flow from `node1` to `node2` in a way that discards call contexts. */ private predicate jumpStep(Node node1, Node node2, Configuration config) { - jumpStep(node1, node2) and + jumpStepCached(node1, node2) and not outBarrier(node1, config) and not inBarrier(node2, config) and not fullBarrier(node1, config) and @@ -388,7 +385,7 @@ private module Stage1 { */ pragma[nomagic] private predicate fwdFlowIsEntered(DataFlowCall call, Cc cc, Configuration config) { - exists(ArgumentNode arg | + exists(ArgumentNodeExt arg | fwdFlow(arg, cc, config) and viableParamArg(call, _, arg) ) @@ -515,7 +512,7 @@ private module Stage1 { pragma[nomagic] predicate viableParamArgNodeCandFwd1( - DataFlowCall call, ParameterNode p, ArgumentNode arg, Configuration config + DataFlowCall call, ParameterNodeExt p, ArgumentNodeExt arg, Configuration config ) { viableParamArg(call, p, arg) and fwdFlow(arg, config) @@ -523,16 +520,16 @@ private module Stage1 { pragma[nomagic] private predicate revFlowIn( - DataFlowCall call, ArgumentNode arg, boolean toReturn, Configuration config + DataFlowCall call, ArgumentNodeExt arg, boolean toReturn, Configuration config ) { - exists(ParameterNode p | + exists(ParameterNodeExt p | revFlow(p, toReturn, config) and viableParamArgNodeCandFwd1(call, p, arg, config) ) } pragma[nomagic] - private predicate revFlowInToReturn(DataFlowCall call, ArgumentNode arg, Configuration config) { + private predicate revFlowInToReturn(DataFlowCall call, ArgumentNodeExt arg, Configuration config) { revFlowIn(call, arg, true, config) } @@ -597,7 +594,9 @@ private module Stage1 { * Holds if flow may enter through `p` and reach a return node making `p` a * candidate for the origin of a summary. */ - predicate parameterMayFlowThrough(ParameterNode p, DataFlowCallable c, Ap ap, Configuration config) { + predicate parameterMayFlowThrough( + ParameterNodeExt p, DataFlowCallable c, Ap ap, Configuration config + ) { exists(ReturnKindExt kind | throughFlowNodeCand(p, config) and returnFlowCallableNodeCand(c, kind, config) and @@ -663,7 +662,7 @@ private predicate flowOutOfCallNodeCand1( pragma[nomagic] private predicate viableParamArgNodeCand1( - DataFlowCall call, ParameterNode p, ArgumentNode arg, Configuration config + DataFlowCall call, ParameterNodeExt p, ArgumentNodeExt arg, Configuration config ) { Stage1::viableParamArgNodeCandFwd1(call, p, arg, config) and Stage1::revFlow(arg, config) @@ -675,7 +674,7 @@ private predicate viableParamArgNodeCand1( */ pragma[nomagic] private predicate flowIntoCallNodeCand1( - DataFlowCall call, ArgumentNode arg, ParameterNode p, Configuration config + DataFlowCall call, ArgumentNodeExt arg, ParameterNodeExt p, Configuration config ) { viableParamArgNodeCand1(call, p, arg, config) and Stage1::revFlow(p, config) and @@ -735,7 +734,7 @@ private predicate flowOutOfCallNodeCand1( */ pragma[nomagic] private predicate flowIntoCallNodeCand1( - DataFlowCall call, ArgumentNode arg, ParameterNode p, boolean allowsFieldFlow, + DataFlowCall call, ArgumentNodeExt arg, ParameterNodeExt p, boolean allowsFieldFlow, Configuration config ) { flowIntoCallNodeCand1(call, arg, p, config) and @@ -944,10 +943,10 @@ private module Stage2 { pragma[nomagic] private predicate fwdFlowIn( - DataFlowCall call, ParameterNode p, Cc outercc, Cc innercc, ApOption argAp, Ap ap, + DataFlowCall call, ParameterNodeExt p, Cc outercc, Cc innercc, ApOption argAp, Ap ap, Configuration config ) { - exists(ArgumentNode arg, boolean allowsFieldFlow | + exists(ArgumentNodeExt arg, boolean allowsFieldFlow | fwdFlow(arg, outercc, argAp, ap, config) and flowIntoCall(call, arg, p, allowsFieldFlow, config) and innercc = getCallContextCall(call, getNodeEnclosingCallable(p), outercc) @@ -992,7 +991,7 @@ private module Stage2 { private predicate fwdFlowIsEntered( DataFlowCall call, Cc cc, ApOption argAp, Ap ap, Configuration config ) { - exists(ParameterNode p | + exists(ParameterNodeExt p | fwdFlowIn(call, p, cc, _, argAp, ap, config) and PrevStage::parameterMayFlowThrough(p, _, getApprox(ap), config) ) @@ -1133,10 +1132,10 @@ private module Stage2 { pragma[nomagic] private predicate revFlowIn( - DataFlowCall call, ArgumentNode arg, boolean toReturn, ApOption returnAp, Ap ap, + DataFlowCall call, ArgumentNodeExt arg, boolean toReturn, ApOption returnAp, Ap ap, Configuration config ) { - exists(ParameterNode p, boolean allowsFieldFlow | + exists(ParameterNodeExt p, boolean allowsFieldFlow | revFlow(p, toReturn, returnAp, ap, config) and flowIntoCall(call, arg, p, allowsFieldFlow, config) | @@ -1146,7 +1145,7 @@ private module Stage2 { pragma[nomagic] private predicate revFlowInToReturn( - DataFlowCall call, ArgumentNode arg, Ap returnAp, Ap ap, Configuration config + DataFlowCall call, ArgumentNodeExt arg, Ap returnAp, Ap ap, Configuration config ) { revFlowIn(call, arg, true, apSome(returnAp), ap, config) } @@ -1199,13 +1198,15 @@ private module Stage2 { pragma[noinline] private predicate parameterFlow( - ParameterNode p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config + ParameterNodeExt p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config ) { revFlow(p, true, apSome(ap0), ap, config) and c = getNodeEnclosingCallable(p) } - predicate parameterMayFlowThrough(ParameterNode p, DataFlowCallable c, Ap ap, Configuration config) { + predicate parameterMayFlowThrough( + ParameterNodeExt p, DataFlowCallable c, Ap ap, Configuration config + ) { exists(ReturnNodeExt ret, Ap ap0, ReturnKindExt kind, int pos | parameterFlow(p, ap, ap0, c, config) and c = getNodeEnclosingCallable(ret) and @@ -1245,7 +1246,7 @@ private predicate flowOutOfCallNodeCand2( pragma[nomagic] private predicate flowIntoCallNodeCand2( - DataFlowCall call, ArgumentNode node1, ParameterNode node2, boolean allowsFieldFlow, + DataFlowCall call, ArgumentNodeExt node1, ParameterNodeExt node2, boolean allowsFieldFlow, Configuration config ) { flowIntoCallNodeCand1(call, node1, node2, allowsFieldFlow, config) and @@ -1260,8 +1261,8 @@ private module LocalFlowBigStep { */ private class FlowCheckNode extends Node { FlowCheckNode() { - this instanceof CastNode or - clearsContent(this, _) + castNode(this) or + clearsContentCached(this, _) } } @@ -1275,7 +1276,7 @@ private module LocalFlowBigStep { config.isSource(node) or jumpStep(_, node, config) or additionalJumpStep(_, node, config) or - node instanceof ParameterNode or + node instanceof ParameterNodeExt or node instanceof OutNodeExt or store(_, _, node, _) or read(_, _, node) or @@ -1321,21 +1322,21 @@ private module LocalFlowBigStep { Node node1, Node node2, boolean preservesValue, DataFlowType t, Configuration config, LocalCallContext cc ) { - not isUnreachableInCall(node2, cc.(LocalCallContextSpecificCall).getCall()) and + not isUnreachableInCallCached(node2, cc.(LocalCallContextSpecificCall).getCall()) and ( localFlowEntry(node1, pragma[only_bind_into](config)) and ( localFlowStepNodeCand1(node1, node2, config) and preservesValue = true and - t = getNodeType(node1) + t = getNodeDataFlowType(node1) or additionalLocalFlowStepNodeCand2(node1, node2, config) and preservesValue = false and - t = getNodeType(node2) + t = getNodeDataFlowType(node2) ) and node1 != node2 and cc.relevantFor(getNodeEnclosingCallable(node1)) and - not isUnreachableInCall(node1, cc.(LocalCallContextSpecificCall).getCall()) and + not isUnreachableInCallCached(node1, cc.(LocalCallContextSpecificCall).getCall()) and Stage2::revFlow(node2, pragma[only_bind_into](config)) or exists(Node mid | @@ -1350,7 +1351,7 @@ private module LocalFlowBigStep { additionalLocalFlowStepNodeCand2(mid, node2, config) and not mid instanceof FlowCheckNode and preservesValue = false and - t = getNodeType(node2) and + t = getNodeDataFlowType(node2) and Stage2::revFlow(node2, pragma[only_bind_into](config)) ) ) @@ -1384,7 +1385,7 @@ private module Stage3 { private ApApprox getApprox(Ap ap) { result = ap.toBoolNonEmpty() } private ApNil getApNil(Node node) { - PrevStage::revFlow(node, _) and result = TFrontNil(getNodeType(node)) + PrevStage::revFlow(node, _) and result = TFrontNil(getNodeDataFlowType(node)) } bindingset[tc, tail] @@ -1443,7 +1444,9 @@ private module Stage3 { bindingset[node, ap] private predicate filter(Node node, Ap ap) { not ap.isClearedAt(node) and - if node instanceof CastingNode then compatibleTypes(getNodeType(node), ap.getType()) else any() + if node instanceof CastingNode + then compatibleTypes(getNodeDataFlowType(node), ap.getType()) + else any() } bindingset[ap, contentType] @@ -1583,10 +1586,10 @@ private module Stage3 { pragma[nomagic] private predicate fwdFlowIn( - DataFlowCall call, ParameterNode p, Cc outercc, Cc innercc, ApOption argAp, Ap ap, + DataFlowCall call, ParameterNodeExt p, Cc outercc, Cc innercc, ApOption argAp, Ap ap, Configuration config ) { - exists(ArgumentNode arg, boolean allowsFieldFlow | + exists(ArgumentNodeExt arg, boolean allowsFieldFlow | fwdFlow(arg, outercc, argAp, ap, config) and flowIntoCall(call, arg, p, allowsFieldFlow, config) and innercc = getCallContextCall(call, getNodeEnclosingCallable(p), outercc) @@ -1631,7 +1634,7 @@ private module Stage3 { private predicate fwdFlowIsEntered( DataFlowCall call, Cc cc, ApOption argAp, Ap ap, Configuration config ) { - exists(ParameterNode p | + exists(ParameterNodeExt p | fwdFlowIn(call, p, cc, _, argAp, ap, config) and PrevStage::parameterMayFlowThrough(p, _, unbindApa(getApprox(ap)), config) ) @@ -1772,10 +1775,10 @@ private module Stage3 { pragma[nomagic] private predicate revFlowIn( - DataFlowCall call, ArgumentNode arg, boolean toReturn, ApOption returnAp, Ap ap, + DataFlowCall call, ArgumentNodeExt arg, boolean toReturn, ApOption returnAp, Ap ap, Configuration config ) { - exists(ParameterNode p, boolean allowsFieldFlow | + exists(ParameterNodeExt p, boolean allowsFieldFlow | revFlow(p, toReturn, returnAp, ap, config) and flowIntoCall(call, arg, p, allowsFieldFlow, config) | @@ -1785,7 +1788,7 @@ private module Stage3 { pragma[nomagic] private predicate revFlowInToReturn( - DataFlowCall call, ArgumentNode arg, Ap returnAp, Ap ap, Configuration config + DataFlowCall call, ArgumentNodeExt arg, Ap returnAp, Ap ap, Configuration config ) { revFlowIn(call, arg, true, apSome(returnAp), ap, config) } @@ -1838,13 +1841,15 @@ private module Stage3 { pragma[noinline] private predicate parameterFlow( - ParameterNode p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config + ParameterNodeExt p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config ) { revFlow(p, true, apSome(ap0), ap, config) and c = getNodeEnclosingCallable(p) } - predicate parameterMayFlowThrough(ParameterNode p, DataFlowCallable c, Ap ap, Configuration config) { + predicate parameterMayFlowThrough( + ParameterNodeExt p, DataFlowCallable c, Ap ap, Configuration config + ) { exists(ReturnNodeExt ret, Ap ap0, ReturnKindExt kind, int pos | parameterFlow(p, ap, ap0, c, config) and c = getNodeEnclosingCallable(ret) and @@ -2088,7 +2093,7 @@ private module Stage4 { private ApApprox getApprox(Ap ap) { result = ap.getFront() } private ApNil getApNil(Node node) { - PrevStage::revFlow(node, _) and result = TNil(getNodeType(node)) + PrevStage::revFlow(node, _) and result = TNil(getNodeDataFlowType(node)) } bindingset[tc, tail] @@ -2155,7 +2160,7 @@ private module Stage4 { pragma[nomagic] private predicate flowIntoCall( - DataFlowCall call, ArgumentNode node1, ParameterNode node2, boolean allowsFieldFlow, + DataFlowCall call, ArgumentNodeExt node1, ParameterNodeExt node2, boolean allowsFieldFlow, Configuration config ) { flowIntoCallNodeCand2(call, node1, node2, allowsFieldFlow, config) and @@ -2300,10 +2305,10 @@ private module Stage4 { pragma[nomagic] private predicate fwdFlowIn( - DataFlowCall call, ParameterNode p, Cc outercc, Cc innercc, ApOption argAp, Ap ap, + DataFlowCall call, ParameterNodeExt p, Cc outercc, Cc innercc, ApOption argAp, Ap ap, Configuration config ) { - exists(ArgumentNode arg, boolean allowsFieldFlow | + exists(ArgumentNodeExt arg, boolean allowsFieldFlow | fwdFlow(arg, outercc, argAp, ap, config) and flowIntoCall(call, arg, p, allowsFieldFlow, config) and innercc = getCallContextCall(call, getNodeEnclosingCallable(p), outercc) @@ -2348,7 +2353,7 @@ private module Stage4 { private predicate fwdFlowIsEntered( DataFlowCall call, Cc cc, ApOption argAp, Ap ap, Configuration config ) { - exists(ParameterNode p | + exists(ParameterNodeExt p | fwdFlowIn(call, p, cc, _, argAp, ap, config) and PrevStage::parameterMayFlowThrough(p, _, unbindApa(getApprox(ap)), config) ) @@ -2489,10 +2494,10 @@ private module Stage4 { pragma[nomagic] private predicate revFlowIn( - DataFlowCall call, ArgumentNode arg, boolean toReturn, ApOption returnAp, Ap ap, + DataFlowCall call, ArgumentNodeExt arg, boolean toReturn, ApOption returnAp, Ap ap, Configuration config ) { - exists(ParameterNode p, boolean allowsFieldFlow | + exists(ParameterNodeExt p, boolean allowsFieldFlow | revFlow(p, toReturn, returnAp, ap, config) and flowIntoCall(call, arg, p, allowsFieldFlow, config) | @@ -2502,7 +2507,7 @@ private module Stage4 { pragma[nomagic] private predicate revFlowInToReturn( - DataFlowCall call, ArgumentNode arg, Ap returnAp, Ap ap, Configuration config + DataFlowCall call, ArgumentNodeExt arg, Ap returnAp, Ap ap, Configuration config ) { revFlowIn(call, arg, true, apSome(returnAp), ap, config) } @@ -2555,13 +2560,15 @@ private module Stage4 { pragma[noinline] private predicate parameterFlow( - ParameterNode p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config + ParameterNodeExt p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config ) { revFlow(p, true, apSome(ap0), ap, config) and c = getNodeEnclosingCallable(p) } - predicate parameterMayFlowThrough(ParameterNode p, DataFlowCallable c, Ap ap, Configuration config) { + predicate parameterMayFlowThrough( + ParameterNodeExt p, DataFlowCallable c, Ap ap, Configuration config + ) { exists(ReturnNodeExt ret, Ap ap0, ReturnKindExt kind, int pos | parameterFlow(p, ap, ap0, c, config) and c = getNodeEnclosingCallable(ret) and @@ -2606,7 +2613,7 @@ private predicate nodeMayUseSummary(Node n, AccessPathApprox apa, Configuration private newtype TSummaryCtx = TSummaryCtxNone() or - TSummaryCtxSome(ParameterNode p, AccessPath ap) { + TSummaryCtxSome(ParameterNodeExt p, AccessPath ap) { Stage4::parameterMayFlowThrough(p, _, ap.getApprox(), _) } @@ -2627,7 +2634,7 @@ private class SummaryCtxNone extends SummaryCtx, TSummaryCtxNone { /** A summary context from which a flow summary can be generated. */ private class SummaryCtxSome extends SummaryCtx, TSummaryCtxSome { - private ParameterNode p; + private ParameterNodeExt p; private AccessPath ap; SummaryCtxSome() { this = TSummaryCtxSome(p, ap) } @@ -2758,7 +2765,7 @@ private newtype TPathNode = config.isSource(node) and cc instanceof CallContextAny and sc instanceof SummaryCtxNone and - ap = TAccessPathNil(getNodeType(node)) + ap = TAccessPathNil(getNodeDataFlowType(node)) or // ... or a step from an existing PathNode to another node. exists(PathNodeMid mid | @@ -2979,7 +2986,7 @@ class PathNode extends TPathNode { Configuration getConfiguration() { none() } private predicate isHidden() { - nodeIsHidden(this.getNode()) and + hiddenNode(this.getNode()) and not this.isSource() and not this instanceof PathNodeSink } @@ -3148,7 +3155,7 @@ private predicate pathStep(PathNodeMid mid, Node node, CallContext cc, SummaryCt cc instanceof CallContextAny and sc instanceof SummaryCtxNone and mid.getAp() instanceof AccessPathNil and - ap = TAccessPathNil(getNodeType(node)) + ap = TAccessPathNil(getNodeDataFlowType(node)) or exists(TypedContent tc | pathStoreStep(mid, node, ap.pop(tc), tc, cc)) and sc = mid.getSummaryCtx() @@ -3235,7 +3242,7 @@ pragma[noinline] private predicate pathIntoArg( PathNodeMid mid, int i, CallContext cc, DataFlowCall call, AccessPath ap, AccessPathApprox apa ) { - exists(ArgumentNode arg | + exists(ArgumentNodeExt arg | arg = mid.getNode() and cc = mid.getCallContext() and arg.argumentOf(call, i) and @@ -3248,7 +3255,7 @@ pragma[noinline] private predicate parameterCand( DataFlowCallable callable, int i, AccessPathApprox apa, Configuration config ) { - exists(ParameterNode p | + exists(ParameterNodeExt p | Stage4::revFlow(p, _, _, apa, config) and p.isParameterOf(callable, i) ) @@ -3272,7 +3279,7 @@ private predicate pathIntoCallable0( * respectively. */ private predicate pathIntoCallable( - PathNodeMid mid, ParameterNode p, CallContext outercc, CallContextCall innercc, SummaryCtx sc, + PathNodeMid mid, ParameterNodeExt p, CallContext outercc, CallContextCall innercc, SummaryCtx sc, DataFlowCall call ) { exists(int i, DataFlowCallable callable, AccessPath ap | @@ -3568,7 +3575,7 @@ private module FlowExploration { private newtype TSummaryCtx1 = TSummaryCtx1None() or - TSummaryCtx1Param(ParameterNode p) + TSummaryCtx1Param(ParameterNodeExt p) private newtype TSummaryCtx2 = TSummaryCtx2None() or @@ -3591,7 +3598,7 @@ private module FlowExploration { cc instanceof CallContextAny and sc1 = TSummaryCtx1None() and sc2 = TSummaryCtx2None() and - ap = TPartialNil(getNodeType(node)) and + ap = TPartialNil(getNodeDataFlowType(node)) and not fullBarrier(node, config) and exists(config.explorationLimit()) or @@ -3611,7 +3618,7 @@ private module FlowExploration { or exists(PartialPathNodeRev mid | revPartialPathStep(mid, node, sc1, sc2, ap, config) and - not clearsContent(node, ap.getHead()) and + not clearsContentCached(node, ap.getHead()) and not fullBarrier(node, config) and distSink(getNodeEnclosingCallable(node), config) <= config.explorationLimit() ) @@ -3625,9 +3632,9 @@ private module FlowExploration { exists(PartialPathNodeFwd mid | partialPathStep(mid, node, cc, sc1, sc2, ap, config) and not fullBarrier(node, config) and - not clearsContent(node, ap.getHead().getContent()) and + not clearsContentCached(node, ap.getHead().getContent()) and if node instanceof CastingNode - then compatibleTypes(getNodeType(node), ap.getType()) + then compatibleTypes(getNodeDataFlowType(node), ap.getType()) else any() ) } @@ -3783,7 +3790,7 @@ private module FlowExploration { PartialPathNodeFwd mid, Node node, CallContext cc, TSummaryCtx1 sc1, TSummaryCtx2 sc2, PartialAccessPath ap, Configuration config ) { - not isUnreachableInCall(node, cc.(CallContextSpecificCall).getCall()) and + not isUnreachableInCallCached(node, cc.(CallContextSpecificCall).getCall()) and ( localFlowStep(mid.getNode(), node, config) and cc = mid.getCallContext() and @@ -3797,7 +3804,7 @@ private module FlowExploration { sc1 = mid.getSummaryCtx1() and sc2 = mid.getSummaryCtx2() and mid.getAp() instanceof PartialAccessPathNil and - ap = TPartialNil(getNodeType(node)) and + ap = TPartialNil(getNodeDataFlowType(node)) and config = mid.getConfiguration() ) or @@ -3813,7 +3820,7 @@ private module FlowExploration { sc1 = TSummaryCtx1None() and sc2 = TSummaryCtx2None() and mid.getAp() instanceof PartialAccessPathNil and - ap = TPartialNil(getNodeType(node)) and + ap = TPartialNil(getNodeDataFlowType(node)) and config = mid.getConfiguration() or partialPathStoreStep(mid, _, _, node, ap) and @@ -3827,7 +3834,7 @@ private module FlowExploration { sc1 = mid.getSummaryCtx1() and sc2 = mid.getSummaryCtx2() and apConsFwd(ap, tc, ap0, config) and - compatibleTypes(ap.getType(), getNodeType(node)) + compatibleTypes(ap.getType(), getNodeDataFlowType(node)) ) or partialPathIntoCallable(mid, node, _, cc, sc1, sc2, _, ap, config) @@ -3924,7 +3931,7 @@ private module FlowExploration { PartialPathNodeFwd mid, int i, CallContext cc, DataFlowCall call, PartialAccessPath ap, Configuration config ) { - exists(ArgumentNode arg | + exists(ArgumentNodeExt arg | arg = mid.getNode() and cc = mid.getCallContext() and arg.argumentOf(call, i) and @@ -3943,7 +3950,7 @@ private module FlowExploration { } private predicate partialPathIntoCallable( - PartialPathNodeFwd mid, ParameterNode p, CallContext outercc, CallContextCall innercc, + PartialPathNodeFwd mid, ParameterNodeExt p, CallContext outercc, CallContextCall innercc, TSummaryCtx1 sc1, TSummaryCtx2 sc2, DataFlowCall call, PartialAccessPath ap, Configuration config ) { @@ -3980,7 +3987,7 @@ private module FlowExploration { DataFlowCall call, PartialPathNodeFwd mid, ReturnKindExt kind, CallContext cc, PartialAccessPath ap, Configuration config ) { - exists(ParameterNode p, CallContext innercc, TSummaryCtx1 sc1, TSummaryCtx2 sc2 | + exists(ParameterNodeExt p, CallContext innercc, TSummaryCtx1 sc1, TSummaryCtx2 sc2 | partialPathIntoCallable(mid, p, cc, innercc, sc1, sc2, call, _, config) and paramFlowsThroughInPartialPath(kind, innercc, sc1, sc2, ap, config) ) @@ -4037,7 +4044,7 @@ private module FlowExploration { apConsRev(ap, c, ap0, config) ) or - exists(ParameterNode p | + exists(ParameterNodeExt p | mid.getNode() = p and viableParamArg(_, p, node) and sc1 = mid.getSummaryCtx1() and @@ -4115,7 +4122,7 @@ private module FlowExploration { int pos, TRevSummaryCtx1Some sc1, TRevSummaryCtx2Some sc2, RevPartialAccessPath ap, Configuration config ) { - exists(PartialPathNodeRev mid, ParameterNode p | + exists(PartialPathNodeRev mid, ParameterNodeExt p | mid.getNode() = p and p.isParameterOf(_, pos) and sc1 = mid.getSummaryCtx1() and @@ -4138,7 +4145,7 @@ private module FlowExploration { pragma[nomagic] private predicate revPartialPathThroughCallable( - PartialPathNodeRev mid, ArgumentNode node, RevPartialAccessPath ap, Configuration config + PartialPathNodeRev mid, ArgumentNodeExt node, RevPartialAccessPath ap, Configuration config ) { exists(DataFlowCall call, int pos | revPartialPathThroughCallable0(call, mid, pos, ap, config) and diff --git a/python/ql/src/semmle/python/dataflow/new/internal/DataFlowImpl3.qll b/python/ql/src/semmle/python/dataflow/new/internal/DataFlowImpl3.qll index 9498e51e7e6..cf83530bcbd 100644 --- a/python/ql/src/semmle/python/dataflow/new/internal/DataFlowImpl3.qll +++ b/python/ql/src/semmle/python/dataflow/new/internal/DataFlowImpl3.qll @@ -211,10 +211,7 @@ private predicate fullBarrier(Node node, Configuration config) { * Holds if data can flow in one local step from `node1` to `node2`. */ private predicate localFlowStep(Node node1, Node node2, Configuration config) { - ( - simpleLocalFlowStep(node1, node2) or - reverseStepThroughInputOutputAlias(node1, node2) - ) and + simpleLocalFlowStepExt(node1, node2) and not outBarrier(node1, config) and not inBarrier(node2, config) and not fullBarrier(node1, config) and @@ -237,7 +234,7 @@ private predicate additionalLocalFlowStep(Node node1, Node node2, Configuration * Holds if data can flow from `node1` to `node2` in a way that discards call contexts. */ private predicate jumpStep(Node node1, Node node2, Configuration config) { - jumpStep(node1, node2) and + jumpStepCached(node1, node2) and not outBarrier(node1, config) and not inBarrier(node2, config) and not fullBarrier(node1, config) and @@ -388,7 +385,7 @@ private module Stage1 { */ pragma[nomagic] private predicate fwdFlowIsEntered(DataFlowCall call, Cc cc, Configuration config) { - exists(ArgumentNode arg | + exists(ArgumentNodeExt arg | fwdFlow(arg, cc, config) and viableParamArg(call, _, arg) ) @@ -515,7 +512,7 @@ private module Stage1 { pragma[nomagic] predicate viableParamArgNodeCandFwd1( - DataFlowCall call, ParameterNode p, ArgumentNode arg, Configuration config + DataFlowCall call, ParameterNodeExt p, ArgumentNodeExt arg, Configuration config ) { viableParamArg(call, p, arg) and fwdFlow(arg, config) @@ -523,16 +520,16 @@ private module Stage1 { pragma[nomagic] private predicate revFlowIn( - DataFlowCall call, ArgumentNode arg, boolean toReturn, Configuration config + DataFlowCall call, ArgumentNodeExt arg, boolean toReturn, Configuration config ) { - exists(ParameterNode p | + exists(ParameterNodeExt p | revFlow(p, toReturn, config) and viableParamArgNodeCandFwd1(call, p, arg, config) ) } pragma[nomagic] - private predicate revFlowInToReturn(DataFlowCall call, ArgumentNode arg, Configuration config) { + private predicate revFlowInToReturn(DataFlowCall call, ArgumentNodeExt arg, Configuration config) { revFlowIn(call, arg, true, config) } @@ -597,7 +594,9 @@ private module Stage1 { * Holds if flow may enter through `p` and reach a return node making `p` a * candidate for the origin of a summary. */ - predicate parameterMayFlowThrough(ParameterNode p, DataFlowCallable c, Ap ap, Configuration config) { + predicate parameterMayFlowThrough( + ParameterNodeExt p, DataFlowCallable c, Ap ap, Configuration config + ) { exists(ReturnKindExt kind | throughFlowNodeCand(p, config) and returnFlowCallableNodeCand(c, kind, config) and @@ -663,7 +662,7 @@ private predicate flowOutOfCallNodeCand1( pragma[nomagic] private predicate viableParamArgNodeCand1( - DataFlowCall call, ParameterNode p, ArgumentNode arg, Configuration config + DataFlowCall call, ParameterNodeExt p, ArgumentNodeExt arg, Configuration config ) { Stage1::viableParamArgNodeCandFwd1(call, p, arg, config) and Stage1::revFlow(arg, config) @@ -675,7 +674,7 @@ private predicate viableParamArgNodeCand1( */ pragma[nomagic] private predicate flowIntoCallNodeCand1( - DataFlowCall call, ArgumentNode arg, ParameterNode p, Configuration config + DataFlowCall call, ArgumentNodeExt arg, ParameterNodeExt p, Configuration config ) { viableParamArgNodeCand1(call, p, arg, config) and Stage1::revFlow(p, config) and @@ -735,7 +734,7 @@ private predicate flowOutOfCallNodeCand1( */ pragma[nomagic] private predicate flowIntoCallNodeCand1( - DataFlowCall call, ArgumentNode arg, ParameterNode p, boolean allowsFieldFlow, + DataFlowCall call, ArgumentNodeExt arg, ParameterNodeExt p, boolean allowsFieldFlow, Configuration config ) { flowIntoCallNodeCand1(call, arg, p, config) and @@ -944,10 +943,10 @@ private module Stage2 { pragma[nomagic] private predicate fwdFlowIn( - DataFlowCall call, ParameterNode p, Cc outercc, Cc innercc, ApOption argAp, Ap ap, + DataFlowCall call, ParameterNodeExt p, Cc outercc, Cc innercc, ApOption argAp, Ap ap, Configuration config ) { - exists(ArgumentNode arg, boolean allowsFieldFlow | + exists(ArgumentNodeExt arg, boolean allowsFieldFlow | fwdFlow(arg, outercc, argAp, ap, config) and flowIntoCall(call, arg, p, allowsFieldFlow, config) and innercc = getCallContextCall(call, getNodeEnclosingCallable(p), outercc) @@ -992,7 +991,7 @@ private module Stage2 { private predicate fwdFlowIsEntered( DataFlowCall call, Cc cc, ApOption argAp, Ap ap, Configuration config ) { - exists(ParameterNode p | + exists(ParameterNodeExt p | fwdFlowIn(call, p, cc, _, argAp, ap, config) and PrevStage::parameterMayFlowThrough(p, _, getApprox(ap), config) ) @@ -1133,10 +1132,10 @@ private module Stage2 { pragma[nomagic] private predicate revFlowIn( - DataFlowCall call, ArgumentNode arg, boolean toReturn, ApOption returnAp, Ap ap, + DataFlowCall call, ArgumentNodeExt arg, boolean toReturn, ApOption returnAp, Ap ap, Configuration config ) { - exists(ParameterNode p, boolean allowsFieldFlow | + exists(ParameterNodeExt p, boolean allowsFieldFlow | revFlow(p, toReturn, returnAp, ap, config) and flowIntoCall(call, arg, p, allowsFieldFlow, config) | @@ -1146,7 +1145,7 @@ private module Stage2 { pragma[nomagic] private predicate revFlowInToReturn( - DataFlowCall call, ArgumentNode arg, Ap returnAp, Ap ap, Configuration config + DataFlowCall call, ArgumentNodeExt arg, Ap returnAp, Ap ap, Configuration config ) { revFlowIn(call, arg, true, apSome(returnAp), ap, config) } @@ -1199,13 +1198,15 @@ private module Stage2 { pragma[noinline] private predicate parameterFlow( - ParameterNode p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config + ParameterNodeExt p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config ) { revFlow(p, true, apSome(ap0), ap, config) and c = getNodeEnclosingCallable(p) } - predicate parameterMayFlowThrough(ParameterNode p, DataFlowCallable c, Ap ap, Configuration config) { + predicate parameterMayFlowThrough( + ParameterNodeExt p, DataFlowCallable c, Ap ap, Configuration config + ) { exists(ReturnNodeExt ret, Ap ap0, ReturnKindExt kind, int pos | parameterFlow(p, ap, ap0, c, config) and c = getNodeEnclosingCallable(ret) and @@ -1245,7 +1246,7 @@ private predicate flowOutOfCallNodeCand2( pragma[nomagic] private predicate flowIntoCallNodeCand2( - DataFlowCall call, ArgumentNode node1, ParameterNode node2, boolean allowsFieldFlow, + DataFlowCall call, ArgumentNodeExt node1, ParameterNodeExt node2, boolean allowsFieldFlow, Configuration config ) { flowIntoCallNodeCand1(call, node1, node2, allowsFieldFlow, config) and @@ -1260,8 +1261,8 @@ private module LocalFlowBigStep { */ private class FlowCheckNode extends Node { FlowCheckNode() { - this instanceof CastNode or - clearsContent(this, _) + castNode(this) or + clearsContentCached(this, _) } } @@ -1275,7 +1276,7 @@ private module LocalFlowBigStep { config.isSource(node) or jumpStep(_, node, config) or additionalJumpStep(_, node, config) or - node instanceof ParameterNode or + node instanceof ParameterNodeExt or node instanceof OutNodeExt or store(_, _, node, _) or read(_, _, node) or @@ -1321,21 +1322,21 @@ private module LocalFlowBigStep { Node node1, Node node2, boolean preservesValue, DataFlowType t, Configuration config, LocalCallContext cc ) { - not isUnreachableInCall(node2, cc.(LocalCallContextSpecificCall).getCall()) and + not isUnreachableInCallCached(node2, cc.(LocalCallContextSpecificCall).getCall()) and ( localFlowEntry(node1, pragma[only_bind_into](config)) and ( localFlowStepNodeCand1(node1, node2, config) and preservesValue = true and - t = getNodeType(node1) + t = getNodeDataFlowType(node1) or additionalLocalFlowStepNodeCand2(node1, node2, config) and preservesValue = false and - t = getNodeType(node2) + t = getNodeDataFlowType(node2) ) and node1 != node2 and cc.relevantFor(getNodeEnclosingCallable(node1)) and - not isUnreachableInCall(node1, cc.(LocalCallContextSpecificCall).getCall()) and + not isUnreachableInCallCached(node1, cc.(LocalCallContextSpecificCall).getCall()) and Stage2::revFlow(node2, pragma[only_bind_into](config)) or exists(Node mid | @@ -1350,7 +1351,7 @@ private module LocalFlowBigStep { additionalLocalFlowStepNodeCand2(mid, node2, config) and not mid instanceof FlowCheckNode and preservesValue = false and - t = getNodeType(node2) and + t = getNodeDataFlowType(node2) and Stage2::revFlow(node2, pragma[only_bind_into](config)) ) ) @@ -1384,7 +1385,7 @@ private module Stage3 { private ApApprox getApprox(Ap ap) { result = ap.toBoolNonEmpty() } private ApNil getApNil(Node node) { - PrevStage::revFlow(node, _) and result = TFrontNil(getNodeType(node)) + PrevStage::revFlow(node, _) and result = TFrontNil(getNodeDataFlowType(node)) } bindingset[tc, tail] @@ -1443,7 +1444,9 @@ private module Stage3 { bindingset[node, ap] private predicate filter(Node node, Ap ap) { not ap.isClearedAt(node) and - if node instanceof CastingNode then compatibleTypes(getNodeType(node), ap.getType()) else any() + if node instanceof CastingNode + then compatibleTypes(getNodeDataFlowType(node), ap.getType()) + else any() } bindingset[ap, contentType] @@ -1583,10 +1586,10 @@ private module Stage3 { pragma[nomagic] private predicate fwdFlowIn( - DataFlowCall call, ParameterNode p, Cc outercc, Cc innercc, ApOption argAp, Ap ap, + DataFlowCall call, ParameterNodeExt p, Cc outercc, Cc innercc, ApOption argAp, Ap ap, Configuration config ) { - exists(ArgumentNode arg, boolean allowsFieldFlow | + exists(ArgumentNodeExt arg, boolean allowsFieldFlow | fwdFlow(arg, outercc, argAp, ap, config) and flowIntoCall(call, arg, p, allowsFieldFlow, config) and innercc = getCallContextCall(call, getNodeEnclosingCallable(p), outercc) @@ -1631,7 +1634,7 @@ private module Stage3 { private predicate fwdFlowIsEntered( DataFlowCall call, Cc cc, ApOption argAp, Ap ap, Configuration config ) { - exists(ParameterNode p | + exists(ParameterNodeExt p | fwdFlowIn(call, p, cc, _, argAp, ap, config) and PrevStage::parameterMayFlowThrough(p, _, unbindApa(getApprox(ap)), config) ) @@ -1772,10 +1775,10 @@ private module Stage3 { pragma[nomagic] private predicate revFlowIn( - DataFlowCall call, ArgumentNode arg, boolean toReturn, ApOption returnAp, Ap ap, + DataFlowCall call, ArgumentNodeExt arg, boolean toReturn, ApOption returnAp, Ap ap, Configuration config ) { - exists(ParameterNode p, boolean allowsFieldFlow | + exists(ParameterNodeExt p, boolean allowsFieldFlow | revFlow(p, toReturn, returnAp, ap, config) and flowIntoCall(call, arg, p, allowsFieldFlow, config) | @@ -1785,7 +1788,7 @@ private module Stage3 { pragma[nomagic] private predicate revFlowInToReturn( - DataFlowCall call, ArgumentNode arg, Ap returnAp, Ap ap, Configuration config + DataFlowCall call, ArgumentNodeExt arg, Ap returnAp, Ap ap, Configuration config ) { revFlowIn(call, arg, true, apSome(returnAp), ap, config) } @@ -1838,13 +1841,15 @@ private module Stage3 { pragma[noinline] private predicate parameterFlow( - ParameterNode p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config + ParameterNodeExt p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config ) { revFlow(p, true, apSome(ap0), ap, config) and c = getNodeEnclosingCallable(p) } - predicate parameterMayFlowThrough(ParameterNode p, DataFlowCallable c, Ap ap, Configuration config) { + predicate parameterMayFlowThrough( + ParameterNodeExt p, DataFlowCallable c, Ap ap, Configuration config + ) { exists(ReturnNodeExt ret, Ap ap0, ReturnKindExt kind, int pos | parameterFlow(p, ap, ap0, c, config) and c = getNodeEnclosingCallable(ret) and @@ -2088,7 +2093,7 @@ private module Stage4 { private ApApprox getApprox(Ap ap) { result = ap.getFront() } private ApNil getApNil(Node node) { - PrevStage::revFlow(node, _) and result = TNil(getNodeType(node)) + PrevStage::revFlow(node, _) and result = TNil(getNodeDataFlowType(node)) } bindingset[tc, tail] @@ -2155,7 +2160,7 @@ private module Stage4 { pragma[nomagic] private predicate flowIntoCall( - DataFlowCall call, ArgumentNode node1, ParameterNode node2, boolean allowsFieldFlow, + DataFlowCall call, ArgumentNodeExt node1, ParameterNodeExt node2, boolean allowsFieldFlow, Configuration config ) { flowIntoCallNodeCand2(call, node1, node2, allowsFieldFlow, config) and @@ -2300,10 +2305,10 @@ private module Stage4 { pragma[nomagic] private predicate fwdFlowIn( - DataFlowCall call, ParameterNode p, Cc outercc, Cc innercc, ApOption argAp, Ap ap, + DataFlowCall call, ParameterNodeExt p, Cc outercc, Cc innercc, ApOption argAp, Ap ap, Configuration config ) { - exists(ArgumentNode arg, boolean allowsFieldFlow | + exists(ArgumentNodeExt arg, boolean allowsFieldFlow | fwdFlow(arg, outercc, argAp, ap, config) and flowIntoCall(call, arg, p, allowsFieldFlow, config) and innercc = getCallContextCall(call, getNodeEnclosingCallable(p), outercc) @@ -2348,7 +2353,7 @@ private module Stage4 { private predicate fwdFlowIsEntered( DataFlowCall call, Cc cc, ApOption argAp, Ap ap, Configuration config ) { - exists(ParameterNode p | + exists(ParameterNodeExt p | fwdFlowIn(call, p, cc, _, argAp, ap, config) and PrevStage::parameterMayFlowThrough(p, _, unbindApa(getApprox(ap)), config) ) @@ -2489,10 +2494,10 @@ private module Stage4 { pragma[nomagic] private predicate revFlowIn( - DataFlowCall call, ArgumentNode arg, boolean toReturn, ApOption returnAp, Ap ap, + DataFlowCall call, ArgumentNodeExt arg, boolean toReturn, ApOption returnAp, Ap ap, Configuration config ) { - exists(ParameterNode p, boolean allowsFieldFlow | + exists(ParameterNodeExt p, boolean allowsFieldFlow | revFlow(p, toReturn, returnAp, ap, config) and flowIntoCall(call, arg, p, allowsFieldFlow, config) | @@ -2502,7 +2507,7 @@ private module Stage4 { pragma[nomagic] private predicate revFlowInToReturn( - DataFlowCall call, ArgumentNode arg, Ap returnAp, Ap ap, Configuration config + DataFlowCall call, ArgumentNodeExt arg, Ap returnAp, Ap ap, Configuration config ) { revFlowIn(call, arg, true, apSome(returnAp), ap, config) } @@ -2555,13 +2560,15 @@ private module Stage4 { pragma[noinline] private predicate parameterFlow( - ParameterNode p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config + ParameterNodeExt p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config ) { revFlow(p, true, apSome(ap0), ap, config) and c = getNodeEnclosingCallable(p) } - predicate parameterMayFlowThrough(ParameterNode p, DataFlowCallable c, Ap ap, Configuration config) { + predicate parameterMayFlowThrough( + ParameterNodeExt p, DataFlowCallable c, Ap ap, Configuration config + ) { exists(ReturnNodeExt ret, Ap ap0, ReturnKindExt kind, int pos | parameterFlow(p, ap, ap0, c, config) and c = getNodeEnclosingCallable(ret) and @@ -2606,7 +2613,7 @@ private predicate nodeMayUseSummary(Node n, AccessPathApprox apa, Configuration private newtype TSummaryCtx = TSummaryCtxNone() or - TSummaryCtxSome(ParameterNode p, AccessPath ap) { + TSummaryCtxSome(ParameterNodeExt p, AccessPath ap) { Stage4::parameterMayFlowThrough(p, _, ap.getApprox(), _) } @@ -2627,7 +2634,7 @@ private class SummaryCtxNone extends SummaryCtx, TSummaryCtxNone { /** A summary context from which a flow summary can be generated. */ private class SummaryCtxSome extends SummaryCtx, TSummaryCtxSome { - private ParameterNode p; + private ParameterNodeExt p; private AccessPath ap; SummaryCtxSome() { this = TSummaryCtxSome(p, ap) } @@ -2758,7 +2765,7 @@ private newtype TPathNode = config.isSource(node) and cc instanceof CallContextAny and sc instanceof SummaryCtxNone and - ap = TAccessPathNil(getNodeType(node)) + ap = TAccessPathNil(getNodeDataFlowType(node)) or // ... or a step from an existing PathNode to another node. exists(PathNodeMid mid | @@ -2979,7 +2986,7 @@ class PathNode extends TPathNode { Configuration getConfiguration() { none() } private predicate isHidden() { - nodeIsHidden(this.getNode()) and + hiddenNode(this.getNode()) and not this.isSource() and not this instanceof PathNodeSink } @@ -3148,7 +3155,7 @@ private predicate pathStep(PathNodeMid mid, Node node, CallContext cc, SummaryCt cc instanceof CallContextAny and sc instanceof SummaryCtxNone and mid.getAp() instanceof AccessPathNil and - ap = TAccessPathNil(getNodeType(node)) + ap = TAccessPathNil(getNodeDataFlowType(node)) or exists(TypedContent tc | pathStoreStep(mid, node, ap.pop(tc), tc, cc)) and sc = mid.getSummaryCtx() @@ -3235,7 +3242,7 @@ pragma[noinline] private predicate pathIntoArg( PathNodeMid mid, int i, CallContext cc, DataFlowCall call, AccessPath ap, AccessPathApprox apa ) { - exists(ArgumentNode arg | + exists(ArgumentNodeExt arg | arg = mid.getNode() and cc = mid.getCallContext() and arg.argumentOf(call, i) and @@ -3248,7 +3255,7 @@ pragma[noinline] private predicate parameterCand( DataFlowCallable callable, int i, AccessPathApprox apa, Configuration config ) { - exists(ParameterNode p | + exists(ParameterNodeExt p | Stage4::revFlow(p, _, _, apa, config) and p.isParameterOf(callable, i) ) @@ -3272,7 +3279,7 @@ private predicate pathIntoCallable0( * respectively. */ private predicate pathIntoCallable( - PathNodeMid mid, ParameterNode p, CallContext outercc, CallContextCall innercc, SummaryCtx sc, + PathNodeMid mid, ParameterNodeExt p, CallContext outercc, CallContextCall innercc, SummaryCtx sc, DataFlowCall call ) { exists(int i, DataFlowCallable callable, AccessPath ap | @@ -3568,7 +3575,7 @@ private module FlowExploration { private newtype TSummaryCtx1 = TSummaryCtx1None() or - TSummaryCtx1Param(ParameterNode p) + TSummaryCtx1Param(ParameterNodeExt p) private newtype TSummaryCtx2 = TSummaryCtx2None() or @@ -3591,7 +3598,7 @@ private module FlowExploration { cc instanceof CallContextAny and sc1 = TSummaryCtx1None() and sc2 = TSummaryCtx2None() and - ap = TPartialNil(getNodeType(node)) and + ap = TPartialNil(getNodeDataFlowType(node)) and not fullBarrier(node, config) and exists(config.explorationLimit()) or @@ -3611,7 +3618,7 @@ private module FlowExploration { or exists(PartialPathNodeRev mid | revPartialPathStep(mid, node, sc1, sc2, ap, config) and - not clearsContent(node, ap.getHead()) and + not clearsContentCached(node, ap.getHead()) and not fullBarrier(node, config) and distSink(getNodeEnclosingCallable(node), config) <= config.explorationLimit() ) @@ -3625,9 +3632,9 @@ private module FlowExploration { exists(PartialPathNodeFwd mid | partialPathStep(mid, node, cc, sc1, sc2, ap, config) and not fullBarrier(node, config) and - not clearsContent(node, ap.getHead().getContent()) and + not clearsContentCached(node, ap.getHead().getContent()) and if node instanceof CastingNode - then compatibleTypes(getNodeType(node), ap.getType()) + then compatibleTypes(getNodeDataFlowType(node), ap.getType()) else any() ) } @@ -3783,7 +3790,7 @@ private module FlowExploration { PartialPathNodeFwd mid, Node node, CallContext cc, TSummaryCtx1 sc1, TSummaryCtx2 sc2, PartialAccessPath ap, Configuration config ) { - not isUnreachableInCall(node, cc.(CallContextSpecificCall).getCall()) and + not isUnreachableInCallCached(node, cc.(CallContextSpecificCall).getCall()) and ( localFlowStep(mid.getNode(), node, config) and cc = mid.getCallContext() and @@ -3797,7 +3804,7 @@ private module FlowExploration { sc1 = mid.getSummaryCtx1() and sc2 = mid.getSummaryCtx2() and mid.getAp() instanceof PartialAccessPathNil and - ap = TPartialNil(getNodeType(node)) and + ap = TPartialNil(getNodeDataFlowType(node)) and config = mid.getConfiguration() ) or @@ -3813,7 +3820,7 @@ private module FlowExploration { sc1 = TSummaryCtx1None() and sc2 = TSummaryCtx2None() and mid.getAp() instanceof PartialAccessPathNil and - ap = TPartialNil(getNodeType(node)) and + ap = TPartialNil(getNodeDataFlowType(node)) and config = mid.getConfiguration() or partialPathStoreStep(mid, _, _, node, ap) and @@ -3827,7 +3834,7 @@ private module FlowExploration { sc1 = mid.getSummaryCtx1() and sc2 = mid.getSummaryCtx2() and apConsFwd(ap, tc, ap0, config) and - compatibleTypes(ap.getType(), getNodeType(node)) + compatibleTypes(ap.getType(), getNodeDataFlowType(node)) ) or partialPathIntoCallable(mid, node, _, cc, sc1, sc2, _, ap, config) @@ -3924,7 +3931,7 @@ private module FlowExploration { PartialPathNodeFwd mid, int i, CallContext cc, DataFlowCall call, PartialAccessPath ap, Configuration config ) { - exists(ArgumentNode arg | + exists(ArgumentNodeExt arg | arg = mid.getNode() and cc = mid.getCallContext() and arg.argumentOf(call, i) and @@ -3943,7 +3950,7 @@ private module FlowExploration { } private predicate partialPathIntoCallable( - PartialPathNodeFwd mid, ParameterNode p, CallContext outercc, CallContextCall innercc, + PartialPathNodeFwd mid, ParameterNodeExt p, CallContext outercc, CallContextCall innercc, TSummaryCtx1 sc1, TSummaryCtx2 sc2, DataFlowCall call, PartialAccessPath ap, Configuration config ) { @@ -3980,7 +3987,7 @@ private module FlowExploration { DataFlowCall call, PartialPathNodeFwd mid, ReturnKindExt kind, CallContext cc, PartialAccessPath ap, Configuration config ) { - exists(ParameterNode p, CallContext innercc, TSummaryCtx1 sc1, TSummaryCtx2 sc2 | + exists(ParameterNodeExt p, CallContext innercc, TSummaryCtx1 sc1, TSummaryCtx2 sc2 | partialPathIntoCallable(mid, p, cc, innercc, sc1, sc2, call, _, config) and paramFlowsThroughInPartialPath(kind, innercc, sc1, sc2, ap, config) ) @@ -4037,7 +4044,7 @@ private module FlowExploration { apConsRev(ap, c, ap0, config) ) or - exists(ParameterNode p | + exists(ParameterNodeExt p | mid.getNode() = p and viableParamArg(_, p, node) and sc1 = mid.getSummaryCtx1() and @@ -4115,7 +4122,7 @@ private module FlowExploration { int pos, TRevSummaryCtx1Some sc1, TRevSummaryCtx2Some sc2, RevPartialAccessPath ap, Configuration config ) { - exists(PartialPathNodeRev mid, ParameterNode p | + exists(PartialPathNodeRev mid, ParameterNodeExt p | mid.getNode() = p and p.isParameterOf(_, pos) and sc1 = mid.getSummaryCtx1() and @@ -4138,7 +4145,7 @@ private module FlowExploration { pragma[nomagic] private predicate revPartialPathThroughCallable( - PartialPathNodeRev mid, ArgumentNode node, RevPartialAccessPath ap, Configuration config + PartialPathNodeRev mid, ArgumentNodeExt node, RevPartialAccessPath ap, Configuration config ) { exists(DataFlowCall call, int pos | revPartialPathThroughCallable0(call, mid, pos, ap, config) and diff --git a/python/ql/src/semmle/python/dataflow/new/internal/DataFlowImpl4.qll b/python/ql/src/semmle/python/dataflow/new/internal/DataFlowImpl4.qll index 9498e51e7e6..cf83530bcbd 100644 --- a/python/ql/src/semmle/python/dataflow/new/internal/DataFlowImpl4.qll +++ b/python/ql/src/semmle/python/dataflow/new/internal/DataFlowImpl4.qll @@ -211,10 +211,7 @@ private predicate fullBarrier(Node node, Configuration config) { * Holds if data can flow in one local step from `node1` to `node2`. */ private predicate localFlowStep(Node node1, Node node2, Configuration config) { - ( - simpleLocalFlowStep(node1, node2) or - reverseStepThroughInputOutputAlias(node1, node2) - ) and + simpleLocalFlowStepExt(node1, node2) and not outBarrier(node1, config) and not inBarrier(node2, config) and not fullBarrier(node1, config) and @@ -237,7 +234,7 @@ private predicate additionalLocalFlowStep(Node node1, Node node2, Configuration * Holds if data can flow from `node1` to `node2` in a way that discards call contexts. */ private predicate jumpStep(Node node1, Node node2, Configuration config) { - jumpStep(node1, node2) and + jumpStepCached(node1, node2) and not outBarrier(node1, config) and not inBarrier(node2, config) and not fullBarrier(node1, config) and @@ -388,7 +385,7 @@ private module Stage1 { */ pragma[nomagic] private predicate fwdFlowIsEntered(DataFlowCall call, Cc cc, Configuration config) { - exists(ArgumentNode arg | + exists(ArgumentNodeExt arg | fwdFlow(arg, cc, config) and viableParamArg(call, _, arg) ) @@ -515,7 +512,7 @@ private module Stage1 { pragma[nomagic] predicate viableParamArgNodeCandFwd1( - DataFlowCall call, ParameterNode p, ArgumentNode arg, Configuration config + DataFlowCall call, ParameterNodeExt p, ArgumentNodeExt arg, Configuration config ) { viableParamArg(call, p, arg) and fwdFlow(arg, config) @@ -523,16 +520,16 @@ private module Stage1 { pragma[nomagic] private predicate revFlowIn( - DataFlowCall call, ArgumentNode arg, boolean toReturn, Configuration config + DataFlowCall call, ArgumentNodeExt arg, boolean toReturn, Configuration config ) { - exists(ParameterNode p | + exists(ParameterNodeExt p | revFlow(p, toReturn, config) and viableParamArgNodeCandFwd1(call, p, arg, config) ) } pragma[nomagic] - private predicate revFlowInToReturn(DataFlowCall call, ArgumentNode arg, Configuration config) { + private predicate revFlowInToReturn(DataFlowCall call, ArgumentNodeExt arg, Configuration config) { revFlowIn(call, arg, true, config) } @@ -597,7 +594,9 @@ private module Stage1 { * Holds if flow may enter through `p` and reach a return node making `p` a * candidate for the origin of a summary. */ - predicate parameterMayFlowThrough(ParameterNode p, DataFlowCallable c, Ap ap, Configuration config) { + predicate parameterMayFlowThrough( + ParameterNodeExt p, DataFlowCallable c, Ap ap, Configuration config + ) { exists(ReturnKindExt kind | throughFlowNodeCand(p, config) and returnFlowCallableNodeCand(c, kind, config) and @@ -663,7 +662,7 @@ private predicate flowOutOfCallNodeCand1( pragma[nomagic] private predicate viableParamArgNodeCand1( - DataFlowCall call, ParameterNode p, ArgumentNode arg, Configuration config + DataFlowCall call, ParameterNodeExt p, ArgumentNodeExt arg, Configuration config ) { Stage1::viableParamArgNodeCandFwd1(call, p, arg, config) and Stage1::revFlow(arg, config) @@ -675,7 +674,7 @@ private predicate viableParamArgNodeCand1( */ pragma[nomagic] private predicate flowIntoCallNodeCand1( - DataFlowCall call, ArgumentNode arg, ParameterNode p, Configuration config + DataFlowCall call, ArgumentNodeExt arg, ParameterNodeExt p, Configuration config ) { viableParamArgNodeCand1(call, p, arg, config) and Stage1::revFlow(p, config) and @@ -735,7 +734,7 @@ private predicate flowOutOfCallNodeCand1( */ pragma[nomagic] private predicate flowIntoCallNodeCand1( - DataFlowCall call, ArgumentNode arg, ParameterNode p, boolean allowsFieldFlow, + DataFlowCall call, ArgumentNodeExt arg, ParameterNodeExt p, boolean allowsFieldFlow, Configuration config ) { flowIntoCallNodeCand1(call, arg, p, config) and @@ -944,10 +943,10 @@ private module Stage2 { pragma[nomagic] private predicate fwdFlowIn( - DataFlowCall call, ParameterNode p, Cc outercc, Cc innercc, ApOption argAp, Ap ap, + DataFlowCall call, ParameterNodeExt p, Cc outercc, Cc innercc, ApOption argAp, Ap ap, Configuration config ) { - exists(ArgumentNode arg, boolean allowsFieldFlow | + exists(ArgumentNodeExt arg, boolean allowsFieldFlow | fwdFlow(arg, outercc, argAp, ap, config) and flowIntoCall(call, arg, p, allowsFieldFlow, config) and innercc = getCallContextCall(call, getNodeEnclosingCallable(p), outercc) @@ -992,7 +991,7 @@ private module Stage2 { private predicate fwdFlowIsEntered( DataFlowCall call, Cc cc, ApOption argAp, Ap ap, Configuration config ) { - exists(ParameterNode p | + exists(ParameterNodeExt p | fwdFlowIn(call, p, cc, _, argAp, ap, config) and PrevStage::parameterMayFlowThrough(p, _, getApprox(ap), config) ) @@ -1133,10 +1132,10 @@ private module Stage2 { pragma[nomagic] private predicate revFlowIn( - DataFlowCall call, ArgumentNode arg, boolean toReturn, ApOption returnAp, Ap ap, + DataFlowCall call, ArgumentNodeExt arg, boolean toReturn, ApOption returnAp, Ap ap, Configuration config ) { - exists(ParameterNode p, boolean allowsFieldFlow | + exists(ParameterNodeExt p, boolean allowsFieldFlow | revFlow(p, toReturn, returnAp, ap, config) and flowIntoCall(call, arg, p, allowsFieldFlow, config) | @@ -1146,7 +1145,7 @@ private module Stage2 { pragma[nomagic] private predicate revFlowInToReturn( - DataFlowCall call, ArgumentNode arg, Ap returnAp, Ap ap, Configuration config + DataFlowCall call, ArgumentNodeExt arg, Ap returnAp, Ap ap, Configuration config ) { revFlowIn(call, arg, true, apSome(returnAp), ap, config) } @@ -1199,13 +1198,15 @@ private module Stage2 { pragma[noinline] private predicate parameterFlow( - ParameterNode p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config + ParameterNodeExt p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config ) { revFlow(p, true, apSome(ap0), ap, config) and c = getNodeEnclosingCallable(p) } - predicate parameterMayFlowThrough(ParameterNode p, DataFlowCallable c, Ap ap, Configuration config) { + predicate parameterMayFlowThrough( + ParameterNodeExt p, DataFlowCallable c, Ap ap, Configuration config + ) { exists(ReturnNodeExt ret, Ap ap0, ReturnKindExt kind, int pos | parameterFlow(p, ap, ap0, c, config) and c = getNodeEnclosingCallable(ret) and @@ -1245,7 +1246,7 @@ private predicate flowOutOfCallNodeCand2( pragma[nomagic] private predicate flowIntoCallNodeCand2( - DataFlowCall call, ArgumentNode node1, ParameterNode node2, boolean allowsFieldFlow, + DataFlowCall call, ArgumentNodeExt node1, ParameterNodeExt node2, boolean allowsFieldFlow, Configuration config ) { flowIntoCallNodeCand1(call, node1, node2, allowsFieldFlow, config) and @@ -1260,8 +1261,8 @@ private module LocalFlowBigStep { */ private class FlowCheckNode extends Node { FlowCheckNode() { - this instanceof CastNode or - clearsContent(this, _) + castNode(this) or + clearsContentCached(this, _) } } @@ -1275,7 +1276,7 @@ private module LocalFlowBigStep { config.isSource(node) or jumpStep(_, node, config) or additionalJumpStep(_, node, config) or - node instanceof ParameterNode or + node instanceof ParameterNodeExt or node instanceof OutNodeExt or store(_, _, node, _) or read(_, _, node) or @@ -1321,21 +1322,21 @@ private module LocalFlowBigStep { Node node1, Node node2, boolean preservesValue, DataFlowType t, Configuration config, LocalCallContext cc ) { - not isUnreachableInCall(node2, cc.(LocalCallContextSpecificCall).getCall()) and + not isUnreachableInCallCached(node2, cc.(LocalCallContextSpecificCall).getCall()) and ( localFlowEntry(node1, pragma[only_bind_into](config)) and ( localFlowStepNodeCand1(node1, node2, config) and preservesValue = true and - t = getNodeType(node1) + t = getNodeDataFlowType(node1) or additionalLocalFlowStepNodeCand2(node1, node2, config) and preservesValue = false and - t = getNodeType(node2) + t = getNodeDataFlowType(node2) ) and node1 != node2 and cc.relevantFor(getNodeEnclosingCallable(node1)) and - not isUnreachableInCall(node1, cc.(LocalCallContextSpecificCall).getCall()) and + not isUnreachableInCallCached(node1, cc.(LocalCallContextSpecificCall).getCall()) and Stage2::revFlow(node2, pragma[only_bind_into](config)) or exists(Node mid | @@ -1350,7 +1351,7 @@ private module LocalFlowBigStep { additionalLocalFlowStepNodeCand2(mid, node2, config) and not mid instanceof FlowCheckNode and preservesValue = false and - t = getNodeType(node2) and + t = getNodeDataFlowType(node2) and Stage2::revFlow(node2, pragma[only_bind_into](config)) ) ) @@ -1384,7 +1385,7 @@ private module Stage3 { private ApApprox getApprox(Ap ap) { result = ap.toBoolNonEmpty() } private ApNil getApNil(Node node) { - PrevStage::revFlow(node, _) and result = TFrontNil(getNodeType(node)) + PrevStage::revFlow(node, _) and result = TFrontNil(getNodeDataFlowType(node)) } bindingset[tc, tail] @@ -1443,7 +1444,9 @@ private module Stage3 { bindingset[node, ap] private predicate filter(Node node, Ap ap) { not ap.isClearedAt(node) and - if node instanceof CastingNode then compatibleTypes(getNodeType(node), ap.getType()) else any() + if node instanceof CastingNode + then compatibleTypes(getNodeDataFlowType(node), ap.getType()) + else any() } bindingset[ap, contentType] @@ -1583,10 +1586,10 @@ private module Stage3 { pragma[nomagic] private predicate fwdFlowIn( - DataFlowCall call, ParameterNode p, Cc outercc, Cc innercc, ApOption argAp, Ap ap, + DataFlowCall call, ParameterNodeExt p, Cc outercc, Cc innercc, ApOption argAp, Ap ap, Configuration config ) { - exists(ArgumentNode arg, boolean allowsFieldFlow | + exists(ArgumentNodeExt arg, boolean allowsFieldFlow | fwdFlow(arg, outercc, argAp, ap, config) and flowIntoCall(call, arg, p, allowsFieldFlow, config) and innercc = getCallContextCall(call, getNodeEnclosingCallable(p), outercc) @@ -1631,7 +1634,7 @@ private module Stage3 { private predicate fwdFlowIsEntered( DataFlowCall call, Cc cc, ApOption argAp, Ap ap, Configuration config ) { - exists(ParameterNode p | + exists(ParameterNodeExt p | fwdFlowIn(call, p, cc, _, argAp, ap, config) and PrevStage::parameterMayFlowThrough(p, _, unbindApa(getApprox(ap)), config) ) @@ -1772,10 +1775,10 @@ private module Stage3 { pragma[nomagic] private predicate revFlowIn( - DataFlowCall call, ArgumentNode arg, boolean toReturn, ApOption returnAp, Ap ap, + DataFlowCall call, ArgumentNodeExt arg, boolean toReturn, ApOption returnAp, Ap ap, Configuration config ) { - exists(ParameterNode p, boolean allowsFieldFlow | + exists(ParameterNodeExt p, boolean allowsFieldFlow | revFlow(p, toReturn, returnAp, ap, config) and flowIntoCall(call, arg, p, allowsFieldFlow, config) | @@ -1785,7 +1788,7 @@ private module Stage3 { pragma[nomagic] private predicate revFlowInToReturn( - DataFlowCall call, ArgumentNode arg, Ap returnAp, Ap ap, Configuration config + DataFlowCall call, ArgumentNodeExt arg, Ap returnAp, Ap ap, Configuration config ) { revFlowIn(call, arg, true, apSome(returnAp), ap, config) } @@ -1838,13 +1841,15 @@ private module Stage3 { pragma[noinline] private predicate parameterFlow( - ParameterNode p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config + ParameterNodeExt p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config ) { revFlow(p, true, apSome(ap0), ap, config) and c = getNodeEnclosingCallable(p) } - predicate parameterMayFlowThrough(ParameterNode p, DataFlowCallable c, Ap ap, Configuration config) { + predicate parameterMayFlowThrough( + ParameterNodeExt p, DataFlowCallable c, Ap ap, Configuration config + ) { exists(ReturnNodeExt ret, Ap ap0, ReturnKindExt kind, int pos | parameterFlow(p, ap, ap0, c, config) and c = getNodeEnclosingCallable(ret) and @@ -2088,7 +2093,7 @@ private module Stage4 { private ApApprox getApprox(Ap ap) { result = ap.getFront() } private ApNil getApNil(Node node) { - PrevStage::revFlow(node, _) and result = TNil(getNodeType(node)) + PrevStage::revFlow(node, _) and result = TNil(getNodeDataFlowType(node)) } bindingset[tc, tail] @@ -2155,7 +2160,7 @@ private module Stage4 { pragma[nomagic] private predicate flowIntoCall( - DataFlowCall call, ArgumentNode node1, ParameterNode node2, boolean allowsFieldFlow, + DataFlowCall call, ArgumentNodeExt node1, ParameterNodeExt node2, boolean allowsFieldFlow, Configuration config ) { flowIntoCallNodeCand2(call, node1, node2, allowsFieldFlow, config) and @@ -2300,10 +2305,10 @@ private module Stage4 { pragma[nomagic] private predicate fwdFlowIn( - DataFlowCall call, ParameterNode p, Cc outercc, Cc innercc, ApOption argAp, Ap ap, + DataFlowCall call, ParameterNodeExt p, Cc outercc, Cc innercc, ApOption argAp, Ap ap, Configuration config ) { - exists(ArgumentNode arg, boolean allowsFieldFlow | + exists(ArgumentNodeExt arg, boolean allowsFieldFlow | fwdFlow(arg, outercc, argAp, ap, config) and flowIntoCall(call, arg, p, allowsFieldFlow, config) and innercc = getCallContextCall(call, getNodeEnclosingCallable(p), outercc) @@ -2348,7 +2353,7 @@ private module Stage4 { private predicate fwdFlowIsEntered( DataFlowCall call, Cc cc, ApOption argAp, Ap ap, Configuration config ) { - exists(ParameterNode p | + exists(ParameterNodeExt p | fwdFlowIn(call, p, cc, _, argAp, ap, config) and PrevStage::parameterMayFlowThrough(p, _, unbindApa(getApprox(ap)), config) ) @@ -2489,10 +2494,10 @@ private module Stage4 { pragma[nomagic] private predicate revFlowIn( - DataFlowCall call, ArgumentNode arg, boolean toReturn, ApOption returnAp, Ap ap, + DataFlowCall call, ArgumentNodeExt arg, boolean toReturn, ApOption returnAp, Ap ap, Configuration config ) { - exists(ParameterNode p, boolean allowsFieldFlow | + exists(ParameterNodeExt p, boolean allowsFieldFlow | revFlow(p, toReturn, returnAp, ap, config) and flowIntoCall(call, arg, p, allowsFieldFlow, config) | @@ -2502,7 +2507,7 @@ private module Stage4 { pragma[nomagic] private predicate revFlowInToReturn( - DataFlowCall call, ArgumentNode arg, Ap returnAp, Ap ap, Configuration config + DataFlowCall call, ArgumentNodeExt arg, Ap returnAp, Ap ap, Configuration config ) { revFlowIn(call, arg, true, apSome(returnAp), ap, config) } @@ -2555,13 +2560,15 @@ private module Stage4 { pragma[noinline] private predicate parameterFlow( - ParameterNode p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config + ParameterNodeExt p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config ) { revFlow(p, true, apSome(ap0), ap, config) and c = getNodeEnclosingCallable(p) } - predicate parameterMayFlowThrough(ParameterNode p, DataFlowCallable c, Ap ap, Configuration config) { + predicate parameterMayFlowThrough( + ParameterNodeExt p, DataFlowCallable c, Ap ap, Configuration config + ) { exists(ReturnNodeExt ret, Ap ap0, ReturnKindExt kind, int pos | parameterFlow(p, ap, ap0, c, config) and c = getNodeEnclosingCallable(ret) and @@ -2606,7 +2613,7 @@ private predicate nodeMayUseSummary(Node n, AccessPathApprox apa, Configuration private newtype TSummaryCtx = TSummaryCtxNone() or - TSummaryCtxSome(ParameterNode p, AccessPath ap) { + TSummaryCtxSome(ParameterNodeExt p, AccessPath ap) { Stage4::parameterMayFlowThrough(p, _, ap.getApprox(), _) } @@ -2627,7 +2634,7 @@ private class SummaryCtxNone extends SummaryCtx, TSummaryCtxNone { /** A summary context from which a flow summary can be generated. */ private class SummaryCtxSome extends SummaryCtx, TSummaryCtxSome { - private ParameterNode p; + private ParameterNodeExt p; private AccessPath ap; SummaryCtxSome() { this = TSummaryCtxSome(p, ap) } @@ -2758,7 +2765,7 @@ private newtype TPathNode = config.isSource(node) and cc instanceof CallContextAny and sc instanceof SummaryCtxNone and - ap = TAccessPathNil(getNodeType(node)) + ap = TAccessPathNil(getNodeDataFlowType(node)) or // ... or a step from an existing PathNode to another node. exists(PathNodeMid mid | @@ -2979,7 +2986,7 @@ class PathNode extends TPathNode { Configuration getConfiguration() { none() } private predicate isHidden() { - nodeIsHidden(this.getNode()) and + hiddenNode(this.getNode()) and not this.isSource() and not this instanceof PathNodeSink } @@ -3148,7 +3155,7 @@ private predicate pathStep(PathNodeMid mid, Node node, CallContext cc, SummaryCt cc instanceof CallContextAny and sc instanceof SummaryCtxNone and mid.getAp() instanceof AccessPathNil and - ap = TAccessPathNil(getNodeType(node)) + ap = TAccessPathNil(getNodeDataFlowType(node)) or exists(TypedContent tc | pathStoreStep(mid, node, ap.pop(tc), tc, cc)) and sc = mid.getSummaryCtx() @@ -3235,7 +3242,7 @@ pragma[noinline] private predicate pathIntoArg( PathNodeMid mid, int i, CallContext cc, DataFlowCall call, AccessPath ap, AccessPathApprox apa ) { - exists(ArgumentNode arg | + exists(ArgumentNodeExt arg | arg = mid.getNode() and cc = mid.getCallContext() and arg.argumentOf(call, i) and @@ -3248,7 +3255,7 @@ pragma[noinline] private predicate parameterCand( DataFlowCallable callable, int i, AccessPathApprox apa, Configuration config ) { - exists(ParameterNode p | + exists(ParameterNodeExt p | Stage4::revFlow(p, _, _, apa, config) and p.isParameterOf(callable, i) ) @@ -3272,7 +3279,7 @@ private predicate pathIntoCallable0( * respectively. */ private predicate pathIntoCallable( - PathNodeMid mid, ParameterNode p, CallContext outercc, CallContextCall innercc, SummaryCtx sc, + PathNodeMid mid, ParameterNodeExt p, CallContext outercc, CallContextCall innercc, SummaryCtx sc, DataFlowCall call ) { exists(int i, DataFlowCallable callable, AccessPath ap | @@ -3568,7 +3575,7 @@ private module FlowExploration { private newtype TSummaryCtx1 = TSummaryCtx1None() or - TSummaryCtx1Param(ParameterNode p) + TSummaryCtx1Param(ParameterNodeExt p) private newtype TSummaryCtx2 = TSummaryCtx2None() or @@ -3591,7 +3598,7 @@ private module FlowExploration { cc instanceof CallContextAny and sc1 = TSummaryCtx1None() and sc2 = TSummaryCtx2None() and - ap = TPartialNil(getNodeType(node)) and + ap = TPartialNil(getNodeDataFlowType(node)) and not fullBarrier(node, config) and exists(config.explorationLimit()) or @@ -3611,7 +3618,7 @@ private module FlowExploration { or exists(PartialPathNodeRev mid | revPartialPathStep(mid, node, sc1, sc2, ap, config) and - not clearsContent(node, ap.getHead()) and + not clearsContentCached(node, ap.getHead()) and not fullBarrier(node, config) and distSink(getNodeEnclosingCallable(node), config) <= config.explorationLimit() ) @@ -3625,9 +3632,9 @@ private module FlowExploration { exists(PartialPathNodeFwd mid | partialPathStep(mid, node, cc, sc1, sc2, ap, config) and not fullBarrier(node, config) and - not clearsContent(node, ap.getHead().getContent()) and + not clearsContentCached(node, ap.getHead().getContent()) and if node instanceof CastingNode - then compatibleTypes(getNodeType(node), ap.getType()) + then compatibleTypes(getNodeDataFlowType(node), ap.getType()) else any() ) } @@ -3783,7 +3790,7 @@ private module FlowExploration { PartialPathNodeFwd mid, Node node, CallContext cc, TSummaryCtx1 sc1, TSummaryCtx2 sc2, PartialAccessPath ap, Configuration config ) { - not isUnreachableInCall(node, cc.(CallContextSpecificCall).getCall()) and + not isUnreachableInCallCached(node, cc.(CallContextSpecificCall).getCall()) and ( localFlowStep(mid.getNode(), node, config) and cc = mid.getCallContext() and @@ -3797,7 +3804,7 @@ private module FlowExploration { sc1 = mid.getSummaryCtx1() and sc2 = mid.getSummaryCtx2() and mid.getAp() instanceof PartialAccessPathNil and - ap = TPartialNil(getNodeType(node)) and + ap = TPartialNil(getNodeDataFlowType(node)) and config = mid.getConfiguration() ) or @@ -3813,7 +3820,7 @@ private module FlowExploration { sc1 = TSummaryCtx1None() and sc2 = TSummaryCtx2None() and mid.getAp() instanceof PartialAccessPathNil and - ap = TPartialNil(getNodeType(node)) and + ap = TPartialNil(getNodeDataFlowType(node)) and config = mid.getConfiguration() or partialPathStoreStep(mid, _, _, node, ap) and @@ -3827,7 +3834,7 @@ private module FlowExploration { sc1 = mid.getSummaryCtx1() and sc2 = mid.getSummaryCtx2() and apConsFwd(ap, tc, ap0, config) and - compatibleTypes(ap.getType(), getNodeType(node)) + compatibleTypes(ap.getType(), getNodeDataFlowType(node)) ) or partialPathIntoCallable(mid, node, _, cc, sc1, sc2, _, ap, config) @@ -3924,7 +3931,7 @@ private module FlowExploration { PartialPathNodeFwd mid, int i, CallContext cc, DataFlowCall call, PartialAccessPath ap, Configuration config ) { - exists(ArgumentNode arg | + exists(ArgumentNodeExt arg | arg = mid.getNode() and cc = mid.getCallContext() and arg.argumentOf(call, i) and @@ -3943,7 +3950,7 @@ private module FlowExploration { } private predicate partialPathIntoCallable( - PartialPathNodeFwd mid, ParameterNode p, CallContext outercc, CallContextCall innercc, + PartialPathNodeFwd mid, ParameterNodeExt p, CallContext outercc, CallContextCall innercc, TSummaryCtx1 sc1, TSummaryCtx2 sc2, DataFlowCall call, PartialAccessPath ap, Configuration config ) { @@ -3980,7 +3987,7 @@ private module FlowExploration { DataFlowCall call, PartialPathNodeFwd mid, ReturnKindExt kind, CallContext cc, PartialAccessPath ap, Configuration config ) { - exists(ParameterNode p, CallContext innercc, TSummaryCtx1 sc1, TSummaryCtx2 sc2 | + exists(ParameterNodeExt p, CallContext innercc, TSummaryCtx1 sc1, TSummaryCtx2 sc2 | partialPathIntoCallable(mid, p, cc, innercc, sc1, sc2, call, _, config) and paramFlowsThroughInPartialPath(kind, innercc, sc1, sc2, ap, config) ) @@ -4037,7 +4044,7 @@ private module FlowExploration { apConsRev(ap, c, ap0, config) ) or - exists(ParameterNode p | + exists(ParameterNodeExt p | mid.getNode() = p and viableParamArg(_, p, node) and sc1 = mid.getSummaryCtx1() and @@ -4115,7 +4122,7 @@ private module FlowExploration { int pos, TRevSummaryCtx1Some sc1, TRevSummaryCtx2Some sc2, RevPartialAccessPath ap, Configuration config ) { - exists(PartialPathNodeRev mid, ParameterNode p | + exists(PartialPathNodeRev mid, ParameterNodeExt p | mid.getNode() = p and p.isParameterOf(_, pos) and sc1 = mid.getSummaryCtx1() and @@ -4138,7 +4145,7 @@ private module FlowExploration { pragma[nomagic] private predicate revPartialPathThroughCallable( - PartialPathNodeRev mid, ArgumentNode node, RevPartialAccessPath ap, Configuration config + PartialPathNodeRev mid, ArgumentNodeExt node, RevPartialAccessPath ap, Configuration config ) { exists(DataFlowCall call, int pos | revPartialPathThroughCallable0(call, mid, pos, ap, config) and diff --git a/python/ql/src/semmle/python/dataflow/new/internal/DataFlowImplCommon.qll b/python/ql/src/semmle/python/dataflow/new/internal/DataFlowImplCommon.qll index a51c20c2288..b5ceabc605d 100644 --- a/python/ql/src/semmle/python/dataflow/new/internal/DataFlowImplCommon.qll +++ b/python/ql/src/semmle/python/dataflow/new/internal/DataFlowImplCommon.qll @@ -35,22 +35,24 @@ predicate accessPathCostLimits(int apLimit, int tupleLimit) { * calls. For this reason, we cannot reuse the code from `DataFlowImpl.qll` directly. */ private module LambdaFlow { - private predicate viableParamNonLambda(DataFlowCall call, int i, ParameterNode p) { + private predicate viableParamNonLambda(DataFlowCall call, int i, ParameterNodeExt p) { p.isParameterOf(viableCallable(call), i) } - private predicate viableParamLambda(DataFlowCall call, int i, ParameterNode p) { + private predicate viableParamLambda(DataFlowCall call, int i, ParameterNodeExt p) { p.isParameterOf(viableCallableLambda(call, _), i) } - private predicate viableParamArgNonLambda(DataFlowCall call, ParameterNode p, ArgumentNode arg) { + private predicate viableParamArgNonLambda( + DataFlowCall call, ParameterNodeExt p, ArgumentNodeExt arg + ) { exists(int i | viableParamNonLambda(call, i, p) and arg.argumentOf(call, i) ) } - private predicate viableParamArgLambda(DataFlowCall call, ParameterNode p, ArgumentNode arg) { + private predicate viableParamArgLambda(DataFlowCall call, ParameterNodeExt p, ArgumentNodeExt arg) { exists(int i | viableParamLambda(call, i, p) and arg.argumentOf(call, i) @@ -118,8 +120,8 @@ private module LambdaFlow { boolean toJump, DataFlowCallOption lastCall ) { revLambdaFlow0(lambdaCall, kind, node, t, toReturn, toJump, lastCall) and - if node instanceof CastNode or node instanceof ArgumentNode or node instanceof ReturnNode - then compatibleTypes(t, getNodeType(node)) + if castNode(node) or node instanceof ArgumentNodeExt or node instanceof ReturnNode + then compatibleTypes(t, getNodeDataFlowType(node)) else any() } @@ -129,7 +131,7 @@ private module LambdaFlow { boolean toJump, DataFlowCallOption lastCall ) { lambdaCall(lambdaCall, kind, node) and - t = getNodeType(node) and + t = getNodeDataFlowType(node) and toReturn = false and toJump = false and lastCall = TDataFlowCallNone() @@ -146,7 +148,7 @@ private module LambdaFlow { getNodeEnclosingCallable(node) = getNodeEnclosingCallable(mid) | preservesValue = false and - t = getNodeType(node) + t = getNodeDataFlowType(node) or preservesValue = true and t = t0 @@ -160,7 +162,7 @@ private module LambdaFlow { toJump = true and lastCall = TDataFlowCallNone() | - jumpStep(node, mid) and + jumpStepCached(node, mid) and t = t0 or exists(boolean preservesValue | @@ -168,7 +170,7 @@ private module LambdaFlow { getNodeEnclosingCallable(node) != getNodeEnclosingCallable(mid) | preservesValue = false and - t = getNodeType(node) + t = getNodeDataFlowType(node) or preservesValue = true and t = t0 @@ -176,7 +178,7 @@ private module LambdaFlow { ) or // flow into a callable - exists(ParameterNode p, DataFlowCallOption lastCall0, DataFlowCall call | + exists(ParameterNodeExt p, DataFlowCallOption lastCall0, DataFlowCall call | revLambdaFlowIn(lambdaCall, kind, p, t, toJump, lastCall0) and ( if lastCall0 = TDataFlowCallNone() and toJump = false @@ -227,8 +229,8 @@ private module LambdaFlow { pragma[nomagic] predicate revLambdaFlowIn( - DataFlowCall lambdaCall, LambdaCallKind kind, ParameterNode p, DataFlowType t, boolean toJump, - DataFlowCallOption lastCall + DataFlowCall lambdaCall, LambdaCallKind kind, ParameterNodeExt p, DataFlowType t, + boolean toJump, DataFlowCallOption lastCall ) { revLambdaFlow(lambdaCall, kind, p, t, false, toJump, lastCall) } @@ -242,6 +244,89 @@ private DataFlowCallable viableCallableExt(DataFlowCall call) { cached private module Cached { + /** + * If needed, call this predicate from `DataFlowImplSpecific.qll` in order to + * force a stage-dependency on the `DataFlowImplCommon.qll` stage and therby + * collapsing the two stages. + */ + cached + predicate forceCachingInSameStage() { any() } + + cached + predicate nodeEnclosingCallable(Node n, DataFlowCallable c) { c = n.getEnclosingCallable() } + + cached + predicate callEnclosingCallable(DataFlowCall call, DataFlowCallable c) { + c = call.getEnclosingCallable() + } + + cached + predicate nodeDataFlowType(Node n, DataFlowType t) { t = getNodeType(n) } + + cached + predicate jumpStepCached(Node node1, Node node2) { jumpStep(node1, node2) } + + cached + predicate clearsContentCached(Node n, Content c) { clearsContent(n, c) } + + cached + predicate isUnreachableInCallCached(Node n, DataFlowCall call) { isUnreachableInCall(n, call) } + + cached + predicate outNodeExt(Node n) { + n instanceof OutNode + or + n.(PostUpdateNode).getPreUpdateNode() instanceof ArgumentNodeExt + } + + cached + predicate hiddenNode(Node n) { nodeIsHidden(n) } + + cached + OutNodeExt getAnOutNodeExt(DataFlowCall call, ReturnKindExt k) { + result = getAnOutNode(call, k.(ValueReturnKind).getKind()) + or + exists(ArgumentNodeExt arg | + result.(PostUpdateNode).getPreUpdateNode() = arg and + arg.argumentOf(call, k.(ParamUpdateReturnKind).getPosition()) + ) + } + + cached + predicate returnNodeExt(Node n, ReturnKindExt k) { + k = TValueReturn(n.(ReturnNode).getKind()) + or + exists(ParameterNodeExt p, int pos | + parameterValueFlowsToPreUpdate(p, n) and + p.isParameterOf(_, pos) and + k = TParamUpdate(pos) + ) + } + + cached + predicate castNode(Node n) { n instanceof CastNode } + + cached + predicate castingNode(Node n) { + castNode(n) or + n instanceof ParameterNodeExt or + n instanceof OutNodeExt or + // For reads, `x.f`, we want to check that the tracked type after the read (which + // is obtained by popping the head of the access path stack) is compatible with + // the type of `x.f`. + read(_, _, n) + } + + cached + predicate parameterNode(Node n, DataFlowCallable c, int i) { + n.(ParameterNode).isParameterOf(c, i) + } + + cached + predicate argumentNode(Node n, DataFlowCall call, int pos) { + n.(ArgumentNode).argumentOf(call, pos) + } + /** * Gets a viable target for the lambda call `call`. * @@ -261,7 +346,7 @@ private module Cached { * The instance parameter is considered to have index `-1`. */ pragma[nomagic] - private predicate viableParam(DataFlowCall call, int i, ParameterNode p) { + private predicate viableParam(DataFlowCall call, int i, ParameterNodeExt p) { p.isParameterOf(viableCallableExt(call), i) } @@ -270,11 +355,11 @@ private module Cached { * dispatch into account. */ cached - predicate viableParamArg(DataFlowCall call, ParameterNode p, ArgumentNode arg) { + predicate viableParamArg(DataFlowCall call, ParameterNodeExt p, ArgumentNodeExt arg) { exists(int i | viableParam(call, i, p) and arg.argumentOf(call, i) and - compatibleTypes(getNodeType(arg), getNodeType(p)) + compatibleTypes(getNodeDataFlowType(arg), getNodeDataFlowType(p)) ) } @@ -312,7 +397,7 @@ private module Cached { * `read` indicates whether it is contents of `p` that can flow to `node`. */ pragma[nomagic] - private predicate parameterValueFlowCand(ParameterNode p, Node node, boolean read) { + private predicate parameterValueFlowCand(ParameterNodeExt p, Node node, boolean read) { p = node and read = false or @@ -325,30 +410,32 @@ private module Cached { // read exists(Node mid | parameterValueFlowCand(p, mid, false) and - readStep(mid, _, node) and + read(mid, _, node) and read = true ) or // flow through: no prior read - exists(ArgumentNode arg | + exists(ArgumentNodeExt arg | parameterValueFlowArgCand(p, arg, false) and argumentValueFlowsThroughCand(arg, node, read) ) or // flow through: no read inside method - exists(ArgumentNode arg | + exists(ArgumentNodeExt arg | parameterValueFlowArgCand(p, arg, read) and argumentValueFlowsThroughCand(arg, node, false) ) } pragma[nomagic] - private predicate parameterValueFlowArgCand(ParameterNode p, ArgumentNode arg, boolean read) { + private predicate parameterValueFlowArgCand( + ParameterNodeExt p, ArgumentNodeExt arg, boolean read + ) { parameterValueFlowCand(p, arg, read) } pragma[nomagic] - predicate parameterValueFlowsToPreUpdateCand(ParameterNode p, PostUpdateNode n) { + predicate parameterValueFlowsToPreUpdateCand(ParameterNodeExt p, PostUpdateNode n) { parameterValueFlowCand(p, n.getPreUpdateNode(), false) } @@ -360,7 +447,7 @@ private module Cached { * `read` indicates whether it is contents of `p` that can flow to the return * node. */ - predicate parameterValueFlowReturnCand(ParameterNode p, ReturnKind kind, boolean read) { + predicate parameterValueFlowReturnCand(ParameterNodeExt p, ReturnKind kind, boolean read) { exists(ReturnNode ret | parameterValueFlowCand(p, ret, read) and kind = ret.getKind() @@ -369,9 +456,9 @@ private module Cached { pragma[nomagic] private predicate argumentValueFlowsThroughCand0( - DataFlowCall call, ArgumentNode arg, ReturnKind kind, boolean read + DataFlowCall call, ArgumentNodeExt arg, ReturnKind kind, boolean read ) { - exists(ParameterNode param | viableParamArg(call, param, arg) | + exists(ParameterNodeExt param | viableParamArg(call, param, arg) | parameterValueFlowReturnCand(param, kind, read) ) } @@ -382,14 +469,14 @@ private module Cached { * * `read` indicates whether it is contents of `arg` that can flow to `out`. */ - predicate argumentValueFlowsThroughCand(ArgumentNode arg, Node out, boolean read) { + predicate argumentValueFlowsThroughCand(ArgumentNodeExt arg, Node out, boolean read) { exists(DataFlowCall call, ReturnKind kind | argumentValueFlowsThroughCand0(call, arg, kind, read) and out = getAnOutNode(call, kind) ) } - predicate cand(ParameterNode p, Node n) { + predicate cand(ParameterNodeExt p, Node n) { parameterValueFlowCand(p, n, _) and ( parameterValueFlowReturnCand(p, _, _) @@ -416,21 +503,21 @@ private module Cached { * If a read step was taken, then `read` captures the `Content`, the * container type, and the content type. */ - predicate parameterValueFlow(ParameterNode p, Node node, ReadStepTypesOption read) { + predicate parameterValueFlow(ParameterNodeExt p, Node node, ReadStepTypesOption read) { parameterValueFlow0(p, node, read) and if node instanceof CastingNode then // normal flow through read = TReadStepTypesNone() and - compatibleTypes(getNodeType(p), getNodeType(node)) + compatibleTypes(getNodeDataFlowType(p), getNodeDataFlowType(node)) or // getter - compatibleTypes(read.getContentType(), getNodeType(node)) + compatibleTypes(read.getContentType(), getNodeDataFlowType(node)) else any() } pragma[nomagic] - private predicate parameterValueFlow0(ParameterNode p, Node node, ReadStepTypesOption read) { + private predicate parameterValueFlow0(ParameterNodeExt p, Node node, ReadStepTypesOption read) { p = node and Cand::cand(p, _) and read = TReadStepTypesNone() @@ -447,7 +534,7 @@ private module Cached { readStepWithTypes(mid, read.getContainerType(), read.getContent(), node, read.getContentType()) and Cand::parameterValueFlowReturnCand(p, _, true) and - compatibleTypes(getNodeType(p), read.getContainerType()) + compatibleTypes(getNodeDataFlowType(p), read.getContainerType()) ) or parameterValueFlow0_0(TReadStepTypesNone(), p, node, read) @@ -455,16 +542,16 @@ private module Cached { pragma[nomagic] private predicate parameterValueFlow0_0( - ReadStepTypesOption mustBeNone, ParameterNode p, Node node, ReadStepTypesOption read + ReadStepTypesOption mustBeNone, ParameterNodeExt p, Node node, ReadStepTypesOption read ) { // flow through: no prior read - exists(ArgumentNode arg | + exists(ArgumentNodeExt arg | parameterValueFlowArg(p, arg, mustBeNone) and argumentValueFlowsThrough(arg, read, node) ) or // flow through: no read inside method - exists(ArgumentNode arg | + exists(ArgumentNodeExt arg | parameterValueFlowArg(p, arg, read) and argumentValueFlowsThrough(arg, mustBeNone, node) ) @@ -472,7 +559,7 @@ private module Cached { pragma[nomagic] private predicate parameterValueFlowArg( - ParameterNode p, ArgumentNode arg, ReadStepTypesOption read + ParameterNodeExt p, ArgumentNodeExt arg, ReadStepTypesOption read ) { parameterValueFlow(p, arg, read) and Cand::argumentValueFlowsThroughCand(arg, _, _) @@ -480,9 +567,9 @@ private module Cached { pragma[nomagic] private predicate argumentValueFlowsThrough0( - DataFlowCall call, ArgumentNode arg, ReturnKind kind, ReadStepTypesOption read + DataFlowCall call, ArgumentNodeExt arg, ReturnKind kind, ReadStepTypesOption read ) { - exists(ParameterNode param | viableParamArg(call, param, arg) | + exists(ParameterNodeExt param | viableParamArg(call, param, arg) | parameterValueFlowReturn(param, kind, read) ) } @@ -496,18 +583,18 @@ private module Cached { * container type, and the content type. */ pragma[nomagic] - predicate argumentValueFlowsThrough(ArgumentNode arg, ReadStepTypesOption read, Node out) { + predicate argumentValueFlowsThrough(ArgumentNodeExt arg, ReadStepTypesOption read, Node out) { exists(DataFlowCall call, ReturnKind kind | argumentValueFlowsThrough0(call, arg, kind, read) and out = getAnOutNode(call, kind) | // normal flow through read = TReadStepTypesNone() and - compatibleTypes(getNodeType(arg), getNodeType(out)) + compatibleTypes(getNodeDataFlowType(arg), getNodeDataFlowType(out)) or // getter - compatibleTypes(getNodeType(arg), read.getContainerType()) and - compatibleTypes(read.getContentType(), getNodeType(out)) + compatibleTypes(getNodeDataFlowType(arg), read.getContainerType()) and + compatibleTypes(read.getContentType(), getNodeDataFlowType(out)) ) } @@ -516,7 +603,7 @@ private module Cached { * value-preserving steps and a single read step, not taking call * contexts into account, thus representing a getter-step. */ - predicate getterStep(ArgumentNode arg, Content c, Node out) { + predicate getterStep(ArgumentNodeExt arg, Content c, Node out) { argumentValueFlowsThrough(arg, TReadStepTypesSome(_, c, _), out) } @@ -529,7 +616,7 @@ private module Cached { * container type, and the content type. */ private predicate parameterValueFlowReturn( - ParameterNode p, ReturnKind kind, ReadStepTypesOption read + ParameterNodeExt p, ReturnKind kind, ReadStepTypesOption read ) { exists(ReturnNode ret | parameterValueFlow(p, ret, read) and @@ -553,7 +640,7 @@ private module Cached { private predicate mayBenefitFromCallContextExt(DataFlowCall call, DataFlowCallable callable) { mayBenefitFromCallContext(call, callable) or - callable = call.getEnclosingCallable() and + callEnclosingCallable(call, callable) and exists(viableCallableLambda(call, TDataFlowCallSome(_))) } @@ -611,7 +698,7 @@ private module Cached { mayBenefitFromCallContextExt(call, _) and c = viableCallableExt(call) and ctxtgts = count(DataFlowCall ctx | c = viableImplInCallContextExt(call, ctx)) and - tgts = strictcount(DataFlowCall ctx | viableCallableExt(ctx) = call.getEnclosingCallable()) and + tgts = strictcount(DataFlowCall ctx | callEnclosingCallable(call, viableCallableExt(ctx))) and ctxtgts < tgts ) } @@ -635,8 +722,7 @@ private module Cached { * Holds if `p` can flow to the pre-update node associated with post-update * node `n`, in the same callable, using only value-preserving steps. */ - cached - predicate parameterValueFlowsToPreUpdate(ParameterNode p, PostUpdateNode n) { + private predicate parameterValueFlowsToPreUpdate(ParameterNodeExt p, PostUpdateNode n) { parameterValueFlow(p, n.getPreUpdateNode(), TReadStepTypesNone()) } @@ -644,9 +730,9 @@ private module Cached { Node node1, Content c, Node node2, DataFlowType contentType, DataFlowType containerType ) { storeStep(node1, c, node2) and - readStep(_, c, _) and - contentType = getNodeType(node1) and - containerType = getNodeType(node2) + read(_, c, _) and + contentType = getNodeDataFlowType(node1) and + containerType = getNodeDataFlowType(node2) or exists(Node n1, Node n2 | n1 = node1.(PostUpdateNode).getPreUpdateNode() and @@ -654,12 +740,15 @@ private module Cached { | argumentValueFlowsThrough(n2, TReadStepTypesSome(containerType, c, contentType), n1) or - readStep(n2, c, n1) and - contentType = getNodeType(n1) and - containerType = getNodeType(n2) + read(n2, c, n1) and + contentType = getNodeDataFlowType(n1) and + containerType = getNodeDataFlowType(n2) ) } + cached + predicate read(Node node1, Content c, Node node2) { readStep(node1, c, node2) } + /** * Holds if data can flow from `node1` to `node2` via a direct assignment to * `f`. @@ -678,8 +767,9 @@ private module Cached { * are aliases. A typical example is a function returning `this`, implementing a fluent * interface. */ - cached - predicate reverseStepThroughInputOutputAlias(PostUpdateNode fromNode, PostUpdateNode toNode) { + private predicate reverseStepThroughInputOutputAlias( + PostUpdateNode fromNode, PostUpdateNode toNode + ) { exists(Node fromPre, Node toPre | fromPre = fromNode.getPreUpdateNode() and toPre = toNode.getPreUpdateNode() @@ -688,14 +778,20 @@ private module Cached { // Does the language-specific simpleLocalFlowStep already model flow // from function input to output? fromPre = getAnOutNode(c, _) and - toPre.(ArgumentNode).argumentOf(c, _) and - simpleLocalFlowStep(toPre.(ArgumentNode), fromPre) + toPre.(ArgumentNodeExt).argumentOf(c, _) and + simpleLocalFlowStep(toPre.(ArgumentNodeExt), fromPre) ) or argumentValueFlowsThrough(toPre, TReadStepTypesNone(), fromPre) ) } + cached + predicate simpleLocalFlowStepExt(Node node1, Node node2) { + simpleLocalFlowStep(node1, node2) or + reverseStepThroughInputOutputAlias(node1, node2) + } + /** * Holds if the call context `call` either improves virtual dispatch in * `callable` or if it allows us to prune unreachable nodes in `callable`. @@ -704,7 +800,7 @@ private module Cached { predicate recordDataFlowCallSite(DataFlowCall call, DataFlowCallable callable) { reducedViableImplInCallContext(_, callable, call) or - exists(Node n | getNodeEnclosingCallable(n) = callable | isUnreachableInCall(n, call)) + exists(Node n | getNodeEnclosingCallable(n) = callable | isUnreachableInCallCached(n, call)) } cached @@ -726,12 +822,12 @@ private module Cached { cached newtype TLocalFlowCallContext = TAnyLocalCall() or - TSpecificLocalCall(DataFlowCall call) { isUnreachableInCall(_, call) } + TSpecificLocalCall(DataFlowCall call) { isUnreachableInCallCached(_, call) } cached newtype TReturnKindExt = TValueReturn(ReturnKind kind) or - TParamUpdate(int pos) { exists(ParameterNode p | p.isParameterOf(_, pos)) } + TParamUpdate(int pos) { exists(ParameterNodeExt p | p.isParameterOf(_, pos)) } cached newtype TBooleanOption = @@ -761,23 +857,15 @@ private module Cached { * A `Node` at which a cast can occur such that the type should be checked. */ class CastingNode extends Node { - CastingNode() { - this instanceof ParameterNode or - this instanceof CastNode or - this instanceof OutNodeExt or - // For reads, `x.f`, we want to check that the tracked type after the read (which - // is obtained by popping the head of the access path stack) is compatible with - // the type of `x.f`. - readStep(_, _, this) - } + CastingNode() { castingNode(this) } } private predicate readStepWithTypes( Node n1, DataFlowType container, Content c, Node n2, DataFlowType content ) { - readStep(n1, c, n2) and - container = getNodeType(n1) and - content = getNodeType(n2) + read(n1, c, n2) and + container = getNodeDataFlowType(n1) and + content = getNodeDataFlowType(n2) } private newtype TReadStepTypesOption = @@ -854,7 +942,7 @@ class CallContextSomeCall extends CallContextCall, TSomeCall { override string toString() { result = "CcSomeCall" } override predicate relevantFor(DataFlowCallable callable) { - exists(ParameterNode p | getNodeEnclosingCallable(p) = callable) + exists(ParameterNodeExt p | getNodeEnclosingCallable(p) = callable) } override predicate matchesCall(DataFlowCall call) { any() } @@ -866,7 +954,7 @@ class CallContextReturn extends CallContextNoCall, TReturn { } override predicate relevantFor(DataFlowCallable callable) { - exists(DataFlowCall call | this = TReturn(_, call) and call.getEnclosingCallable() = callable) + exists(DataFlowCall call | this = TReturn(_, call) and callEnclosingCallable(call, callable)) } } @@ -899,7 +987,7 @@ class LocalCallContextSpecificCall extends LocalCallContext, TSpecificLocalCall } private predicate relevantLocalCCtx(DataFlowCall call, DataFlowCallable callable) { - exists(Node n | getNodeEnclosingCallable(n) = callable and isUnreachableInCall(n, call)) + exists(Node n | getNodeEnclosingCallable(n) = callable and isUnreachableInCallCached(n, call)) } /** @@ -913,26 +1001,37 @@ LocalCallContext getLocalCallContext(CallContext ctx, DataFlowCallable callable) else result instanceof LocalCallContextAny } +/** + * The value of a parameter at function entry, viewed as a node in a data + * flow graph. + */ +class ParameterNodeExt extends Node { + ParameterNodeExt() { parameterNode(this, _, _) } + + /** + * Holds if this node is the parameter of callable `c` at the specified + * (zero-based) position. + */ + predicate isParameterOf(DataFlowCallable c, int i) { parameterNode(this, c, i) } +} + +/** A data-flow node that represents a call argument. */ +class ArgumentNodeExt extends Node { + ArgumentNodeExt() { argumentNode(this, _, _) } + + /** Holds if this argument occurs at the given position in the given call. */ + final predicate argumentOf(DataFlowCall call, int pos) { argumentNode(this, call, pos) } +} + /** * A node from which flow can return to the caller. This is either a regular * `ReturnNode` or a `PostUpdateNode` corresponding to the value of a parameter. */ class ReturnNodeExt extends Node { - ReturnNodeExt() { - this instanceof ReturnNode or - parameterValueFlowsToPreUpdate(_, this) - } + ReturnNodeExt() { returnNodeExt(this, _) } /** Gets the kind of this returned value. */ - ReturnKindExt getKind() { - result = TValueReturn(this.(ReturnNode).getKind()) - or - exists(ParameterNode p, int pos | - parameterValueFlowsToPreUpdate(p, this) and - p.isParameterOf(_, pos) and - result = TParamUpdate(pos) - ) - } + ReturnKindExt getKind() { returnNodeExt(this, result) } } /** @@ -940,11 +1039,7 @@ class ReturnNodeExt extends Node { * or a post-update node associated with a call argument. */ class OutNodeExt extends Node { - OutNodeExt() { - this instanceof OutNode - or - this.(PostUpdateNode).getPreUpdateNode() instanceof ArgumentNode - } + OutNodeExt() { outNodeExt(this) } } /** @@ -957,7 +1052,7 @@ abstract class ReturnKindExt extends TReturnKindExt { abstract string toString(); /** Gets a node corresponding to data flow out of `call`. */ - abstract OutNodeExt getAnOutNode(DataFlowCall call); + final OutNodeExt getAnOutNode(DataFlowCall call) { result = getAnOutNodeExt(call, this) } } class ValueReturnKind extends ReturnKindExt, TValueReturn { @@ -968,10 +1063,6 @@ class ValueReturnKind extends ReturnKindExt, TValueReturn { ReturnKind getKind() { result = kind } override string toString() { result = kind.toString() } - - override OutNodeExt getAnOutNode(DataFlowCall call) { - result = getAnOutNode(call, this.getKind()) - } } class ParamUpdateReturnKind extends ReturnKindExt, TParamUpdate { @@ -982,13 +1073,6 @@ class ParamUpdateReturnKind extends ReturnKindExt, TParamUpdate { int getPosition() { result = pos } override string toString() { result = "param update " + pos } - - override OutNodeExt getAnOutNode(DataFlowCall call) { - exists(ArgumentNode arg | - result.(PostUpdateNode).getPreUpdateNode() = arg and - arg.argumentOf(call, this.getPosition()) - ) - } } /** A callable tagged with a relevant return kind. */ @@ -1015,10 +1099,13 @@ class ReturnPosition extends TReturnPosition0 { */ pragma[inline] DataFlowCallable getNodeEnclosingCallable(Node n) { - exists(Node n0 | - pragma[only_bind_into](n0) = n and - pragma[only_bind_into](result) = n0.getEnclosingCallable() - ) + nodeEnclosingCallable(pragma[only_bind_out](n), pragma[only_bind_into](result)) +} + +/** Gets the type of `n` used for type pruning. */ +pragma[inline] +DataFlowType getNodeDataFlowType(Node n) { + nodeDataFlowType(pragma[only_bind_out](n), pragma[only_bind_into](result)) } pragma[noinline] @@ -1042,7 +1129,7 @@ predicate resolveReturn(CallContext cc, DataFlowCallable callable, DataFlowCall cc instanceof CallContextAny and callable = viableCallableExt(call) or exists(DataFlowCallable c0, DataFlowCall call0 | - call0.getEnclosingCallable() = callable and + callEnclosingCallable(call0, callable) and cc = TReturn(c0, call0) and c0 = prunedViableImplInCallContextReverse(call0, call) ) @@ -1063,8 +1150,6 @@ DataFlowCallable resolveCall(DataFlowCall call, CallContext cc) { result = viableCallableExt(call) and cc instanceof CallContextReturn } -predicate read = readStep/3; - /** An optional Boolean value. */ class BooleanOption extends TBooleanOption { string toString() { @@ -1116,7 +1201,7 @@ abstract class AccessPathFront extends TAccessPathFront { TypedContent getHead() { this = TFrontHead(result) } - predicate isClearedAt(Node n) { clearsContent(n, getHead().getContent()) } + predicate isClearedAt(Node n) { clearsContentCached(n, getHead().getContent()) } } class AccessPathFrontNil extends AccessPathFront, TFrontNil { From befc80b3cbe8ac7899a2e5b023ba2a39d5221099 Mon Sep 17 00:00:00 2001 From: Tom Hvitved Date: Mon, 26 Apr 2021 17:02:28 +0200 Subject: [PATCH 019/168] C#: Update data-flow caching --- csharp/ql/src/semmle/code/csharp/Caching.qll | 38 -- .../dataflow/internal/DataFlowDispatch.qll | 49 +- .../dataflow/internal/DataFlowPrivate.qll | 582 ++++++++---------- .../dataflow/internal/DataFlowPublic.qll | 12 +- .../internal/TaintTrackingPrivate.qll | 7 +- 5 files changed, 304 insertions(+), 384 deletions(-) diff --git a/csharp/ql/src/semmle/code/csharp/Caching.qll b/csharp/ql/src/semmle/code/csharp/Caching.qll index 374ecaaa183..92417e34586 100644 --- a/csharp/ql/src/semmle/code/csharp/Caching.qll +++ b/csharp/ql/src/semmle/code/csharp/Caching.qll @@ -47,44 +47,6 @@ module Stages { } } - cached - module DataFlowStage { - private import semmle.code.csharp.dataflow.internal.DataFlowDispatch - private import semmle.code.csharp.dataflow.internal.DataFlowPrivate - private import semmle.code.csharp.dataflow.internal.DataFlowImplCommon - private import semmle.code.csharp.dataflow.internal.TaintTrackingPrivate - - cached - predicate forceCachingInSameStage() { any() } - - cached - private predicate forceCachingInSameStageRev() { - defaultAdditionalTaintStep(_, _) - or - any(ArgumentNode n).argumentOf(_, _) - or - exists(any(DataFlow::Node n).getEnclosingCallable()) - or - exists(any(DataFlow::Node n).getControlFlowNode()) - or - exists(any(DataFlow::Node n).getType()) - or - exists(any(NodeImpl n).getDataFlowType()) - or - exists(any(DataFlow::Node n).getLocation()) - or - exists(any(DataFlow::Node n).toString()) - or - exists(any(OutNode n).getCall(_)) - or - exists(CallContext cc) - or - exists(any(DataFlowCall c).getEnclosingCallable()) - or - forceCachingInSameStageRev() - } - } - cached module UnificationStage { private import semmle.code.csharp.Unification diff --git a/csharp/ql/src/semmle/code/csharp/dataflow/internal/DataFlowDispatch.qll b/csharp/ql/src/semmle/code/csharp/dataflow/internal/DataFlowDispatch.qll index 0a7cf4acc41..04465f5ae9e 100644 --- a/csharp/ql/src/semmle/code/csharp/dataflow/internal/DataFlowDispatch.qll +++ b/csharp/ql/src/semmle/code/csharp/dataflow/internal/DataFlowDispatch.qll @@ -1,10 +1,10 @@ private import csharp private import cil private import dotnet +private import DataFlowImplCommon as DataFlowImplCommon private import DataFlowPublic private import DataFlowPrivate private import FlowSummaryImpl as FlowSummaryImpl -private import semmle.code.csharp.Caching private import semmle.code.csharp.dataflow.FlowSummary private import semmle.code.csharp.dispatch.Dispatch private import semmle.code.csharp.frameworks.system.Collections @@ -68,31 +68,30 @@ private predicate transitiveCapturedCallTarget(ControlFlow::Nodes::ElementNode c ) } -cached -private module Cached { - cached - newtype TReturnKind = - TNormalReturnKind() { Stages::DataFlowStage::forceCachingInSameStage() } or - TOutReturnKind(int i) { i = any(Parameter p | p.isOut()).getPosition() } or - TRefReturnKind(int i) { i = any(Parameter p | p.isRef()).getPosition() } or - TImplicitCapturedReturnKind(LocalScopeVariable v) { - exists(Ssa::ExplicitDefinition def | def.isCapturedVariableDefinitionFlowOut(_, _) | - v = def.getSourceVariable().getAssignable() - ) - } or - TJumpReturnKind(DataFlowCallable target, ReturnKind rk) { - rk instanceof NormalReturnKind and - ( - target instanceof Constructor or - not target.getReturnType() instanceof VoidType - ) - or - exists(target.getParameter(rk.(OutRefReturnKind).getPosition())) - } +newtype TReturnKind = + TNormalReturnKind() or + TOutReturnKind(int i) { i = any(Parameter p | p.isOut()).getPosition() } or + TRefReturnKind(int i) { i = any(Parameter p | p.isRef()).getPosition() } or + TImplicitCapturedReturnKind(LocalScopeVariable v) { + exists(Ssa::ExplicitDefinition def | def.isCapturedVariableDefinitionFlowOut(_, _) | + v = def.getSourceVariable().getAssignable() + ) + } or + TJumpReturnKind(DataFlowCallable target, ReturnKind rk) { + rk instanceof NormalReturnKind and + ( + target instanceof Constructor or + not target.getReturnType() instanceof VoidType + ) + or + exists(target.getParameter(rk.(OutRefReturnKind).getPosition())) + } +private module Cached { cached newtype TDataFlowCall = TNonDelegateCall(ControlFlow::Nodes::ElementNode cfn, DispatchCall dc) { + DataFlowImplCommon::forceCachingInSameStage() and cfn.getElement() = dc.getCall() } or TExplicitDelegateLikeCall(ControlFlow::Nodes::ElementNode cfn, DelegateLikeCall dc) { @@ -246,7 +245,6 @@ abstract class DataFlowCall extends TDataFlowCall { abstract DataFlow::Node getNode(); /** Gets the enclosing callable of this call. */ - cached abstract DataFlowCallable getEnclosingCallable(); /** Gets the underlying expression, if any. */ @@ -280,10 +278,7 @@ class NonDelegateDataFlowCall extends DataFlowCall, TNonDelegateCall { override DataFlow::ExprNode getNode() { result.getControlFlowNode() = cfn } - override DataFlowCallable getEnclosingCallable() { - Stages::DataFlowStage::forceCachingInSameStage() and - result = cfn.getEnclosingCallable() - } + override DataFlowCallable getEnclosingCallable() { result = cfn.getEnclosingCallable() } override string toString() { result = cfn.toString() } diff --git a/csharp/ql/src/semmle/code/csharp/dataflow/internal/DataFlowPrivate.qll b/csharp/ql/src/semmle/code/csharp/dataflow/internal/DataFlowPrivate.qll index f42410ca3a5..c1b15df72dc 100644 --- a/csharp/ql/src/semmle/code/csharp/dataflow/internal/DataFlowPrivate.qll +++ b/csharp/ql/src/semmle/code/csharp/dataflow/internal/DataFlowPrivate.qll @@ -7,7 +7,6 @@ private import DataFlowImplCommon private import ControlFlowReachability private import FlowSummaryImpl as FlowSummaryImpl private import semmle.code.csharp.dataflow.FlowSummary -private import semmle.code.csharp.Caching private import semmle.code.csharp.Conversion private import semmle.code.csharp.dataflow.internal.SsaImpl as SsaImpl private import semmle.code.csharp.ExprOrStmtParent @@ -21,7 +20,6 @@ private import semmle.code.csharp.frameworks.system.threading.Tasks abstract class NodeImpl extends Node { /** Do not call: use `getEnclosingCallable()` instead. */ - cached abstract DataFlowCallable getEnclosingCallableImpl(); /** Do not call: use `getType()` instead. */ @@ -29,9 +27,8 @@ abstract class NodeImpl extends Node { abstract DotNet::Type getTypeImpl(); /** Gets the type of this node used for type pruning. */ - cached Gvn::GvnType getDataFlowType() { - Stages::DataFlowStage::forceCachingInSameStage() and + forceCachingInSameStage() and exists(Type t0 | result = Gvn::getGlobalValueNumber(t0) | t0 = getCSharpType(this.getType()) or @@ -55,26 +52,25 @@ abstract class NodeImpl extends Node { private class ExprNodeImpl extends ExprNode, NodeImpl { override DataFlowCallable getEnclosingCallableImpl() { - Stages::DataFlowStage::forceCachingInSameStage() and result = this.getExpr().getEnclosingCallable() } override DotNet::Type getTypeImpl() { - Stages::DataFlowStage::forceCachingInSameStage() and + forceCachingInSameStage() and result = this.getExpr().getType() } override ControlFlow::Nodes::ElementNode getControlFlowNodeImpl() { - Stages::DataFlowStage::forceCachingInSameStage() and this = TExprNode(result) + forceCachingInSameStage() and this = TExprNode(result) } override Location getLocationImpl() { - Stages::DataFlowStage::forceCachingInSameStage() and result = this.getExpr().getLocation() + forceCachingInSameStage() and result = this.getExpr().getLocation() } override string toStringImpl() { - Stages::DataFlowStage::forceCachingInSameStage() and - result = this.getControlFlowNode().toString() + forceCachingInSameStage() and + result = this.getControlFlowNodeImpl().toString() or exists(CIL::Expr e | this = TCilExprNode(e) and @@ -394,6 +390,22 @@ module LocalFlow { } } +/** + * This is the local flow predicate that is used as a building block in global + * data flow. It excludes SSA flow through instance fields, as flow through fields + * is handled by the global data-flow library, but includes various other steps + * that are only relevant for global flow. + */ +predicate simpleLocalFlowStep(Node nodeFrom, Node nodeTo) { + LocalFlow::localFlowStepCommon(nodeFrom, nodeTo) + or + LocalFlow::localFlowCapturedVarStep(nodeFrom, nodeTo) + or + FlowSummaryImpl::Private::Steps::summaryLocalStep(nodeFrom, nodeTo, true) + or + nodeTo.(ObjectCreationNode).getPreUpdateNode() = nodeFrom.(ObjectInitializerNode) +} + pragma[noinline] private Expr getImplicitArgument(Call c, int pos) { result = c.getArgument(pos) and @@ -582,12 +594,16 @@ private Type getCSharpType(DotNet::Type t) { result.matchesHandle(t) } +private class RelevantDataFlowType extends DataFlowType { + RelevantDataFlowType() { this = any(NodeImpl n).getDataFlowType() } +} + /** A GVN type that is either a `DataFlowType` or unifiable with a `DataFlowType`. */ private class DataFlowTypeOrUnifiable extends Gvn::GvnType { pragma[nomagic] DataFlowTypeOrUnifiable() { - this instanceof DataFlowType or - Gvn::unifiable(any(DataFlowType t), this) + this instanceof RelevantDataFlowType or + Gvn::unifiable(any(RelevantDataFlowType t), this) } } @@ -598,7 +614,7 @@ private TypeParameter getATypeParameterSubType(DataFlowTypeOrUnifiable t) { } pragma[noinline] -private TypeParameter getATypeParameterSubTypeRestricted(DataFlowType t) { +private TypeParameter getATypeParameterSubTypeRestricted(RelevantDataFlowType t) { result = getATypeParameterSubType(t) } @@ -614,17 +630,30 @@ private Gvn::GvnType getANonTypeParameterSubType(DataFlowTypeOrUnifiable t) { } pragma[noinline] -private Gvn::GvnType getANonTypeParameterSubTypeRestricted(DataFlowType t) { +private Gvn::GvnType getANonTypeParameterSubTypeRestricted(RelevantDataFlowType t) { result = getANonTypeParameterSubType(t) } /** A collection of cached types and predicates to be evaluated in the same stage. */ cached private module Cached { + private import TaintTrackingPrivate as TaintTrackingPrivate + + // Add artificial dependencies to enforce all cached predicates are evaluated + // in the "DataFlowImplCommon stage" + private predicate forceCaching() { + TaintTrackingPrivate::forceCachingInSameStage() or + exists(any(NodeImpl n).getTypeImpl()) or + exists(any(NodeImpl n).getControlFlowNodeImpl()) or + exists(any(NodeImpl n).getLocationImpl()) or + exists(any(NodeImpl n).toStringImpl()) + } + cached newtype TNode = TExprNode(ControlFlow::Nodes::ElementNode cfn) { - Stages::DataFlowStage::forceCachingInSameStage() and cfn.getElement() instanceof Expr + forceCaching() and + cfn.getElement() instanceof Expr } or TCilExprNode(CIL::Expr e) { e.getImplementation() instanceof CIL::BestImplementation } or TSsaDefinitionNode(Ssa::Definition def) { @@ -679,23 +708,6 @@ private module Cached { callCfn = any(Call c | isParamsArg(c, _, _)).getAControlFlowNode() } - /** - * This is the local flow predicate that is used as a building block in global - * data flow. It excludes SSA flow through instance fields, as flow through fields - * is handled by the global data-flow library, but includes various other steps - * that are only relevant for global flow. - */ - cached - predicate simpleLocalFlowStep(Node nodeFrom, Node nodeTo) { - LocalFlow::localFlowStepCommon(nodeFrom, nodeTo) - or - LocalFlow::localFlowCapturedVarStep(nodeFrom, nodeTo) - or - FlowSummaryImpl::Private::Steps::summaryLocalStep(nodeFrom, nodeTo, true) - or - nodeTo.(ObjectCreationNode).getPreUpdateNode() = nodeFrom.(ObjectInitializerNode) - } - /** * Holds if data flows from `nodeFrom` to `nodeTo` in exactly one local * (intra-procedural) step. @@ -714,178 +726,14 @@ private module Cached { FlowSummaryImpl::Private::Steps::summaryThroughStep(nodeFrom, nodeTo, true) } - /** - * Holds if `pred` can flow to `succ`, by jumping from one callable to - * another. Additional steps specified by the configuration are *not* - * taken into account. - */ - cached - predicate jumpStepImpl(Node pred, Node succ) { - pred.(NonLocalJumpNode).getAJumpSuccessor(true) = succ - or - exists(FieldOrProperty fl, FieldOrPropertyRead flr | - fl.isStatic() and - fl.isFieldLike() and - fl.getAnAssignedValue() = pred.asExpr() and - fl.getAnAccess() = flr and - flr = succ.asExpr() and - flr.hasNonlocalValue() - ) - or - exists(JumpReturnKind jrk, DataFlowCall call | - FlowSummaryImpl::Private::summaryReturnNode(pred, jrk) and - viableCallable(call) = jrk.getTarget() and - succ = getAnOutNode(call, jrk.getTargetReturnKind()) - ) - } - cached newtype TContent = TFieldContent(Field f) { f.isUnboundDeclaration() } or TPropertyContent(Property p) { p.isUnboundDeclaration() } or TElementContent() - /** - * Holds if data can flow from `node1` to `node2` via an assignment to - * content `c`. - */ - cached - predicate storeStepImpl(Node node1, Content c, Node node2) { - exists(StoreStepConfiguration x, ExprNode node, boolean postUpdate | - hasNodePath(x, node1, node) and - if postUpdate = true then node = node2.(PostUpdateNode).getPreUpdateNode() else node = node2 - | - fieldOrPropertyStore(_, c, node1.asExpr(), node.getExpr(), postUpdate) - or - arrayStore(_, node1.asExpr(), node.getExpr(), postUpdate) and c instanceof ElementContent - ) - or - exists(StoreStepConfiguration x, Expr arg, ControlFlow::Node callCfn | - x.hasExprPath(arg, node1.(ExprNode).getControlFlowNode(), _, callCfn) and - node2 = TParamsArgumentNode(callCfn) and - isParamsArg(_, arg, _) and - c instanceof ElementContent - ) - or - exists(Expr e | - e = node1.asExpr() and - node2.(YieldReturnNode).getYieldReturnStmt().getExpr() = e and - c instanceof ElementContent - ) - or - exists(Expr e | - e = node1.asExpr() and - node2.(AsyncReturnNode).getExpr() = e and - c = getResultContent() - ) - or - FlowSummaryImpl::Private::Steps::summaryStoreStep(node1, c, node2) - } - pragma[nomagic] - private PropertyContent getResultContent() { - result.getProperty() = any(SystemThreadingTasksTaskTClass c_).getResultProperty() - } - - /** - * Holds if data can flow from `node1` to `node2` via a read of content `c`. - */ - cached - predicate readStepImpl(Node node1, Content c, Node node2) { - exists(ReadStepConfiguration x | - hasNodePath(x, node1, node2) and - fieldOrPropertyRead(node1.asExpr(), c, node2.asExpr()) - or - hasNodePath(x, node1, node2) and - arrayRead(node1.asExpr(), node2.asExpr()) and - c instanceof ElementContent - or - exists(ForeachStmt fs, Ssa::ExplicitDefinition def | - x.hasDefPath(fs.getIterableExpr(), node1.getControlFlowNode(), def.getADefinition(), - def.getControlFlowNode()) and - node2.(SsaDefinitionNode).getDefinition() = def and - c instanceof ElementContent - ) - or - hasNodePath(x, node1, node2) and - node2.asExpr().(AwaitExpr).getExpr() = node1.asExpr() and - c = getResultContent() - or - // node1 = (..., node2, ...) - // node1.ItemX flows to node2 - exists(TupleExpr te, int i, Expr item | - te = node1.asExpr() and - not te.isConstruction() and - c.(FieldContent).getField() = te.getType().(TupleType).getElement(i).getUnboundDeclaration() and - // node1 = (..., item, ...) - te.getArgument(i) = item - | - // item = (..., ..., ...) in node1 = (..., (..., ..., ...), ...) - node2.asExpr().(TupleExpr) = item and - hasNodePath(x, node1, node2) - or - // item = variable in node1 = (..., variable, ...) - exists(AssignableDefinitions::TupleAssignmentDefinition tad, Ssa::ExplicitDefinition def | - node2.(SsaDefinitionNode).getDefinition() = def and - def.getADefinition() = tad and - tad.getLeaf() = item and - hasNodePath(x, node1, node2) - ) - or - // item = variable in node1 = (..., variable, ...) in a case/is var (..., ...) - te = any(PatternExpr pe).getAChildExpr*() and - exists(AssignableDefinitions::LocalVariableDefinition lvd, Ssa::ExplicitDefinition def | - node2.(SsaDefinitionNode).getDefinition() = def and - def.getADefinition() = lvd and - lvd.getDeclaration() = item and - hasNodePath(x, node1, node2) - ) - ) - ) - or - FlowSummaryImpl::Private::Steps::summaryReadStep(node1, c, node2) - } - - /** - * Holds if values stored inside content `c` are cleared at node `n`. For example, - * any value stored inside `f` is cleared at the pre-update node associated with `x` - * in `x.f = newValue`. - */ - cached - predicate clearsContent(Node n, Content c) { - fieldOrPropertyStore(_, c, _, n.asExpr(), true) - or - fieldOrPropertyStore(_, c, _, n.(ObjectInitializerNode).getInitializer(), false) - or - FlowSummaryImpl::Private::Steps::summaryStoresIntoArg(c, n) and - not c instanceof ElementContent - or - FlowSummaryImpl::Private::Steps::summaryClearsContent(n, c) - or - exists(WithExpr we, ObjectInitializer oi, FieldOrProperty f | - oi = we.getInitializer() and - n.asExpr() = oi and - f = oi.getAMemberInitializer().getInitializedMember() and - c = f.getContent() - ) - } - - /** - * Holds if the node `n` is unreachable when the call context is `call`. - */ - cached - predicate isUnreachableInCall(Node n, DataFlowCall call) { - exists( - ExplicitParameterNode paramNode, Guard guard, ControlFlow::SuccessorTypes::BooleanSuccessor bs - | - viableConstantBooleanParamArg(paramNode, bs.getValue().booleanNot(), call) and - paramNode.getSsaDefinition().getARead() = guard and - guard.controlsBlock(n.getControlFlowNode().getBasicBlock(), bs, _) - ) - } - - pragma[nomagic] - private predicate commonSubTypeGeneral(DataFlowTypeOrUnifiable t1, DataFlowType t2) { + private predicate commonSubTypeGeneral(DataFlowTypeOrUnifiable t1, RelevantDataFlowType t2) { not t1 instanceof Gvn::TypeParameterGvnType and t1 = t2 or @@ -899,102 +747,53 @@ private module Cached { * `t2` are allowed to be type parameters. */ cached - predicate commonSubType(DataFlowType t1, DataFlowType t2) { commonSubTypeGeneral(t1, t2) } + predicate commonSubType(RelevantDataFlowType t1, RelevantDataFlowType t2) { + commonSubTypeGeneral(t1, t2) + } cached - predicate commonSubTypeUnifiableLeft(DataFlowType t1, DataFlowType t2) { + predicate commonSubTypeUnifiableLeft(RelevantDataFlowType t1, RelevantDataFlowType t2) { exists(Gvn::GvnType t | Gvn::unifiable(t1, t) and commonSubTypeGeneral(t, t2) ) } - - cached - predicate outRefReturnNode(Ssa::ExplicitDefinition def, OutRefReturnKind kind) { - exists(Parameter p | - def.isLiveOutRefParameterDefinition(p) and - kind.getPosition() = p.getPosition() - | - p.isOut() and kind instanceof OutReturnKind - or - p.isRef() and kind instanceof RefReturnKind - ) - } - - cached - predicate summaryOutNodeCached(DataFlowCall c, Node out, ReturnKind rk) { - FlowSummaryImpl::Private::summaryOutNode(c, out, rk) - } - - cached - predicate summaryArgumentNodeCached(DataFlowCall c, Node arg, int i) { - FlowSummaryImpl::Private::summaryArgumentNode(c, arg, i) - } - - cached - predicate summaryPostUpdateNodeCached(Node post, ParameterNode pre) { - FlowSummaryImpl::Private::summaryPostUpdateNode(post, pre) - } - - cached - predicate summaryReturnNodeCached(Node ret, ReturnKind rk) { - FlowSummaryImpl::Private::summaryReturnNode(ret, rk) and - not rk instanceof JumpReturnKind - } - - cached - predicate castNode(Node n) { - n.asExpr() instanceof Cast - or - n.(AssignableDefinitionNode).getDefinition() instanceof AssignableDefinitions::PatternDefinition - } - - /** Holds if `n` should be hidden from path explanations. */ - cached - predicate nodeIsHidden(Node n) { - exists(Ssa::Definition def | def = n.(SsaDefinitionNode).getDefinition() | - def instanceof Ssa::PhiNode - or - def instanceof Ssa::ImplicitEntryDefinition - or - def instanceof Ssa::ImplicitCallDefinition - ) - or - exists(Parameter p | - p = n.(ParameterNode).getParameter() and - not p.fromSource() - ) - or - n = TInstanceParameterNode(any(Callable c | not c.fromSource())) - or - n instanceof YieldReturnNode - or - n instanceof AsyncReturnNode - or - n instanceof ImplicitCapturedArgumentNode - or - n instanceof MallocNode - or - n instanceof SummaryNode - or - n instanceof ParamsArgumentNode - or - n.asExpr() = any(WithExpr we).getInitializer() - } - - cached - predicate parameterNode(Node n, DataFlowCallable c, int i) { - n.(ParameterNodeImpl).isParameterOf(c, i) - } - - cached - predicate argumentNode(Node n, DataFlowCall call, int pos) { - n.(ArgumentNodeImpl).argumentOf(call, pos) - } } import Cached +/** Holds if `n` should be hidden from path explanations. */ +predicate nodeIsHidden(Node n) { + exists(Ssa::Definition def | def = n.(SsaDefinitionNode).getDefinition() | + def instanceof Ssa::PhiNode + or + def instanceof Ssa::ImplicitEntryDefinition + or + def instanceof Ssa::ImplicitCallDefinition + ) + or + exists(Parameter p | + p = n.(ParameterNode).getParameter() and + not p.fromSource() + ) + or + n = TInstanceParameterNode(any(Callable c | not c.fromSource())) + or + n instanceof YieldReturnNode + or + n instanceof AsyncReturnNode + or + n instanceof ImplicitCapturedArgumentNode + or + n instanceof MallocNode + or + n instanceof SummaryNode + or + n instanceof ParamsArgumentNode + or + n.asExpr() = any(WithExpr we).getInitializer() +} + /** An SSA definition, viewed as a node in a data flow graph. */ class SsaDefinitionNode extends NodeImpl, TSsaDefinitionNode { Ssa::Definition def; @@ -1142,10 +941,12 @@ import ParameterNodes /** A data-flow node that represents a call argument. */ class ArgumentNode extends Node { - ArgumentNode() { argumentNode(this, _, _) } + ArgumentNode() { this instanceof ArgumentNodeImpl } /** Holds if this argument occurs at the given position in the given call. */ - final predicate argumentOf(DataFlowCall call, int pos) { argumentNode(this, call, pos) } + final predicate argumentOf(DataFlowCall call, int pos) { + this.(ArgumentNodeImpl).argumentOf(call, pos) + } } abstract private class ArgumentNodeImpl extends Node { @@ -1310,14 +1111,10 @@ private module ArgumentNodes { } private class SummaryArgumentNode extends SummaryNode, ArgumentNodeImpl { - private DataFlowCall c; - private int i; - - SummaryArgumentNode() { summaryArgumentNodeCached(c, this, i) } + SummaryArgumentNode() { FlowSummaryImpl::Private::summaryArgumentNode(_, this, _) } override predicate argumentOf(DataFlowCall call, int pos) { - call = c and - i = pos + FlowSummaryImpl::Private::summaryArgumentNode(call, this, pos) } } } @@ -1352,7 +1149,16 @@ private module ReturnNodes { class OutRefReturnNode extends ReturnNode, SsaDefinitionNode { OutRefReturnKind kind; - OutRefReturnNode() { outRefReturnNode(this.getDefinition(), kind) } + OutRefReturnNode() { + exists(Parameter p | + this.getDefinition().isLiveOutRefParameterDefinition(p) and + kind.getPosition() = p.getPosition() + | + p.isOut() and kind instanceof OutReturnKind + or + p.isRef() and kind instanceof RefReturnKind + ) + } override ReturnKind getKind() { result = kind } } @@ -1449,7 +1255,10 @@ private module ReturnNodes { private class SummaryReturnNode extends SummaryNode, ReturnNode { private ReturnKind rk; - SummaryReturnNode() { summaryReturnNodeCached(this, rk) } + SummaryReturnNode() { + FlowSummaryImpl::Private::summaryReturnNode(this, rk) and + not rk instanceof JumpReturnKind + } override ReturnKind getKind() { result = rk } } @@ -1460,7 +1269,6 @@ import ReturnNodes /** A data-flow node that represents the output of a call. */ abstract class OutNode extends Node { /** Gets the underlying call, where this node is a corresponding output of kind `kind`. */ - cached abstract DataFlowCall getCall(ReturnKind kind); } @@ -1488,7 +1296,6 @@ private module OutNodes { } override DataFlowCall getCall(ReturnKind kind) { - Stages::DataFlowStage::forceCachingInSameStage() and result = call and ( kind instanceof NormalReturnKind and @@ -1564,14 +1371,10 @@ private module OutNodes { } private class SummaryOutNode extends SummaryNode, OutNode { - private DataFlowCall c; - private ReturnKind rk; - - SummaryOutNode() { summaryOutNodeCached(c, this, rk) } + SummaryOutNode() { FlowSummaryImpl::Private::summaryOutNode(_, this, _) } override DataFlowCall getCall(ReturnKind kind) { - result = c and - kind = rk + FlowSummaryImpl::Private::summaryOutNode(result, this, kind) } } } @@ -1654,7 +1457,29 @@ private class FieldOrPropertyRead extends FieldOrPropertyAccess, AssignableRead } } -predicate jumpStep = jumpStepImpl/2; +/** + * Holds if `pred` can flow to `succ`, by jumping from one callable to + * another. Additional steps specified by the configuration are *not* + * taken into account. + */ +predicate jumpStep(Node pred, Node succ) { + pred.(NonLocalJumpNode).getAJumpSuccessor(true) = succ + or + exists(FieldOrProperty fl, FieldOrPropertyRead flr | + fl.isStatic() and + fl.isFieldLike() and + fl.getAnAssignedValue() = pred.asExpr() and + fl.getAnAccess() = flr and + flr = succ.asExpr() and + flr.hasNonlocalValue() + ) + or + exists(JumpReturnKind jrk, DataFlowCall call | + FlowSummaryImpl::Private::summaryReturnNode(pred, jrk) and + viableCallable(call) = jrk.getTarget() and + succ = getAnOutNode(call, jrk.getTargetReturnKind()) + ) +} private class StoreStepConfiguration extends ControlFlowReachabilityConfiguration { StoreStepConfiguration() { this = "StoreStepConfiguration" } @@ -1675,7 +1500,46 @@ private class StoreStepConfiguration extends ControlFlowReachabilityConfiguratio } } -predicate storeStep = storeStepImpl/3; +pragma[nomagic] +private PropertyContent getResultContent() { + result.getProperty() = any(SystemThreadingTasksTaskTClass c_).getResultProperty() +} + +/** + * Holds if data can flow from `node1` to `node2` via an assignment to + * content `c`. + */ +predicate storeStep(Node node1, Content c, Node node2) { + exists(StoreStepConfiguration x, ExprNode node, boolean postUpdate | + hasNodePath(x, node1, node) and + if postUpdate = true then node = node2.(PostUpdateNode).getPreUpdateNode() else node = node2 + | + fieldOrPropertyStore(_, c, node1.asExpr(), node.getExpr(), postUpdate) + or + arrayStore(_, node1.asExpr(), node.getExpr(), postUpdate) and c instanceof ElementContent + ) + or + exists(StoreStepConfiguration x, Expr arg, ControlFlow::Node callCfn | + x.hasExprPath(arg, node1.(ExprNode).getControlFlowNode(), _, callCfn) and + node2 = TParamsArgumentNode(callCfn) and + isParamsArg(_, arg, _) and + c instanceof ElementContent + ) + or + exists(Expr e | + e = node1.asExpr() and + node2.(YieldReturnNode).getYieldReturnStmt().getExpr() = e and + c instanceof ElementContent + ) + or + exists(Expr e | + e = node1.asExpr() and + node2.(AsyncReturnNode).getExpr() = e and + c = getResultContent() + ) + or + FlowSummaryImpl::Private::Steps::summaryStoreStep(node1, c, node2) +} private class ReadStepConfiguration extends ControlFlowReachabilityConfiguration { ReadStepConfiguration() { this = "ReadStepConfiguration" } @@ -1742,7 +1606,99 @@ private class ReadStepConfiguration extends ControlFlowReachabilityConfiguration } } -predicate readStep = readStepImpl/3; +/** + * Holds if data can flow from `node1` to `node2` via a read of content `c`. + */ +predicate readStep(Node node1, Content c, Node node2) { + exists(ReadStepConfiguration x | + hasNodePath(x, node1, node2) and + fieldOrPropertyRead(node1.asExpr(), c, node2.asExpr()) + or + hasNodePath(x, node1, node2) and + arrayRead(node1.asExpr(), node2.asExpr()) and + c instanceof ElementContent + or + exists(ForeachStmt fs, Ssa::ExplicitDefinition def | + x.hasDefPath(fs.getIterableExpr(), node1.getControlFlowNode(), def.getADefinition(), + def.getControlFlowNode()) and + node2.(SsaDefinitionNode).getDefinition() = def and + c instanceof ElementContent + ) + or + hasNodePath(x, node1, node2) and + node2.asExpr().(AwaitExpr).getExpr() = node1.asExpr() and + c = getResultContent() + or + // node1 = (..., node2, ...) + // node1.ItemX flows to node2 + exists(TupleExpr te, int i, Expr item | + te = node1.asExpr() and + not te.isConstruction() and + c.(FieldContent).getField() = te.getType().(TupleType).getElement(i).getUnboundDeclaration() and + // node1 = (..., item, ...) + te.getArgument(i) = item + | + // item = (..., ..., ...) in node1 = (..., (..., ..., ...), ...) + node2.asExpr().(TupleExpr) = item and + hasNodePath(x, node1, node2) + or + // item = variable in node1 = (..., variable, ...) + exists(AssignableDefinitions::TupleAssignmentDefinition tad, Ssa::ExplicitDefinition def | + node2.(SsaDefinitionNode).getDefinition() = def and + def.getADefinition() = tad and + tad.getLeaf() = item and + hasNodePath(x, node1, node2) + ) + or + // item = variable in node1 = (..., variable, ...) in a case/is var (..., ...) + te = any(PatternExpr pe).getAChildExpr*() and + exists(AssignableDefinitions::LocalVariableDefinition lvd, Ssa::ExplicitDefinition def | + node2.(SsaDefinitionNode).getDefinition() = def and + def.getADefinition() = lvd and + lvd.getDeclaration() = item and + hasNodePath(x, node1, node2) + ) + ) + ) + or + FlowSummaryImpl::Private::Steps::summaryReadStep(node1, c, node2) +} + +/** + * Holds if values stored inside content `c` are cleared at node `n`. For example, + * any value stored inside `f` is cleared at the pre-update node associated with `x` + * in `x.f = newValue`. + */ +predicate clearsContent(Node n, Content c) { + fieldOrPropertyStore(_, c, _, n.asExpr(), true) + or + fieldOrPropertyStore(_, c, _, n.(ObjectInitializerNode).getInitializer(), false) + or + FlowSummaryImpl::Private::Steps::summaryStoresIntoArg(c, n) and + not c instanceof ElementContent + or + FlowSummaryImpl::Private::Steps::summaryClearsContent(n, c) + or + exists(WithExpr we, ObjectInitializer oi, FieldOrProperty f | + oi = we.getInitializer() and + n.asExpr() = oi and + f = oi.getAMemberInitializer().getInitializedMember() and + c = f.getContent() + ) +} + +/** + * Holds if the node `n` is unreachable when the call context is `call`. + */ +predicate isUnreachableInCall(Node n, DataFlowCall call) { + exists( + ExplicitParameterNode paramNode, Guard guard, ControlFlow::SuccessorTypes::BooleanSuccessor bs + | + viableConstantBooleanParamArg(paramNode, bs.getValue().booleanNot(), call) and + paramNode.getSsaDefinition().getARead() = guard and + guard.controlsBlock(n.getControlFlowNode().getBasicBlock(), bs, _) + ) +} /** * An entity used to represent the type of data-flow node. Two nodes will have @@ -1753,10 +1709,7 @@ predicate readStep = readStepImpl/3; * `DataFlowType`, while `Func` and `Func` are not, because * `string` is not a type parameter. */ -class DataFlowType extends Gvn::GvnType { - pragma[nomagic] - DataFlowType() { this = any(NodeImpl n).getDataFlowType() } -} +class DataFlowType = Gvn::GvnType; /** Gets the type of `n` used for type pruning. */ pragma[inline] @@ -1888,11 +1841,11 @@ private module PostUpdateNodes { } private class SummaryPostUpdateNode extends SummaryNode, PostUpdateNode { - private Node pre; + SummaryPostUpdateNode() { FlowSummaryImpl::Private::summaryPostUpdateNode(this, _) } - SummaryPostUpdateNode() { summaryPostUpdateNodeCached(this, pre) } - - override Node getPreUpdateNode() { result = pre } + override Node getPreUpdateNode() { + FlowSummaryImpl::Private::summaryPostUpdateNode(this, result) + } } } @@ -1900,7 +1853,12 @@ private import PostUpdateNodes /** A node that performs a type cast. */ class CastNode extends Node { - CastNode() { castNode(this) } + CastNode() { + this.asExpr() instanceof Cast + or + this.(AssignableDefinitionNode).getDefinition() instanceof + AssignableDefinitions::PatternDefinition + } } class DataFlowExpr = DotNet::Expr; diff --git a/csharp/ql/src/semmle/code/csharp/dataflow/internal/DataFlowPublic.qll b/csharp/ql/src/semmle/code/csharp/dataflow/internal/DataFlowPublic.qll index 12a62faf387..bfc2f5469d0 100644 --- a/csharp/ql/src/semmle/code/csharp/dataflow/internal/DataFlowPublic.qll +++ b/csharp/ql/src/semmle/code/csharp/dataflow/internal/DataFlowPublic.qll @@ -67,6 +67,8 @@ class Node extends TNode { } } +private class TExprNode_ = TExprNode or TCilExprNode; + /** * An expression, viewed as a node in a data flow graph. * @@ -74,9 +76,7 @@ class Node extends TNode { * to multiple `ExprNode`s, just like it may correspond to multiple * `ControlFlow::Node`s. */ -class ExprNode extends Node { - ExprNode() { this = TExprNode(_) or this = TCilExprNode(_) } - +class ExprNode extends Node, TExprNode_ { /** Gets the expression corresponding to this node. */ DotNet::Expr getExpr() { result = this.getExprAtNode(_) @@ -99,7 +99,7 @@ class ExprNode extends Node { * flow graph. */ class ParameterNode extends Node { - ParameterNode() { parameterNode(this, _, _) } + ParameterNode() { this instanceof ParameterNodeImpl } /** Gets the parameter corresponding to this node, if any. */ DotNet::Parameter getParameter() { @@ -110,7 +110,9 @@ class ParameterNode extends Node { * Holds if this node is the parameter of callable `c` at the specified * (zero-based) position. */ - predicate isParameterOf(DataFlowCallable c, int i) { parameterNode(this, c, i) } + predicate isParameterOf(DataFlowCallable c, int i) { + this.(ParameterNodeImpl).isParameterOf(c, i) + } } /** A definition, viewed as a node in a data flow graph. */ diff --git a/csharp/ql/src/semmle/code/csharp/dataflow/internal/TaintTrackingPrivate.qll b/csharp/ql/src/semmle/code/csharp/dataflow/internal/TaintTrackingPrivate.qll index 72525d3234a..462838abcd1 100755 --- a/csharp/ql/src/semmle/code/csharp/dataflow/internal/TaintTrackingPrivate.qll +++ b/csharp/ql/src/semmle/code/csharp/dataflow/internal/TaintTrackingPrivate.qll @@ -1,6 +1,5 @@ private import csharp private import TaintTrackingPublic -private import DataFlowImplCommon private import FlowSummaryImpl as FlowSummaryImpl private import semmle.code.csharp.Caching private import semmle.code.csharp.dataflow.internal.DataFlowPrivate @@ -79,7 +78,6 @@ private class LocalTaintExprStepConfiguration extends ControlFlowReachabilityCon } private predicate localTaintStepCommon(DataFlow::Node nodeFrom, DataFlow::Node nodeTo) { - Stages::DataFlowStage::forceCachingInSameStage() and hasNodePath(any(LocalTaintExprStepConfiguration x), nodeFrom, nodeTo) or localTaintStepCil(nodeFrom, nodeTo) @@ -87,6 +85,11 @@ private predicate localTaintStepCommon(DataFlow::Node nodeFrom, DataFlow::Node n cached private module Cached { + private import DataFlowImplCommon as DataFlowImplCommon + + cached + predicate forceCachingInSameStage() { DataFlowImplCommon::forceCachingInSameStage() } + /** * Holds if taint propagates from `nodeFrom` to `nodeTo` in exactly one local * (intra-procedural) step. From bd0a196a397cd30d92f45b8d7e5db5dbfa2733c3 Mon Sep 17 00:00:00 2001 From: Tom Hvitved Date: Mon, 26 Apr 2021 20:19:43 +0200 Subject: [PATCH 020/168] Java: Update data-flow caching --- .../java/dataflow/internal/DataFlowNodes.qll | 109 +++++++----------- .../dataflow/internal/DataFlowPrivate.qll | 1 - .../java/dataflow/internal/DataFlowUtil.qll | 2 + .../dataflow/internal/TaintTrackingUtil.qll | 108 +++++++++-------- 4 files changed, 107 insertions(+), 113 deletions(-) diff --git a/java/ql/src/semmle/code/java/dataflow/internal/DataFlowNodes.qll b/java/ql/src/semmle/code/java/dataflow/internal/DataFlowNodes.qll index 552849c2c34..c47efa287ad 100644 --- a/java/ql/src/semmle/code/java/dataflow/internal/DataFlowNodes.qll +++ b/java/ql/src/semmle/code/java/dataflow/internal/DataFlowNodes.qll @@ -4,71 +4,48 @@ private import semmle.code.java.dataflow.FlowSummary private import semmle.code.java.dataflow.TypeFlow private import DataFlowPrivate private import FlowSummaryImpl as FlowSummaryImpl +private import DataFlowImplCommon as DataFlowImplCommon cached -private module Cached { - cached - newtype TNode = - TExprNode(Expr e) { - not e.getType() instanceof VoidType and - not e.getParent*() instanceof Annotation - } or - TExplicitParameterNode(Parameter p) { - exists(p.getCallable().getBody()) or p.getCallable() instanceof SummarizedCallable - } or - TImplicitVarargsArray(Call c) { - c.getCallee().isVarargs() and - not exists(Argument arg | arg.getCall() = c and arg.isExplicitVarargsArray()) - } or - TInstanceParameterNode(Callable c) { - (exists(c.getBody()) or c instanceof SummarizedCallable) and - not c.isStatic() - } or - TImplicitInstanceAccess(InstanceAccessExt ia) { not ia.isExplicit(_) } or - TMallocNode(ClassInstanceExpr cie) or - TExplicitExprPostUpdate(Expr e) { - explicitInstanceArgument(_, e) - or - e instanceof Argument and not e.getType() instanceof ImmutableType - or - exists(FieldAccess fa | fa.getField() instanceof InstanceField and e = fa.getQualifier()) - or - exists(ArrayAccess aa | e = aa.getArray()) - } or - TImplicitExprPostUpdate(InstanceAccessExt ia) { - implicitInstanceArgument(_, ia) - or - exists(FieldAccess fa | - fa.getField() instanceof InstanceField and ia.isImplicitFieldQualifier(fa) - ) - } or - TSummaryInternalNode(SummarizedCallable c, FlowSummaryImpl::Private::SummaryNodeState state) { - FlowSummaryImpl::Private::summaryNodeRange(c, state) - } - - cached - predicate summaryOutNodeCached(DataFlowCall c, Node out) { - FlowSummaryImpl::Private::summaryOutNode(c, out, _) +newtype TNode = + TExprNode(Expr e) { + DataFlowImplCommon::forceCachingInSameStage() and + not e.getType() instanceof VoidType and + not e.getParent*() instanceof Annotation + } or + TExplicitParameterNode(Parameter p) { + exists(p.getCallable().getBody()) or p.getCallable() instanceof SummarizedCallable + } or + TImplicitVarargsArray(Call c) { + c.getCallee().isVarargs() and + not exists(Argument arg | arg.getCall() = c and arg.isExplicitVarargsArray()) + } or + TInstanceParameterNode(Callable c) { + (exists(c.getBody()) or c instanceof SummarizedCallable) and + not c.isStatic() + } or + TImplicitInstanceAccess(InstanceAccessExt ia) { not ia.isExplicit(_) } or + TMallocNode(ClassInstanceExpr cie) or + TExplicitExprPostUpdate(Expr e) { + explicitInstanceArgument(_, e) + or + e instanceof Argument and not e.getType() instanceof ImmutableType + or + exists(FieldAccess fa | fa.getField() instanceof InstanceField and e = fa.getQualifier()) + or + exists(ArrayAccess aa | e = aa.getArray()) + } or + TImplicitExprPostUpdate(InstanceAccessExt ia) { + implicitInstanceArgument(_, ia) + or + exists(FieldAccess fa | + fa.getField() instanceof InstanceField and ia.isImplicitFieldQualifier(fa) + ) + } or + TSummaryInternalNode(SummarizedCallable c, FlowSummaryImpl::Private::SummaryNodeState state) { + FlowSummaryImpl::Private::summaryNodeRange(c, state) } - cached - predicate summaryArgumentNodeCached(DataFlowCall c, Node arg, int i) { - FlowSummaryImpl::Private::summaryArgumentNode(c, arg, i) - } - - cached - predicate summaryPostUpdateNodeCached(Node post, ParameterNode pre) { - FlowSummaryImpl::Private::summaryPostUpdateNode(post, pre) - } - - cached - predicate summaryReturnNodeCached(Node ret) { - FlowSummaryImpl::Private::summaryReturnNode(ret, _) - } -} - -private import Cached - private predicate explicitInstanceArgument(Call call, Expr instarg) { call instanceof MethodAccess and instarg = call.getQualifier() and @@ -404,13 +381,15 @@ module Private { override string toString() { result = "[summary] " + state + " in " + c } /** Holds if this summary node is the `i`th argument of `call`. */ - predicate isArgumentOf(DataFlowCall call, int i) { summaryArgumentNodeCached(call, this, i) } + predicate isArgumentOf(DataFlowCall call, int i) { + FlowSummaryImpl::Private::summaryArgumentNode(call, this, i) + } /** Holds if this summary node is a return node. */ - predicate isReturn() { summaryReturnNodeCached(this) } + predicate isReturn() { FlowSummaryImpl::Private::summaryReturnNode(this, _) } /** Holds if this summary node is an out node for `call`. */ - predicate isOut(DataFlowCall call) { summaryOutNodeCached(call, this) } + predicate isOut(DataFlowCall call) { FlowSummaryImpl::Private::summaryOutNode(call, this, _) } } SummaryNode getSummaryNode(SummarizedCallable c, FlowSummaryImpl::Private::SummaryNodeState state) { @@ -439,7 +418,7 @@ private class MallocNode extends Node, TMallocNode { private class SummaryPostUpdateNode extends SummaryNode, PostUpdateNode { private Node pre; - SummaryPostUpdateNode() { summaryPostUpdateNodeCached(this, pre) } + SummaryPostUpdateNode() { FlowSummaryImpl::Private::summaryPostUpdateNode(this, pre) } override Node getPreUpdateNode() { result = pre } } diff --git a/java/ql/src/semmle/code/java/dataflow/internal/DataFlowPrivate.qll b/java/ql/src/semmle/code/java/dataflow/internal/DataFlowPrivate.qll index 23d84c10c9f..5756c56ba6d 100644 --- a/java/ql/src/semmle/code/java/dataflow/internal/DataFlowPrivate.qll +++ b/java/ql/src/semmle/code/java/dataflow/internal/DataFlowPrivate.qll @@ -284,7 +284,6 @@ private class ConstantBooleanArgumentNode extends ArgumentNode, ExprNode { /** * Holds if the node `n` is unreachable when the call context is `call`. */ -cached predicate isUnreachableInCall(Node n, DataFlowCall call) { exists( ExplicitParameterNode paramNode, ConstantBooleanArgumentNode arg, SsaImplicitInit param, diff --git a/java/ql/src/semmle/code/java/dataflow/internal/DataFlowUtil.qll b/java/ql/src/semmle/code/java/dataflow/internal/DataFlowUtil.qll index 2ad09574015..e25ab24cfbb 100644 --- a/java/ql/src/semmle/code/java/dataflow/internal/DataFlowUtil.qll +++ b/java/ql/src/semmle/code/java/dataflow/internal/DataFlowUtil.qll @@ -11,6 +11,7 @@ private import semmle.code.java.dataflow.FlowSteps private import semmle.code.java.dataflow.FlowSummary import semmle.code.java.dataflow.InstanceAccess private import FlowSummaryImpl as FlowSummaryImpl +private import TaintTrackingUtil as TaintTrackingUtil import DataFlowNodes::Public /** Holds if `n` is an access to an unqualified `this` at `cfgnode`. */ @@ -112,6 +113,7 @@ predicate localFlowStep(Node node1, Node node2) { */ cached predicate simpleLocalFlowStep(Node node1, Node node2) { + TaintTrackingUtil::forceCachingInSameStage() and // Variable flow steps through adjacent def-use and use-use pairs. exists(SsaExplicitUpdate upd | upd.getDefiningExpr().(VariableAssign).getSource() = node1.asExpr() or diff --git a/java/ql/src/semmle/code/java/dataflow/internal/TaintTrackingUtil.qll b/java/ql/src/semmle/code/java/dataflow/internal/TaintTrackingUtil.qll index f9278ab815e..8c514e357d2 100644 --- a/java/ql/src/semmle/code/java/dataflow/internal/TaintTrackingUtil.qll +++ b/java/ql/src/semmle/code/java/dataflow/internal/TaintTrackingUtil.qll @@ -29,55 +29,69 @@ predicate localExprTaint(Expr src, Expr sink) { localTaint(DataFlow::exprNode(src), DataFlow::exprNode(sink)) } -/** - * Holds if taint can flow in one local step from `src` to `sink`. - */ -predicate localTaintStep(DataFlow::Node src, DataFlow::Node sink) { - DataFlow::localFlowStep(src, sink) or - localAdditionalTaintStep(src, sink) or - // Simple flow through library code is included in the exposed local - // step relation, even though flow is technically inter-procedural - FlowSummaryImpl::Private::Steps::summaryThroughStep(src, sink, false) +cached +private module Cached { + private import DataFlowImplCommon as DataFlowImplCommon + + cached + predicate forceCachingInSameStage() { DataFlowImplCommon::forceCachingInSameStage() } + + /** + * Holds if taint can flow in one local step from `src` to `sink`. + */ + cached + predicate localTaintStep(DataFlow::Node src, DataFlow::Node sink) { + DataFlow::localFlowStep(src, sink) or + localAdditionalTaintStep(src, sink) or + // Simple flow through library code is included in the exposed local + // step relation, even though flow is technically inter-procedural + FlowSummaryImpl::Private::Steps::summaryThroughStep(src, sink, false) + } + + /** + * Holds if taint can flow in one local step from `src` to `sink` excluding + * local data flow steps. That is, `src` and `sink` are likely to represent + * different objects. + */ + cached + predicate localAdditionalTaintStep(DataFlow::Node src, DataFlow::Node sink) { + localAdditionalTaintExprStep(src.asExpr(), sink.asExpr()) + or + localAdditionalTaintUpdateStep(src.asExpr(), + sink.(DataFlow::PostUpdateNode).getPreUpdateNode().asExpr()) + or + exists(Argument arg | + src.asExpr() = arg and + arg.isVararg() and + sink.(DataFlow::ImplicitVarargsArray).getCall() = arg.getCall() + ) + or + FlowSummaryImpl::Private::Steps::summaryLocalStep(src, sink, false) + } + + /** + * Holds if the additional step from `src` to `sink` should be included in all + * global taint flow configurations. + */ + cached + predicate defaultAdditionalTaintStep(DataFlow::Node src, DataFlow::Node sink) { + localAdditionalTaintStep(src, sink) or + any(AdditionalTaintStep a).step(src, sink) + } + + /** + * Holds if `node` should be a sanitizer in all global taint flow configurations + * but not in local taint. + */ + cached + predicate defaultTaintSanitizer(DataFlow::Node node) { + // Ignore paths through test code. + node.getEnclosingCallable().getDeclaringType() instanceof NonSecurityTestClass or + node.asExpr() instanceof ValidatedVariableAccess + } } -/** - * Holds if taint can flow in one local step from `src` to `sink` excluding - * local data flow steps. That is, `src` and `sink` are likely to represent - * different objects. - */ -predicate localAdditionalTaintStep(DataFlow::Node src, DataFlow::Node sink) { - localAdditionalTaintExprStep(src.asExpr(), sink.asExpr()) - or - localAdditionalTaintUpdateStep(src.asExpr(), - sink.(DataFlow::PostUpdateNode).getPreUpdateNode().asExpr()) - or - exists(Argument arg | - src.asExpr() = arg and - arg.isVararg() and - sink.(DataFlow::ImplicitVarargsArray).getCall() = arg.getCall() - ) - or - FlowSummaryImpl::Private::Steps::summaryLocalStep(src, sink, false) -} - -/** - * Holds if the additional step from `src` to `sink` should be included in all - * global taint flow configurations. - */ -predicate defaultAdditionalTaintStep(DataFlow::Node src, DataFlow::Node sink) { - localAdditionalTaintStep(src, sink) or - any(AdditionalTaintStep a).step(src, sink) -} - -/** - * Holds if `node` should be a sanitizer in all global taint flow configurations - * but not in local taint. - */ -predicate defaultTaintSanitizer(DataFlow::Node node) { - // Ignore paths through test code. - node.getEnclosingCallable().getDeclaringType() instanceof NonSecurityTestClass or - node.asExpr() instanceof ValidatedVariableAccess -} +import Cached /** * Holds if taint can flow in one local step from `src` to `sink` excluding From 7adc3c2fba0ef298fba66f767404ca55ad5ccda7 Mon Sep 17 00:00:00 2001 From: jorgectf Date: Thu, 18 Mar 2021 17:25:10 +0100 Subject: [PATCH 021/168] Upload ReDoS query, qhelp and tests --- python/ql/src/Security/CWE-400/RegexDoS.qhelp | 27 ++++++ python/ql/src/Security/CWE-400/RegexDoS.ql | 82 +++++++++++++++++++ .../ql/src/Security/CWE-400/tests/re_bad.py | 22 +++++ .../ql/src/Security/CWE-400/tests/re_good.py | 22 +++++ 4 files changed, 153 insertions(+) create mode 100644 python/ql/src/Security/CWE-400/RegexDoS.qhelp create mode 100644 python/ql/src/Security/CWE-400/RegexDoS.ql create mode 100644 python/ql/src/Security/CWE-400/tests/re_bad.py create mode 100644 python/ql/src/Security/CWE-400/tests/re_good.py diff --git a/python/ql/src/Security/CWE-400/RegexDoS.qhelp b/python/ql/src/Security/CWE-400/RegexDoS.qhelp new file mode 100644 index 00000000000..de564c83347 --- /dev/null +++ b/python/ql/src/Security/CWE-400/RegexDoS.qhelp @@ -0,0 +1,27 @@ + + + + +

    If aregular expression is built by a not escaped user-provided value, a user is likely to be able to cause a Denial of Service.

    +
    + + +

    In case user input must compose a regular expression, it should be escaped with functions such as re.escape. + + + +

  • + OWASP + Regular Expression DoS +
  • +
  • + SonarSource + RSPEC-2631 +
  • +
  • + CWE- + 400 +
  • + + +
    \ No newline at end of file diff --git a/python/ql/src/Security/CWE-400/RegexDoS.ql b/python/ql/src/Security/CWE-400/RegexDoS.ql new file mode 100644 index 00000000000..fbdca996e55 --- /dev/null +++ b/python/ql/src/Security/CWE-400/RegexDoS.ql @@ -0,0 +1,82 @@ +/** + * @name Python Regex DoS + * @description Python Regular Expression Denial of Service + * @kind path-problem + * @problem.severity error + * @id python/regex-dos + * @tags experimental + * security + * external/cwe/cwe-400 + */ + +import python +import semmle.python.dataflow.new.RemoteFlowSources +import semmle.python.dataflow.new.DataFlow +import semmle.python.dataflow.new.TaintTracking +import semmle.python.dataflow.new.internal.TaintTrackingPublic +import DataFlow::PathGraph + +class ReMethods extends string { + ReMethods() { + this = "match" or + this = "fullmatch" or + this = "search" or + this = "split" or + this = "findall" or + this = "finditer" + } +} + +class DirectRegex extends DataFlow::Node { + DirectRegex() { + exists(string reMethod, CallNode reCall | + reMethod instanceof ReMethods and + reCall = Value::named("re." + reMethod).getACall() and + this.asExpr() = reCall.getArg(0).getNode() + ) + } +} + +class CompiledRegex extends DataFlow::Node { + CompiledRegex() { + exists(CallNode patternCall, SsaVariable patternVar, CallNode reMethodCall | + patternCall = Value::named("re.compile").getACall() and + patternVar.getDefinition().getImmediateDominator() = patternCall and + patternVar.getAUse().getNode() = reMethodCall.getNode().getFunc().(Attribute).getObject() and + reMethodCall.getNode().getFunc().(Attribute).getName() instanceof ReMethods and + this.asExpr() = patternCall.getArg(0).getNode() + ) + } +} + +class RegexDoSSink extends DataFlow::Node { + RegexDoSSink() { this instanceof DirectRegex or this instanceof CompiledRegex } +} + +class EscapeSanitizer extends DataFlow::Node { + EscapeSanitizer() { + exists(Call c | + ( + // avoid flow through any %escape% function + c.getFunc().(Attribute).getName().matches("%escape%") or // something.%escape%() + c.getFunc().(Name).getId().matches("%escape%") // %escape%() + ) and + this.asExpr() = c + ) + } +} + +class RegexDoSFlowConfig extends TaintTracking::Configuration { + RegexDoSFlowConfig() { this = "RegexDoSFlowConfig" } + + override predicate isSource(DataFlow::Node source) { source instanceof RemoteFlowSource } + + override predicate isSink(DataFlow::Node sink) { sink instanceof RegexDoSSink } + + override predicate isSanitizer(DataFlow::Node sanitizer) { sanitizer instanceof EscapeSanitizer } +} + +from RegexDoSFlowConfig config, DataFlow::PathNode source, DataFlow::PathNode sink +where config.hasFlowPath(source, sink) +select sink.getNode(), source, sink, "$@ regex operation includes $@.", sink.getNode(), "This", + source.getNode(), "a user-provided value" diff --git a/python/ql/src/Security/CWE-400/tests/re_bad.py b/python/ql/src/Security/CWE-400/tests/re_bad.py new file mode 100644 index 00000000000..6a0aa7c6614 --- /dev/null +++ b/python/ql/src/Security/CWE-400/tests/re_bad.py @@ -0,0 +1,22 @@ +from flask import request, Flask +import re + +app = Flask(__name__) + + +@app.route("/direct") +def direct(): + pattern = request.args['pattern'] + + re.search(pattern, "") + + +@app.route("/compile") +def compile(): + pattern = re.compile(request.args['pattern']) + + pattern.search("") + + +# if __name__ == "__main__": +# app.run(debug=True) diff --git a/python/ql/src/Security/CWE-400/tests/re_good.py b/python/ql/src/Security/CWE-400/tests/re_good.py new file mode 100644 index 00000000000..ba09b1fe30b --- /dev/null +++ b/python/ql/src/Security/CWE-400/tests/re_good.py @@ -0,0 +1,22 @@ +from flask import request, Flask +import re + +app = Flask(__name__) + + +@app.route("/direct") +def direct(): + pattern = re.escape(request.args['pattern']) + + re.search(pattern, "") + + +@app.route("/compile") +def compile(): + pattern = re.compile(re.escape(request.args['pattern'])) + + pattern.search("") + + +# if __name__ == "__main__": +# app.run(debug=True) From bd3d2ec686ad9686180f8d6ec94d74c4703f2c1f Mon Sep 17 00:00:00 2001 From: jorgectf Date: Thu, 18 Mar 2021 19:44:23 +0100 Subject: [PATCH 022/168] Update to match consistent naming across languages --- .../{CWE-400 => CWE-730}/RegexDoS.qhelp | 4 +++ .../Security/{CWE-400 => CWE-730}/RegexDoS.ql | 30 +++++++++++-------- .../{CWE-400 => CWE-730}/tests/re_bad.py | 0 .../{CWE-400 => CWE-730}/tests/re_good.py | 0 4 files changed, 21 insertions(+), 13 deletions(-) rename python/ql/src/Security/{CWE-400 => CWE-730}/RegexDoS.qhelp (88%) rename python/ql/src/Security/{CWE-400 => CWE-730}/RegexDoS.ql (66%) rename python/ql/src/Security/{CWE-400 => CWE-730}/tests/re_bad.py (100%) rename python/ql/src/Security/{CWE-400 => CWE-730}/tests/re_good.py (100%) diff --git a/python/ql/src/Security/CWE-400/RegexDoS.qhelp b/python/ql/src/Security/CWE-730/RegexDoS.qhelp similarity index 88% rename from python/ql/src/Security/CWE-400/RegexDoS.qhelp rename to python/ql/src/Security/CWE-730/RegexDoS.qhelp index de564c83347..22f328e852b 100644 --- a/python/ql/src/Security/CWE-400/RegexDoS.qhelp +++ b/python/ql/src/Security/CWE-730/RegexDoS.qhelp @@ -22,6 +22,10 @@ CWE- 400 +
  • + CWE- + 730 +
  • \ No newline at end of file diff --git a/python/ql/src/Security/CWE-400/RegexDoS.ql b/python/ql/src/Security/CWE-730/RegexDoS.ql similarity index 66% rename from python/ql/src/Security/CWE-400/RegexDoS.ql rename to python/ql/src/Security/CWE-730/RegexDoS.ql index fbdca996e55..79905fe6921 100644 --- a/python/ql/src/Security/CWE-400/RegexDoS.ql +++ b/python/ql/src/Security/CWE-730/RegexDoS.ql @@ -1,14 +1,17 @@ /** - * @name Python Regex DoS - * @description Python Regular Expression Denial of Service + * @name Regular expression injection + * @description User input should not be used in regular expressions without first being escaped, + * otherwise a malicious user may be able to inject an expression that could require + * exponential time on certain inputs. * @kind path-problem * @problem.severity error - * @id python/regex-dos - * @tags experimental - * security + * @id python/regex-injection + * @tags security + * external/cwe/cwe-730 * external/cwe/cwe-400 */ +// determine precision above import python import semmle.python.dataflow.new.RemoteFlowSources import semmle.python.dataflow.new.DataFlow @@ -16,6 +19,7 @@ import semmle.python.dataflow.new.TaintTracking import semmle.python.dataflow.new.internal.TaintTrackingPublic import DataFlow::PathGraph +// Should this be moved to a different structure? (For other queries to be able to use it) class ReMethods extends string { ReMethods() { this = "match" or @@ -49,8 +53,8 @@ class CompiledRegex extends DataFlow::Node { } } -class RegexDoSSink extends DataFlow::Node { - RegexDoSSink() { this instanceof DirectRegex or this instanceof CompiledRegex } +class RegexInjectionSink extends DataFlow::Node { + RegexInjectionSink() { this instanceof DirectRegex or this instanceof CompiledRegex } } class EscapeSanitizer extends DataFlow::Node { @@ -66,17 +70,17 @@ class EscapeSanitizer extends DataFlow::Node { } } -class RegexDoSFlowConfig extends TaintTracking::Configuration { - RegexDoSFlowConfig() { this = "RegexDoSFlowConfig" } +class RegexInjectionFlowConfig extends TaintTracking::Configuration { + RegexInjectionFlowConfig() { this = "RegexInjectionFlowConfig" } override predicate isSource(DataFlow::Node source) { source instanceof RemoteFlowSource } - override predicate isSink(DataFlow::Node sink) { sink instanceof RegexDoSSink } + override predicate isSink(DataFlow::Node sink) { sink instanceof RegexInjectionSink } override predicate isSanitizer(DataFlow::Node sanitizer) { sanitizer instanceof EscapeSanitizer } } -from RegexDoSFlowConfig config, DataFlow::PathNode source, DataFlow::PathNode sink +from RegexInjectionFlowConfig config, DataFlow::PathNode source, DataFlow::PathNode sink where config.hasFlowPath(source, sink) -select sink.getNode(), source, sink, "$@ regex operation includes $@.", sink.getNode(), "This", - source.getNode(), "a user-provided value" +select sink.getNode(), source, sink, "$@ regular expression is constructed from a $@.", + sink.getNode(), "This", source.getNode(), "user-provided value" diff --git a/python/ql/src/Security/CWE-400/tests/re_bad.py b/python/ql/src/Security/CWE-730/tests/re_bad.py similarity index 100% rename from python/ql/src/Security/CWE-400/tests/re_bad.py rename to python/ql/src/Security/CWE-730/tests/re_bad.py diff --git a/python/ql/src/Security/CWE-400/tests/re_good.py b/python/ql/src/Security/CWE-730/tests/re_good.py similarity index 100% rename from python/ql/src/Security/CWE-400/tests/re_good.py rename to python/ql/src/Security/CWE-730/tests/re_good.py From afc4f51e9c29bfef762f372b732157d583bdaeac Mon Sep 17 00:00:00 2001 From: jorgectf Date: Thu, 18 Mar 2021 20:10:18 +0100 Subject: [PATCH 023/168] Remove CWE references --- python/ql/src/Security/CWE-730/RegexDoS.qhelp | 8 -------- 1 file changed, 8 deletions(-) diff --git a/python/ql/src/Security/CWE-730/RegexDoS.qhelp b/python/ql/src/Security/CWE-730/RegexDoS.qhelp index 22f328e852b..002cbcef0a8 100644 --- a/python/ql/src/Security/CWE-730/RegexDoS.qhelp +++ b/python/ql/src/Security/CWE-730/RegexDoS.qhelp @@ -18,14 +18,6 @@ SonarSource RSPEC-2631 -
  • - CWE- - 400 -
  • -
  • - CWE- - 730 -
  • \ No newline at end of file From 21f8135fa6824f42bdf643d48c34b1a712cbe715 Mon Sep 17 00:00:00 2001 From: jorgectf Date: Thu, 18 Mar 2021 20:19:08 +0100 Subject: [PATCH 024/168] Move to experimental folder --- python/ql/src/{ => experimental}/Security/CWE-730/RegexDoS.qhelp | 0 python/ql/src/{ => experimental}/Security/CWE-730/RegexDoS.ql | 0 python/ql/src/{ => experimental}/Security/CWE-730/tests/re_bad.py | 0 .../ql/src/{ => experimental}/Security/CWE-730/tests/re_good.py | 0 4 files changed, 0 insertions(+), 0 deletions(-) rename python/ql/src/{ => experimental}/Security/CWE-730/RegexDoS.qhelp (100%) rename python/ql/src/{ => experimental}/Security/CWE-730/RegexDoS.ql (100%) rename python/ql/src/{ => experimental}/Security/CWE-730/tests/re_bad.py (100%) rename python/ql/src/{ => experimental}/Security/CWE-730/tests/re_good.py (100%) diff --git a/python/ql/src/Security/CWE-730/RegexDoS.qhelp b/python/ql/src/experimental/Security/CWE-730/RegexDoS.qhelp similarity index 100% rename from python/ql/src/Security/CWE-730/RegexDoS.qhelp rename to python/ql/src/experimental/Security/CWE-730/RegexDoS.qhelp diff --git a/python/ql/src/Security/CWE-730/RegexDoS.ql b/python/ql/src/experimental/Security/CWE-730/RegexDoS.ql similarity index 100% rename from python/ql/src/Security/CWE-730/RegexDoS.ql rename to python/ql/src/experimental/Security/CWE-730/RegexDoS.ql diff --git a/python/ql/src/Security/CWE-730/tests/re_bad.py b/python/ql/src/experimental/Security/CWE-730/tests/re_bad.py similarity index 100% rename from python/ql/src/Security/CWE-730/tests/re_bad.py rename to python/ql/src/experimental/Security/CWE-730/tests/re_bad.py diff --git a/python/ql/src/Security/CWE-730/tests/re_good.py b/python/ql/src/experimental/Security/CWE-730/tests/re_good.py similarity index 100% rename from python/ql/src/Security/CWE-730/tests/re_good.py rename to python/ql/src/experimental/Security/CWE-730/tests/re_good.py From 6cc714464c519dd59bbc4456f312ca6b1b10a984 Mon Sep 17 00:00:00 2001 From: Jorge <46056498+jorgectf@users.noreply.github.com> Date: Thu, 18 Mar 2021 22:42:49 +0100 Subject: [PATCH 025/168] Apply suggestions from code review Co-authored-by: yoff --- .../experimental/Security/CWE-730/RegexDoS.ql | 27 +++++++------------ 1 file changed, 9 insertions(+), 18 deletions(-) diff --git a/python/ql/src/experimental/Security/CWE-730/RegexDoS.ql b/python/ql/src/experimental/Security/CWE-730/RegexDoS.ql index 79905fe6921..b89835aad8a 100644 --- a/python/ql/src/experimental/Security/CWE-730/RegexDoS.ql +++ b/python/ql/src/experimental/Security/CWE-730/RegexDoS.ql @@ -21,34 +21,25 @@ import DataFlow::PathGraph // Should this be moved to a different structure? (For other queries to be able to use it) class ReMethods extends string { - ReMethods() { - this = "match" or - this = "fullmatch" or - this = "search" or - this = "split" or - this = "findall" or - this = "finditer" - } + ReMethods() { this in ["match", "fullmatch", "search", "split", "findall", "finditer"] }``` } class DirectRegex extends DataFlow::Node { DirectRegex() { - exists(string reMethod, CallNode reCall | - reMethod instanceof ReMethods and - reCall = Value::named("re." + reMethod).getACall() and - this.asExpr() = reCall.getArg(0).getNode() + exists(ReMethods reMethod, DataFlow::CallCfgNode reCall | + reCall = API::moduleImport("re").getMember(reMethod).getACall() and + this = reCall.getArg(0) ) } } class CompiledRegex extends DataFlow::Node { CompiledRegex() { - exists(CallNode patternCall, SsaVariable patternVar, CallNode reMethodCall | - patternCall = Value::named("re.compile").getACall() and - patternVar.getDefinition().getImmediateDominator() = patternCall and - patternVar.getAUse().getNode() = reMethodCall.getNode().getFunc().(Attribute).getObject() and - reMethodCall.getNode().getFunc().(Attribute).getName() instanceof ReMethods and - this.asExpr() = patternCall.getArg(0).getNode() + exists(DataFlow::CallCfgNode patternCall, AttrRead reMethod | + patternCall = API::moduleImport("re").getMember("compile").getACall() and + patternCall = reMethod.getObject().getALocalSource() and + reMethod.getAttributeName() instanceof ReMethods and + this = patternCall.getArg(0) ) } } From 63f708dd57e53bd3f4cb0c73aa865384933a1789 Mon Sep 17 00:00:00 2001 From: jorgectf Date: Thu, 18 Mar 2021 23:03:33 +0100 Subject: [PATCH 026/168] Apply suggestions --- python/ql/src/experimental/Security/CWE-730/RegexDoS.ql | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/python/ql/src/experimental/Security/CWE-730/RegexDoS.ql b/python/ql/src/experimental/Security/CWE-730/RegexDoS.ql index b89835aad8a..d35b596c045 100644 --- a/python/ql/src/experimental/Security/CWE-730/RegexDoS.ql +++ b/python/ql/src/experimental/Security/CWE-730/RegexDoS.ql @@ -17,11 +17,12 @@ import semmle.python.dataflow.new.RemoteFlowSources import semmle.python.dataflow.new.DataFlow import semmle.python.dataflow.new.TaintTracking import semmle.python.dataflow.new.internal.TaintTrackingPublic +import semmle.python.ApiGraphs import DataFlow::PathGraph // Should this be moved to a different structure? (For other queries to be able to use it) class ReMethods extends string { - ReMethods() { this in ["match", "fullmatch", "search", "split", "findall", "finditer"] }``` + ReMethods() { this in ["match", "fullmatch", "search", "split", "findall", "finditer"] } } class DirectRegex extends DataFlow::Node { @@ -35,7 +36,7 @@ class DirectRegex extends DataFlow::Node { class CompiledRegex extends DataFlow::Node { CompiledRegex() { - exists(DataFlow::CallCfgNode patternCall, AttrRead reMethod | + exists(DataFlow::CallCfgNode patternCall, DataFlow::AttrRead reMethod | patternCall = API::moduleImport("re").getMember("compile").getACall() and patternCall = reMethod.getObject().getALocalSource() and reMethod.getAttributeName() instanceof ReMethods and From 5dae9207831dbe30365235ef03e9c28357172668 Mon Sep 17 00:00:00 2001 From: jorgectf Date: Thu, 18 Mar 2021 23:04:47 +0100 Subject: [PATCH 027/168] Edit filenames to match consistent naming --- .../Security/CWE-730/{RegexDoS.qhelp => RegexInjection.qhelp} | 0 .../Security/CWE-730/{RegexDoS.ql => RegexInjection.ql} | 0 2 files changed, 0 insertions(+), 0 deletions(-) rename python/ql/src/experimental/Security/CWE-730/{RegexDoS.qhelp => RegexInjection.qhelp} (100%) rename python/ql/src/experimental/Security/CWE-730/{RegexDoS.ql => RegexInjection.ql} (100%) diff --git a/python/ql/src/experimental/Security/CWE-730/RegexDoS.qhelp b/python/ql/src/experimental/Security/CWE-730/RegexInjection.qhelp similarity index 100% rename from python/ql/src/experimental/Security/CWE-730/RegexDoS.qhelp rename to python/ql/src/experimental/Security/CWE-730/RegexInjection.qhelp diff --git a/python/ql/src/experimental/Security/CWE-730/RegexDoS.ql b/python/ql/src/experimental/Security/CWE-730/RegexInjection.ql similarity index 100% rename from python/ql/src/experimental/Security/CWE-730/RegexDoS.ql rename to python/ql/src/experimental/Security/CWE-730/RegexInjection.ql From f45307f9906e581a527710eb5b6a1bf224a36c56 Mon Sep 17 00:00:00 2001 From: jorgectf Date: Thu, 18 Mar 2021 23:33:10 +0100 Subject: [PATCH 028/168] Apply rebase --- .../Security/CWE-730/RegexInjection.qhelp | 0 .../ql/src/Security/CWE-730/RegexInjection.ql | 22 ++++++ .../tests => Security/CWE-730}/re_bad.py | 0 .../tests => Security/CWE-730}/re_good.py | 0 .../Security/CWE-730/RegexInjection.ql | 78 ------------------- python/ql/src/semmle/python/Concepts.qll | 43 ++++++++++ .../security/dataflow/RegexInjection.qll | 23 ++++++ 7 files changed, 88 insertions(+), 78 deletions(-) rename python/ql/src/{experimental => }/Security/CWE-730/RegexInjection.qhelp (100%) create mode 100644 python/ql/src/Security/CWE-730/RegexInjection.ql rename python/ql/src/{experimental/Security/CWE-730/tests => Security/CWE-730}/re_bad.py (100%) rename python/ql/src/{experimental/Security/CWE-730/tests => Security/CWE-730}/re_good.py (100%) delete mode 100644 python/ql/src/experimental/Security/CWE-730/RegexInjection.ql create mode 100644 python/ql/src/semmle/python/security/dataflow/RegexInjection.qll diff --git a/python/ql/src/experimental/Security/CWE-730/RegexInjection.qhelp b/python/ql/src/Security/CWE-730/RegexInjection.qhelp similarity index 100% rename from python/ql/src/experimental/Security/CWE-730/RegexInjection.qhelp rename to python/ql/src/Security/CWE-730/RegexInjection.qhelp diff --git a/python/ql/src/Security/CWE-730/RegexInjection.ql b/python/ql/src/Security/CWE-730/RegexInjection.ql new file mode 100644 index 00000000000..27bbe69529d --- /dev/null +++ b/python/ql/src/Security/CWE-730/RegexInjection.ql @@ -0,0 +1,22 @@ +/** + * @name Regular expression injection + * @description User input should not be used in regular expressions without first being escaped, + * otherwise a malicious user may be able to inject an expression that could require + * exponential time on certain inputs. + * @kind path-problem + * @problem.severity error + * @id python/regex-injection + * @tags security + * external/cwe/cwe-730 + * external/cwe/cwe-400 + */ + +// determine precision above +import python +import semmle.python.security.dataflow.RegexInjection +import DataFlow::PathGraph + +from RegexInjectionFlowConfig config, DataFlow::PathNode source, DataFlow::PathNode sink +where config.hasFlowPath(source, sink) +select sink.getNode(), source, sink, "$@ regular expression is constructed from a $@.", + sink.getNode(), "This", source.getNode(), "user-provided value" diff --git a/python/ql/src/experimental/Security/CWE-730/tests/re_bad.py b/python/ql/src/Security/CWE-730/re_bad.py similarity index 100% rename from python/ql/src/experimental/Security/CWE-730/tests/re_bad.py rename to python/ql/src/Security/CWE-730/re_bad.py diff --git a/python/ql/src/experimental/Security/CWE-730/tests/re_good.py b/python/ql/src/Security/CWE-730/re_good.py similarity index 100% rename from python/ql/src/experimental/Security/CWE-730/tests/re_good.py rename to python/ql/src/Security/CWE-730/re_good.py diff --git a/python/ql/src/experimental/Security/CWE-730/RegexInjection.ql b/python/ql/src/experimental/Security/CWE-730/RegexInjection.ql deleted file mode 100644 index d35b596c045..00000000000 --- a/python/ql/src/experimental/Security/CWE-730/RegexInjection.ql +++ /dev/null @@ -1,78 +0,0 @@ -/** - * @name Regular expression injection - * @description User input should not be used in regular expressions without first being escaped, - * otherwise a malicious user may be able to inject an expression that could require - * exponential time on certain inputs. - * @kind path-problem - * @problem.severity error - * @id python/regex-injection - * @tags security - * external/cwe/cwe-730 - * external/cwe/cwe-400 - */ - -// determine precision above -import python -import semmle.python.dataflow.new.RemoteFlowSources -import semmle.python.dataflow.new.DataFlow -import semmle.python.dataflow.new.TaintTracking -import semmle.python.dataflow.new.internal.TaintTrackingPublic -import semmle.python.ApiGraphs -import DataFlow::PathGraph - -// Should this be moved to a different structure? (For other queries to be able to use it) -class ReMethods extends string { - ReMethods() { this in ["match", "fullmatch", "search", "split", "findall", "finditer"] } -} - -class DirectRegex extends DataFlow::Node { - DirectRegex() { - exists(ReMethods reMethod, DataFlow::CallCfgNode reCall | - reCall = API::moduleImport("re").getMember(reMethod).getACall() and - this = reCall.getArg(0) - ) - } -} - -class CompiledRegex extends DataFlow::Node { - CompiledRegex() { - exists(DataFlow::CallCfgNode patternCall, DataFlow::AttrRead reMethod | - patternCall = API::moduleImport("re").getMember("compile").getACall() and - patternCall = reMethod.getObject().getALocalSource() and - reMethod.getAttributeName() instanceof ReMethods and - this = patternCall.getArg(0) - ) - } -} - -class RegexInjectionSink extends DataFlow::Node { - RegexInjectionSink() { this instanceof DirectRegex or this instanceof CompiledRegex } -} - -class EscapeSanitizer extends DataFlow::Node { - EscapeSanitizer() { - exists(Call c | - ( - // avoid flow through any %escape% function - c.getFunc().(Attribute).getName().matches("%escape%") or // something.%escape%() - c.getFunc().(Name).getId().matches("%escape%") // %escape%() - ) and - this.asExpr() = c - ) - } -} - -class RegexInjectionFlowConfig extends TaintTracking::Configuration { - RegexInjectionFlowConfig() { this = "RegexInjectionFlowConfig" } - - override predicate isSource(DataFlow::Node source) { source instanceof RemoteFlowSource } - - override predicate isSink(DataFlow::Node sink) { sink instanceof RegexInjectionSink } - - override predicate isSanitizer(DataFlow::Node sanitizer) { sanitizer instanceof EscapeSanitizer } -} - -from RegexInjectionFlowConfig config, DataFlow::PathNode source, DataFlow::PathNode sink -where config.hasFlowPath(source, sink) -select sink.getNode(), source, sink, "$@ regular expression is constructed from a $@.", - sink.getNode(), "This", source.getNode(), "user-provided value" diff --git a/python/ql/src/semmle/python/Concepts.qll b/python/ql/src/semmle/python/Concepts.qll index 4be00ba5bf9..f58f6317e1e 100644 --- a/python/ql/src/semmle/python/Concepts.qll +++ b/python/ql/src/semmle/python/Concepts.qll @@ -9,6 +9,7 @@ private import semmle.python.dataflow.new.DataFlow private import semmle.python.dataflow.new.RemoteFlowSources private import semmle.python.dataflow.new.TaintTracking private import semmle.python.Frameworks +private import semmle.python.ApiGraphs /** * A data-flow node that executes an operating system command, @@ -625,5 +626,47 @@ module Cryptography { final override int minimumSecureKeySize() { result = 224 } } } +/* + */ + +class ReMethods extends string { + ReMethods() { this in ["match", "fullmatch", "search", "split", "findall", "finditer"] } +} + +class DirectRegex extends DataFlow::Node { + DirectRegex() { + exists(ReMethods reMethod, DataFlow::CallCfgNode reCall | + reCall = API::moduleImport("re").getMember(reMethod).getACall() and + this = reCall.getArg(0) + ) + } +} + +class CompiledRegex extends DataFlow::Node { + CompiledRegex() { + exists(DataFlow::CallCfgNode patternCall, DataFlow::AttrRead reMethod | + patternCall = API::moduleImport("re").getMember("compile").getACall() and + patternCall = reMethod.getObject().getALocalSource() and + reMethod.getAttributeName() instanceof ReMethods and + this = patternCall.getArg(0) + ) + } +} + +class RegexExecution extends DataFlow::Node { + RegexExecution() { this instanceof DirectRegex or this instanceof CompiledRegex } +} + +// pending refactor if needed +class RegexEscape extends DataFlow::Node { + RegexEscape() { + exists(Call c | + ( + // avoid flow through any %escape% function + c.getFunc().(Attribute).getName().matches("%escape%") or // something.%escape%() + c.getFunc().(Name).getId().matches("%escape%") // %escape%() + ) and + this.asExpr() = c + ) } } diff --git a/python/ql/src/semmle/python/security/dataflow/RegexInjection.qll b/python/ql/src/semmle/python/security/dataflow/RegexInjection.qll new file mode 100644 index 00000000000..5a8f386a781 --- /dev/null +++ b/python/ql/src/semmle/python/security/dataflow/RegexInjection.qll @@ -0,0 +1,23 @@ +/** + * Provides a taint-tracking configuration for detecting regular expression injections + * vulnerabilities. + */ + +import python +import semmle.python.Concepts +import semmle.python.dataflow.new.DataFlow +import semmle.python.dataflow.new.TaintTracking +import semmle.python.dataflow.new.RemoteFlowSources + +/** + * A taint-tracking configuration for detecting regular expression injections. + */ +class RegexInjectionFlowConfig extends TaintTracking::Configuration { + RegexInjectionFlowConfig() { this = "RegexInjectionFlowConfig" } + + override predicate isSource(DataFlow::Node source) { source instanceof RemoteFlowSource } + + override predicate isSink(DataFlow::Node sink) { sink instanceof RegexExecution } + + override predicate isSanitizer(DataFlow::Node sanitizer) { sanitizer instanceof RegexEscape } +} From e4736d064e525ca51cdb52df1fedcdb54b0b8b04 Mon Sep 17 00:00:00 2001 From: jorgectf Date: Thu, 18 Mar 2021 23:36:54 +0100 Subject: [PATCH 029/168] Typo --- .../ql/src/semmle/python/security/dataflow/RegexInjection.qll | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/python/ql/src/semmle/python/security/dataflow/RegexInjection.qll b/python/ql/src/semmle/python/security/dataflow/RegexInjection.qll index 5a8f386a781..968010dd9a0 100644 --- a/python/ql/src/semmle/python/security/dataflow/RegexInjection.qll +++ b/python/ql/src/semmle/python/security/dataflow/RegexInjection.qll @@ -1,5 +1,5 @@ /** - * Provides a taint-tracking configuration for detecting regular expression injections + * Provides a taint-tracking configuration for detecting regular expression injection * vulnerabilities. */ From a1b5cc3bc61860e50449617a8c79a9bfa975b3bd Mon Sep 17 00:00:00 2001 From: jorgectf Date: Fri, 19 Mar 2021 01:26:21 +0100 Subject: [PATCH 030/168] Typo --- python/ql/src/Security/CWE-730/RegexInjection.qhelp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/python/ql/src/Security/CWE-730/RegexInjection.qhelp b/python/ql/src/Security/CWE-730/RegexInjection.qhelp index 002cbcef0a8..2e3d238daa4 100644 --- a/python/ql/src/Security/CWE-730/RegexInjection.qhelp +++ b/python/ql/src/Security/CWE-730/RegexInjection.qhelp @@ -2,7 +2,7 @@ -

    If aregular expression is built by a not escaped user-provided value, a user is likely to be able to cause a Denial of Service.

    +

    If a regular expression is built by a not escaped user-provided value, a user is likely to be able to cause a Denial of Service.

    From 6d5a0f2f842b1f7a459a2e8e5d54c67fc86516c7 Mon Sep 17 00:00:00 2001 From: jorgectf Date: Fri, 19 Mar 2021 01:34:13 +0100 Subject: [PATCH 031/168] Limit Sanitizer to re.escape(arg) --- python/ql/src/semmle/python/Concepts.qll | 10 +--------- 1 file changed, 1 insertion(+), 9 deletions(-) diff --git a/python/ql/src/semmle/python/Concepts.qll b/python/ql/src/semmle/python/Concepts.qll index f58f6317e1e..60880794845 100644 --- a/python/ql/src/semmle/python/Concepts.qll +++ b/python/ql/src/semmle/python/Concepts.qll @@ -657,16 +657,8 @@ class RegexExecution extends DataFlow::Node { RegexExecution() { this instanceof DirectRegex or this instanceof CompiledRegex } } -// pending refactor if needed class RegexEscape extends DataFlow::Node { RegexEscape() { - exists(Call c | - ( - // avoid flow through any %escape% function - c.getFunc().(Attribute).getName().matches("%escape%") or // something.%escape%() - c.getFunc().(Name).getId().matches("%escape%") // %escape%() - ) and - this.asExpr() = c - ) + this = API::moduleImport("re").getMember("escape").getACall().(DataFlow::CallCfgNode).getArg(0) } } From caaf5436c6a4ed8a32c0b4f43a1418052a4b7ba5 Mon Sep 17 00:00:00 2001 From: jorgectf Date: Fri, 19 Mar 2021 01:51:59 +0100 Subject: [PATCH 032/168] Attempt to restructuring ReMethods and RegexExecution's modules --- python/ql/src/semmle/python/Concepts.qll | 2 +- .../src/semmle/python/frameworks/Stdlib.qll | 30 +++++++++++++++++++ 2 files changed, 31 insertions(+), 1 deletion(-) diff --git a/python/ql/src/semmle/python/Concepts.qll b/python/ql/src/semmle/python/Concepts.qll index 60880794845..751f9f0a302 100644 --- a/python/ql/src/semmle/python/Concepts.qll +++ b/python/ql/src/semmle/python/Concepts.qll @@ -654,7 +654,7 @@ class CompiledRegex extends DataFlow::Node { } class RegexExecution extends DataFlow::Node { - RegexExecution() { this instanceof DirectRegex or this instanceof CompiledRegex } + RegexExecution() { this instanceof DirectRegex or this instanceof CompiledRegex } // How should this be cross-imported with Stdlib? } class RegexEscape extends DataFlow::Node { diff --git a/python/ql/src/semmle/python/frameworks/Stdlib.qll b/python/ql/src/semmle/python/frameworks/Stdlib.qll index 01df130d0db..c074e6fe1cd 100644 --- a/python/ql/src/semmle/python/frameworks/Stdlib.qll +++ b/python/ql/src/semmle/python/frameworks/Stdlib.qll @@ -864,6 +864,36 @@ private module Stdlib { class Sqlite3 extends PEP249ModuleApiNode { Sqlite3() { this = API::moduleImport("sqlite3") } } + + // --------------------------------------------------------------------------- + // re + // --------------------------------------------------------------------------- + /** List of re methods. */ + private class ReMethods extends string { + ReMethods() { this in ["match", "fullmatch", "search", "split", "findall", "finditer"] } + } + + /** re.ReMethod(pattern, string) */ + private class DirectRegex extends DataFlow::Node { + DirectRegex() { + exists(ReMethods reMethod, DataFlow::CallCfgNode reCall | + reCall = API::moduleImport("re").getMember(reMethod).getACall() and + this = reCall.getArg(0) + ) + } + } + + /** re.compile(pattern).ReMethod */ + class CompiledRegex extends DataFlow::Node { + CompiledRegex() { + exists(DataFlow::CallCfgNode patternCall, DataFlow::AttrRead reMethod | + patternCall = API::moduleImport("re").getMember("compile").getACall() and + patternCall = reMethod.getObject().getALocalSource() and + reMethod.getAttributeName() instanceof ReMethods and + this = patternCall.getArg(0) + ) + } + } } // --------------------------------------------------------------------------- From 3daec8e6a27a0776220ca52fe28e184717e1fc50 Mon Sep 17 00:00:00 2001 From: jorgectf Date: Fri, 19 Mar 2021 17:46:32 +0100 Subject: [PATCH 033/168] Enclose Sinks and ReMethods in a module --- .../src/semmle/python/frameworks/Stdlib.qll | 46 ++++++++++--------- 1 file changed, 24 insertions(+), 22 deletions(-) diff --git a/python/ql/src/semmle/python/frameworks/Stdlib.qll b/python/ql/src/semmle/python/frameworks/Stdlib.qll index c074e6fe1cd..f9949560d68 100644 --- a/python/ql/src/semmle/python/frameworks/Stdlib.qll +++ b/python/ql/src/semmle/python/frameworks/Stdlib.qll @@ -868,30 +868,32 @@ private module Stdlib { // --------------------------------------------------------------------------- // re // --------------------------------------------------------------------------- - /** List of re methods. */ - private class ReMethods extends string { - ReMethods() { this in ["match", "fullmatch", "search", "split", "findall", "finditer"] } - } - - /** re.ReMethod(pattern, string) */ - private class DirectRegex extends DataFlow::Node { - DirectRegex() { - exists(ReMethods reMethod, DataFlow::CallCfgNode reCall | - reCall = API::moduleImport("re").getMember(reMethod).getACall() and - this = reCall.getArg(0) - ) + private module Re { + /** List of re methods. */ + private class ReMethods extends string { + ReMethods() { this in ["match", "fullmatch", "search", "split", "findall", "finditer"] } } - } - /** re.compile(pattern).ReMethod */ - class CompiledRegex extends DataFlow::Node { - CompiledRegex() { - exists(DataFlow::CallCfgNode patternCall, DataFlow::AttrRead reMethod | - patternCall = API::moduleImport("re").getMember("compile").getACall() and - patternCall = reMethod.getObject().getALocalSource() and - reMethod.getAttributeName() instanceof ReMethods and - this = patternCall.getArg(0) - ) + /** re.ReMethod(pattern, string) */ + private class DirectRegex extends DataFlow::Node { + DirectRegex() { + exists(ReMethods reMethod, DataFlow::CallCfgNode reCall | + reCall = API::moduleImport("re").getMember(reMethod).getACall() and + this = reCall.getArg(0) + ) + } + } + + /** re.compile(pattern).ReMethod */ + class CompiledRegex extends DataFlow::Node { + CompiledRegex() { + exists(DataFlow::CallCfgNode patternCall, DataFlow::AttrRead reMethod | + patternCall = API::moduleImport("re").getMember("compile").getACall() and + patternCall = reMethod.getObject().getALocalSource() and + reMethod.getAttributeName() instanceof ReMethods and + this = patternCall.getArg(0) + ) + } } } } From b207929e0ad280fec73fffb7d4c5d193e01f3e34 Mon Sep 17 00:00:00 2001 From: jorgectf Date: Fri, 19 Mar 2021 18:07:39 +0100 Subject: [PATCH 034/168] RegexExecution restructuring --- python/ql/src/semmle/python/Concepts.qll | 18 +++++++++++++++--- .../ql/src/semmle/python/frameworks/Stdlib.qll | 9 ++++++++- 2 files changed, 23 insertions(+), 4 deletions(-) diff --git a/python/ql/src/semmle/python/Concepts.qll b/python/ql/src/semmle/python/Concepts.qll index 751f9f0a302..aa0c748c1da 100644 --- a/python/ql/src/semmle/python/Concepts.qll +++ b/python/ql/src/semmle/python/Concepts.qll @@ -656,9 +656,21 @@ class CompiledRegex extends DataFlow::Node { class RegexExecution extends DataFlow::Node { RegexExecution() { this instanceof DirectRegex or this instanceof CompiledRegex } // How should this be cross-imported with Stdlib? } +/* + */ -class RegexEscape extends DataFlow::Node { - RegexEscape() { - this = API::moduleImport("re").getMember("escape").getACall().(DataFlow::CallCfgNode).getArg(0) +module RegexExecution { + abstract class Range extends DataFlow::Node { + DataFlow::Node getRegexNode() { + result instanceof DirectRegex or result instanceof CompiledRegex + } } } + +class RegexExecution extends DataFlow::Node { + override RegexExecution::Range range; + + RegexExecution() { this = range } + + DataFlow::Node getRegexNode() { result = range.getRegexNode() } +} diff --git a/python/ql/src/semmle/python/frameworks/Stdlib.qll b/python/ql/src/semmle/python/frameworks/Stdlib.qll index f9949560d68..ed5a56be931 100644 --- a/python/ql/src/semmle/python/frameworks/Stdlib.qll +++ b/python/ql/src/semmle/python/frameworks/Stdlib.qll @@ -885,7 +885,7 @@ private module Stdlib { } /** re.compile(pattern).ReMethod */ - class CompiledRegex extends DataFlow::Node { + private class CompiledRegex extends DataFlow::Node { CompiledRegex() { exists(DataFlow::CallCfgNode patternCall, DataFlow::AttrRead reMethod | patternCall = API::moduleImport("re").getMember("compile").getACall() and @@ -895,6 +895,13 @@ private module Stdlib { ) } } + + private class RegexEscape extends Concepts::RegexExecution { + RegexEscape() { + this = + API::moduleImport("re").getMember("escape").getACall().(DataFlow::CallCfgNode).getArg(0) + } + } } } From 249e4097e3a2faaa7b10914ba01b99a39d2c8f65 Mon Sep 17 00:00:00 2001 From: Jorge <46056498+jorgectf@users.noreply.github.com> Date: Mon, 22 Mar 2021 15:12:12 +0100 Subject: [PATCH 035/168] Change query ID Co-authored-by: Rasmus Wriedt Larsen --- python/ql/src/Security/CWE-730/RegexInjection.ql | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/python/ql/src/Security/CWE-730/RegexInjection.ql b/python/ql/src/Security/CWE-730/RegexInjection.ql index 27bbe69529d..3baa3b54bea 100644 --- a/python/ql/src/Security/CWE-730/RegexInjection.ql +++ b/python/ql/src/Security/CWE-730/RegexInjection.ql @@ -5,7 +5,7 @@ * exponential time on certain inputs. * @kind path-problem * @problem.severity error - * @id python/regex-injection + * @id py/regex-injection * @tags security * external/cwe/cwe-730 * external/cwe/cwe-400 From b27b77c38fda588586bd1ea3a0e942a5f4e403f8 Mon Sep 17 00:00:00 2001 From: Jorge <46056498+jorgectf@users.noreply.github.com> Date: Mon, 22 Mar 2021 15:30:54 +0100 Subject: [PATCH 036/168] Apply suggestions from code review Co-authored-by: yoff --- python/ql/src/semmle/python/Concepts.qll | 4 +--- python/ql/src/semmle/python/frameworks/Stdlib.qll | 6 +++--- 2 files changed, 4 insertions(+), 6 deletions(-) diff --git a/python/ql/src/semmle/python/Concepts.qll b/python/ql/src/semmle/python/Concepts.qll index aa0c748c1da..497ea0c0e31 100644 --- a/python/ql/src/semmle/python/Concepts.qll +++ b/python/ql/src/semmle/python/Concepts.qll @@ -661,9 +661,7 @@ class RegexExecution extends DataFlow::Node { module RegexExecution { abstract class Range extends DataFlow::Node { - DataFlow::Node getRegexNode() { - result instanceof DirectRegex or result instanceof CompiledRegex - } + abstract DataFlow::Node getRegexNode(); } } diff --git a/python/ql/src/semmle/python/frameworks/Stdlib.qll b/python/ql/src/semmle/python/frameworks/Stdlib.qll index ed5a56be931..c40ac53f8e4 100644 --- a/python/ql/src/semmle/python/frameworks/Stdlib.qll +++ b/python/ql/src/semmle/python/frameworks/Stdlib.qll @@ -875,7 +875,7 @@ private module Stdlib { } /** re.ReMethod(pattern, string) */ - private class DirectRegex extends DataFlow::Node { + private class DirectRegex extends RegexExecution::Range { DirectRegex() { exists(ReMethods reMethod, DataFlow::CallCfgNode reCall | reCall = API::moduleImport("re").getMember(reMethod).getACall() and @@ -885,7 +885,7 @@ private module Stdlib { } /** re.compile(pattern).ReMethod */ - private class CompiledRegex extends DataFlow::Node { + private class CompiledRegex extends RegexExecution::Range { CompiledRegex() { exists(DataFlow::CallCfgNode patternCall, DataFlow::AttrRead reMethod | patternCall = API::moduleImport("re").getMember("compile").getACall() and @@ -896,7 +896,7 @@ private module Stdlib { } } - private class RegexEscape extends Concepts::RegexExecution { + private class RegexEscape extends DataFlow::Node { RegexEscape() { this = API::moduleImport("re").getMember("escape").getACall().(DataFlow::CallCfgNode).getArg(0) From 0f20eeb395645b2185528b832a6f56ba039b0552 Mon Sep 17 00:00:00 2001 From: Jorge <46056498+jorgectf@users.noreply.github.com> Date: Wed, 24 Mar 2021 09:34:04 +0100 Subject: [PATCH 037/168] Apply suggestions Co-authored-by: yoff --- python/ql/src/semmle/python/Concepts.qll | 2 +- .../src/semmle/python/frameworks/Stdlib.qll | 21 ++++++++++++------- .../security/dataflow/RegexInjection.qll | 7 +++++-- 3 files changed, 20 insertions(+), 10 deletions(-) diff --git a/python/ql/src/semmle/python/Concepts.qll b/python/ql/src/semmle/python/Concepts.qll index 497ea0c0e31..d2defca27a1 100644 --- a/python/ql/src/semmle/python/Concepts.qll +++ b/python/ql/src/semmle/python/Concepts.qll @@ -666,7 +666,7 @@ module RegexExecution { } class RegexExecution extends DataFlow::Node { - override RegexExecution::Range range; + RegexExecution::Range range; RegexExecution() { this = range } diff --git a/python/ql/src/semmle/python/frameworks/Stdlib.qll b/python/ql/src/semmle/python/frameworks/Stdlib.qll index c40ac53f8e4..c86e4cf2e22 100644 --- a/python/ql/src/semmle/python/frameworks/Stdlib.qll +++ b/python/ql/src/semmle/python/frameworks/Stdlib.qll @@ -875,25 +875,32 @@ private module Stdlib { } /** re.ReMethod(pattern, string) */ - private class DirectRegex extends RegexExecution::Range { + private class DirectRegex extends DataFlow::CallCfgNode, RegexExecution::Range { + DataFlow::Node regexNode; + DirectRegex() { - exists(ReMethods reMethod, DataFlow::CallCfgNode reCall | - reCall = API::moduleImport("re").getMember(reMethod).getACall() and - this = reCall.getArg(0) - ) + this = API::moduleImport("re").getMember(any(ReMethods m)).getACall() and + regexNode = this.getArg(0) } + + override DataFlow::Node getRegexNode() { result = regexNode } } /** re.compile(pattern).ReMethod */ - private class CompiledRegex extends RegexExecution::Range { + private class CompiledRegex extends DataFlow::CallCfgNode, RegexExecution::Range { + DataFlow::Node regexNode; + CompiledRegex() { exists(DataFlow::CallCfgNode patternCall, DataFlow::AttrRead reMethod | + this.getFunction() = reMethod and patternCall = API::moduleImport("re").getMember("compile").getACall() and patternCall = reMethod.getObject().getALocalSource() and reMethod.getAttributeName() instanceof ReMethods and - this = patternCall.getArg(0) + regexNode = patternCall.getArg(0) ) } + + override DataFlow::Node getRegexNode() { result = regexNode } } private class RegexEscape extends DataFlow::Node { diff --git a/python/ql/src/semmle/python/security/dataflow/RegexInjection.qll b/python/ql/src/semmle/python/security/dataflow/RegexInjection.qll index 968010dd9a0..2758db37d65 100644 --- a/python/ql/src/semmle/python/security/dataflow/RegexInjection.qll +++ b/python/ql/src/semmle/python/security/dataflow/RegexInjection.qll @@ -17,7 +17,10 @@ class RegexInjectionFlowConfig extends TaintTracking::Configuration { override predicate isSource(DataFlow::Node source) { source instanceof RemoteFlowSource } - override predicate isSink(DataFlow::Node sink) { sink instanceof RegexExecution } + override predicate isSink(DataFlow::Node sink) { sink = any(RegexExecution re).getRegexNode() } - override predicate isSanitizer(DataFlow::Node sanitizer) { sanitizer instanceof RegexEscape } + override predicate isSanitizer(DataFlow::Node sanitizer) { + sanitizer = + API::moduleImport("re").getMember("escape").getACall().(DataFlow::CallCfgNode).getArg(0) + } } From 444a15a461ee8f99428650daa2ae6d95f7eb25b1 Mon Sep 17 00:00:00 2001 From: jorgectf Date: Wed, 24 Mar 2021 10:00:10 +0100 Subject: [PATCH 038/168] Polish imports --- python/ql/src/semmle/python/Concepts.qll | 1 - python/ql/src/semmle/python/security/dataflow/RegexInjection.qll | 1 + 2 files changed, 1 insertion(+), 1 deletion(-) diff --git a/python/ql/src/semmle/python/Concepts.qll b/python/ql/src/semmle/python/Concepts.qll index d2defca27a1..cc2e4ac3ca7 100644 --- a/python/ql/src/semmle/python/Concepts.qll +++ b/python/ql/src/semmle/python/Concepts.qll @@ -9,7 +9,6 @@ private import semmle.python.dataflow.new.DataFlow private import semmle.python.dataflow.new.RemoteFlowSources private import semmle.python.dataflow.new.TaintTracking private import semmle.python.Frameworks -private import semmle.python.ApiGraphs /** * A data-flow node that executes an operating system command, diff --git a/python/ql/src/semmle/python/security/dataflow/RegexInjection.qll b/python/ql/src/semmle/python/security/dataflow/RegexInjection.qll index 2758db37d65..98545816c82 100644 --- a/python/ql/src/semmle/python/security/dataflow/RegexInjection.qll +++ b/python/ql/src/semmle/python/security/dataflow/RegexInjection.qll @@ -8,6 +8,7 @@ import semmle.python.Concepts import semmle.python.dataflow.new.DataFlow import semmle.python.dataflow.new.TaintTracking import semmle.python.dataflow.new.RemoteFlowSources +import semmle.python.ApiGraphs /** * A taint-tracking configuration for detecting regular expression injections. From 28fdeba4fafc4ab0b7cff4cb1307fff3298440b8 Mon Sep 17 00:00:00 2001 From: jorgectf Date: Wed, 24 Mar 2021 17:59:48 +0100 Subject: [PATCH 039/168] Structure development --- .../Security/CWE-730/RegexInjection.qhelp | 0 .../Security/CWE-730/RegexInjection.ql | 4 +- .../Security/CWE-730/re_bad.py | 0 .../Security/CWE-730/re_good.py | 0 .../experimental/semmle/python/Concepts.qll | 29 +++ .../semmle/python/frameworks/Stdlib.qll | 232 +++++++++++++++++- .../security/injection}/RegexInjection.qll | 2 +- python/ql/src/semmle/python/Concepts.qll | 46 +--- .../src/semmle/python/frameworks/Stdlib.qll | 46 ---- 9 files changed, 264 insertions(+), 95 deletions(-) rename python/ql/src/{ => experimental}/Security/CWE-730/RegexInjection.qhelp (100%) rename python/ql/src/{ => experimental}/Security/CWE-730/RegexInjection.ql (92%) rename python/ql/src/{ => experimental}/Security/CWE-730/re_bad.py (100%) rename python/ql/src/{ => experimental}/Security/CWE-730/re_good.py (100%) rename python/ql/src/{semmle/python/security/dataflow => experimental/semmle/python/security/injection}/RegexInjection.qll (95%) diff --git a/python/ql/src/Security/CWE-730/RegexInjection.qhelp b/python/ql/src/experimental/Security/CWE-730/RegexInjection.qhelp similarity index 100% rename from python/ql/src/Security/CWE-730/RegexInjection.qhelp rename to python/ql/src/experimental/Security/CWE-730/RegexInjection.qhelp diff --git a/python/ql/src/Security/CWE-730/RegexInjection.ql b/python/ql/src/experimental/Security/CWE-730/RegexInjection.ql similarity index 92% rename from python/ql/src/Security/CWE-730/RegexInjection.ql rename to python/ql/src/experimental/Security/CWE-730/RegexInjection.ql index 3baa3b54bea..ccb97fbee54 100644 --- a/python/ql/src/Security/CWE-730/RegexInjection.ql +++ b/python/ql/src/experimental/Security/CWE-730/RegexInjection.ql @@ -13,10 +13,10 @@ // determine precision above import python -import semmle.python.security.dataflow.RegexInjection +import experimental.semmle.python.security.injection.RegexInjection import DataFlow::PathGraph from RegexInjectionFlowConfig config, DataFlow::PathNode source, DataFlow::PathNode sink where config.hasFlowPath(source, sink) select sink.getNode(), source, sink, "$@ regular expression is constructed from a $@.", - sink.getNode(), "This", source.getNode(), "user-provided value" + sink.getNode(), "This", source.getNode(), "user-provided value" \ No newline at end of file diff --git a/python/ql/src/Security/CWE-730/re_bad.py b/python/ql/src/experimental/Security/CWE-730/re_bad.py similarity index 100% rename from python/ql/src/Security/CWE-730/re_bad.py rename to python/ql/src/experimental/Security/CWE-730/re_bad.py diff --git a/python/ql/src/Security/CWE-730/re_good.py b/python/ql/src/experimental/Security/CWE-730/re_good.py similarity index 100% rename from python/ql/src/Security/CWE-730/re_good.py rename to python/ql/src/experimental/Security/CWE-730/re_good.py diff --git a/python/ql/src/experimental/semmle/python/Concepts.qll b/python/ql/src/experimental/semmle/python/Concepts.qll index 904b7967ee8..172a7fc2003 100644 --- a/python/ql/src/experimental/semmle/python/Concepts.qll +++ b/python/ql/src/experimental/semmle/python/Concepts.qll @@ -13,3 +13,32 @@ private import semmle.python.dataflow.new.DataFlow private import semmle.python.dataflow.new.RemoteFlowSources private import semmle.python.dataflow.new.TaintTracking private import experimental.semmle.python.Frameworks + +/** Provides classes for modeling Regular Expression-related APIs. */ +module RegexExecution { + /** + * A data-flow node that works with regular expressions. + * + * Extend this class to model new APIs. If you want to refine existing API models, + * extend `RegexExecution` instead. + */ + abstract class Range extends DataFlow::Node { + abstract DataFlow::Node getRegexNode(); + abstract Attribute getRegexMethod(); + } +} + +/** + * A data-flow node that works with regular expressions. + * + * Extend this class to refine existing API models. If you want to model new APIs, + * extend `RegexExecution::Range` instead. + */ +class RegexExecution extends DataFlow::Node { + RegexExecution::Range range; + + RegexExecution() { this = range } + + DataFlow::Node getRegexNode() { result = range.getRegexNode() } + Attribute getRegexMethod() { result = range.getRegexMethod() } +} diff --git a/python/ql/src/experimental/semmle/python/frameworks/Stdlib.qll b/python/ql/src/experimental/semmle/python/frameworks/Stdlib.qll index 420caf0d73b..edaa42fad8a 100644 --- a/python/ql/src/experimental/semmle/python/frameworks/Stdlib.qll +++ b/python/ql/src/experimental/semmle/python/frameworks/Stdlib.qll @@ -8,4 +8,234 @@ private import semmle.python.dataflow.new.DataFlow private import semmle.python.dataflow.new.TaintTracking private import semmle.python.dataflow.new.RemoteFlowSources private import experimental.semmle.python.Concepts -private import semmle.python.ApiGraphs + +/** Provides models for the Python standard library. */ +private module Stdlib { + // --------------------------------------------------------------------------- + // re + // --------------------------------------------------------------------------- + private module Re { + + /** Gets a reference to the `re` module. */ + private DataFlow::Node re(DataFlow::TypeTracker t) { + t.start() and + result = DataFlow::importNode("re") + or + exists(DataFlow::TypeTracker t2 | result = re(t2).track(t2, t)) + } + + /** Gets a reference to the `re` module. */ + DataFlow::Node re() { result = re(DataFlow::TypeTracker::end()) } + + /** + * Gets a reference to the attribute `attr_name` of the `re` module. + * WARNING: Only holds for a few predefined attributes. + */ + private DataFlow::Node re_attr(DataFlow::TypeTracker t, string attr_name) { + attr_name in ["match", "fullmatch", "search", "split", "findall", "finditer", "sub", "subn", "compile"] and + ( + t.start() and + result = DataFlow::importNode("re" + "." + attr_name) + or + t.startInAttr(attr_name) and + result = re() + ) + or + // Due to bad performance when using normal setup with `re_attr(t2, attr_name).track(t2, t)` + // we have inlined that code and forced a join + exists(DataFlow::TypeTracker t2 | + exists(DataFlow::StepSummary summary | + re_attr_first_join(t2, attr_name, result, summary) and + t = t2.append(summary) + ) + ) + } + + pragma[nomagic] + private predicate re_attr_first_join( + DataFlow::TypeTracker t2, string attr_name, DataFlow::Node res, DataFlow::StepSummary summary + ) { + DataFlow::StepSummary::step(re_attr(t2, attr_name), res, summary) + } + + /** + * Gets a reference to the attribute `attr_name` of the `re` module. + * WARNING: Only holds for a few predefined attributes. + */ + private DataFlow::Node re_attr(string attr_name) { + result = re_attr(DataFlow::TypeTracker::end(), attr_name) + } + + /** + * Gets a reference to any `attr_name` of the `re` module that immediately executes an expression. + * WARNING: Only holds for a few predefined attributes. + */ + private DataFlow::Node re_exec_attr() { + exists(string attr_name | + attr_name in ["match", "fullmatch", "search", "split", "findall", "finditer", "sub", "subn"] and + result = re_attr(DataFlow::TypeTracker::end(), attr_name) + ) + } + + /** + * A call to `re.match` + * See https://docs.python.org/3/library/re.html#re.match + */ + private class ReMatchCall extends RegexExecution::Range, DataFlow::CfgNode { + override CallNode node; + + ReMatchCall() { node.getFunction() = re_attr("match").asCfgNode() } + + override DataFlow::Node getRegexNode() { result.asCfgNode() = node.getArg(0) } + override Attribute getRegexMethod() { result = node.getNode().getFunc().(Attribute) } + } + + /** + * A call to `re.fullmatch` + * See https://docs.python.org/3/library/re.html#re.fullmatch + */ + private class ReFullMatchCall extends RegexExecution::Range, DataFlow::CfgNode { + override CallNode node; + + ReFullMatchCall() { node.getFunction() = re_attr("fullmatch").asCfgNode() } + + override DataFlow::Node getRegexNode() { result.asCfgNode() = node.getArg(0) } + override Attribute getRegexMethod() { result = node.getNode().getFunc().(Attribute) } + } + + /** + * A call to `re.search` + * See https://docs.python.org/3/library/re.html#re.search + */ + private class ReSearchCall extends RegexExecution::Range, DataFlow::CfgNode { + override CallNode node; + + ReSearchCall() { node.getFunction() = re_attr("search").asCfgNode() } + + override DataFlow::Node getRegexNode() { result.asCfgNode() = node.getArg(0) } + override Attribute getRegexMethod() { result = node.getNode().getFunc().(Attribute) } + } + + /** + * A call to `re.split` + * See https://docs.python.org/3/library/re.html#re.split + */ + private class ReSplitCall extends RegexExecution::Range, DataFlow::CfgNode { + override CallNode node; + + ReSplitCall() { node.getFunction() = re_attr("split").asCfgNode() } + + override DataFlow::Node getRegexNode() { result.asCfgNode() = node.getArg(0) } + override Attribute getRegexMethod() { result = node.getNode().getFunc().(Attribute) } + } + + /** + * A call to `re.findall` + * See https://docs.python.org/3/library/re.html#re.findall + */ + private class ReFindAllCall extends RegexExecution::Range, DataFlow::CfgNode { + override CallNode node; + + ReFindAllCall() { node.getFunction() = re_attr("findall").asCfgNode() } + + override DataFlow::Node getRegexNode() { result.asCfgNode() = node.getArg(0) } + override Attribute getRegexMethod() { result = node.getNode().getFunc().(Attribute) } + } + + /** + * A call to `re.finditer` + * See https://docs.python.org/3/library/re.html#re.finditer + */ + private class ReFindIterCall extends RegexExecution::Range, DataFlow::CfgNode { + override CallNode node; + + ReFindIterCall() { node.getFunction() = re_attr("finditer").asCfgNode() } + + override DataFlow::Node getRegexNode() { result.asCfgNode() = node.getArg(0) } + override Attribute getRegexMethod() { result = node.getNode().getFunc().(Attribute) } + } + + /** + * A call to `re.sub` + * See https://docs.python.org/3/library/re.html#re.sub + */ + private class ReSubCall extends RegexExecution::Range, DataFlow::CfgNode { + override CallNode node; + + ReSubCall() { node.getFunction() = re_attr("sub").asCfgNode() } + + override DataFlow::Node getRegexNode() { result.asCfgNode() = node.getArg(0) } + override Attribute getRegexMethod() { result = node.getNode().getFunc().(Attribute) } + } + + /** + * A call to `re.subn` + * See https://docs.python.org/3/library/re.html#re.subn + */ + private class ReSubNCall extends RegexExecution::Range, DataFlow::CfgNode { + override CallNode node; + + ReSubNCall() { node.getFunction() = re_attr("subn").asCfgNode() } + + override DataFlow::Node getRegexNode() { result.asCfgNode() = node.getArg(0) } + override Attribute getRegexMethod() { result = node.getNode().getFunc().(Attribute) } + } + + /** + * A call to `re.compile` + * See https://docs.python.org/3/library/re.html#re.match + */ + private class ReCompileCall extends RegexExecution::Range, DataFlow::CfgNode { + override CallNode node; + + ReCompileCall() { node.getFunction() = re_attr("compile").asCfgNode() } + + override DataFlow::Node getRegexNode() { result.asCfgNode() = node.getArg(0) } + override Attribute getRegexMethod() { + exists (DataFlow::AttrRead reMethod | + reMethod = re_exec_attr() and + node.getFunction() = reMethod.getObject().getALocalSource().asCfgNode() and + result = reMethod.asExpr().(Attribute) + ) + } + } + + /** + * A class for modeling expressions immediately executing a regular expression. + * See `re_exec_attr()` + */ + private class DirectRegex extends DataFlow::CallCfgNode, RegexExecution::Range { + DataFlow::Node regexNode; + Attribute regexMethod; + + DirectRegex() { + // needs inheritance (?) + this = re_exec_attr() and regexNode = this.getRegexNode() and regexMethod = this.getRegexMethod() + } + + override DataFlow::Node getRegexNode() { result = regexNode } + override Attribute getRegexMethod() { result = regexMethod } + } + + /** + * A class for finding `ReCompileCall` whose `Attribute` is an instance of `DirectRegex`. + * See `ReCompileCall`, `DirectRegex`, `re_exec_attr()` + */ + private class CompiledRegex extends DataFlow::CallCfgNode, RegexExecution::Range { + DataFlow::Node regexNode; + Attribute regexMethod; + + CompiledRegex() { + exists(DirectRegex reMethod, ReCompileCall compileCall | + this = reMethod and + reMethod.getRegexMethod() = compileCall.getRegexMethod() and + regexNode = compileCall.getRegexNode() and + regexMethod = reMethod.getRegexMethod() + ) + } + + override DataFlow::Node getRegexNode() { result = regexNode } + override Attribute getRegexMethod() { result = regexMethod } + } + } +} \ No newline at end of file diff --git a/python/ql/src/semmle/python/security/dataflow/RegexInjection.qll b/python/ql/src/experimental/semmle/python/security/injection/RegexInjection.qll similarity index 95% rename from python/ql/src/semmle/python/security/dataflow/RegexInjection.qll rename to python/ql/src/experimental/semmle/python/security/injection/RegexInjection.qll index 98545816c82..5e2cc684b43 100644 --- a/python/ql/src/semmle/python/security/dataflow/RegexInjection.qll +++ b/python/ql/src/experimental/semmle/python/security/injection/RegexInjection.qll @@ -4,7 +4,7 @@ */ import python -import semmle.python.Concepts +import experimental.semmle.python.Concepts import semmle.python.dataflow.new.DataFlow import semmle.python.dataflow.new.TaintTracking import semmle.python.dataflow.new.RemoteFlowSources diff --git a/python/ql/src/semmle/python/Concepts.qll b/python/ql/src/semmle/python/Concepts.qll index cc2e4ac3ca7..ccae7251a2c 100644 --- a/python/ql/src/semmle/python/Concepts.qll +++ b/python/ql/src/semmle/python/Concepts.qll @@ -625,49 +625,5 @@ module Cryptography { final override int minimumSecureKeySize() { result = 224 } } } -/* - */ - -class ReMethods extends string { - ReMethods() { this in ["match", "fullmatch", "search", "split", "findall", "finditer"] } -} - -class DirectRegex extends DataFlow::Node { - DirectRegex() { - exists(ReMethods reMethod, DataFlow::CallCfgNode reCall | - reCall = API::moduleImport("re").getMember(reMethod).getACall() and - this = reCall.getArg(0) - ) } -} - -class CompiledRegex extends DataFlow::Node { - CompiledRegex() { - exists(DataFlow::CallCfgNode patternCall, DataFlow::AttrRead reMethod | - patternCall = API::moduleImport("re").getMember("compile").getACall() and - patternCall = reMethod.getObject().getALocalSource() and - reMethod.getAttributeName() instanceof ReMethods and - this = patternCall.getArg(0) - ) - } -} - -class RegexExecution extends DataFlow::Node { - RegexExecution() { this instanceof DirectRegex or this instanceof CompiledRegex } // How should this be cross-imported with Stdlib? -} -/* - */ - -module RegexExecution { - abstract class Range extends DataFlow::Node { - abstract DataFlow::Node getRegexNode(); - } -} - -class RegexExecution extends DataFlow::Node { - RegexExecution::Range range; - - RegexExecution() { this = range } - - DataFlow::Node getRegexNode() { result = range.getRegexNode() } -} +} \ No newline at end of file diff --git a/python/ql/src/semmle/python/frameworks/Stdlib.qll b/python/ql/src/semmle/python/frameworks/Stdlib.qll index c86e4cf2e22..01df130d0db 100644 --- a/python/ql/src/semmle/python/frameworks/Stdlib.qll +++ b/python/ql/src/semmle/python/frameworks/Stdlib.qll @@ -864,52 +864,6 @@ private module Stdlib { class Sqlite3 extends PEP249ModuleApiNode { Sqlite3() { this = API::moduleImport("sqlite3") } } - - // --------------------------------------------------------------------------- - // re - // --------------------------------------------------------------------------- - private module Re { - /** List of re methods. */ - private class ReMethods extends string { - ReMethods() { this in ["match", "fullmatch", "search", "split", "findall", "finditer"] } - } - - /** re.ReMethod(pattern, string) */ - private class DirectRegex extends DataFlow::CallCfgNode, RegexExecution::Range { - DataFlow::Node regexNode; - - DirectRegex() { - this = API::moduleImport("re").getMember(any(ReMethods m)).getACall() and - regexNode = this.getArg(0) - } - - override DataFlow::Node getRegexNode() { result = regexNode } - } - - /** re.compile(pattern).ReMethod */ - private class CompiledRegex extends DataFlow::CallCfgNode, RegexExecution::Range { - DataFlow::Node regexNode; - - CompiledRegex() { - exists(DataFlow::CallCfgNode patternCall, DataFlow::AttrRead reMethod | - this.getFunction() = reMethod and - patternCall = API::moduleImport("re").getMember("compile").getACall() and - patternCall = reMethod.getObject().getALocalSource() and - reMethod.getAttributeName() instanceof ReMethods and - regexNode = patternCall.getArg(0) - ) - } - - override DataFlow::Node getRegexNode() { result = regexNode } - } - - private class RegexEscape extends DataFlow::Node { - RegexEscape() { - this = - API::moduleImport("re").getMember("escape").getACall().(DataFlow::CallCfgNode).getArg(0) - } - } - } } // --------------------------------------------------------------------------- From a1a3c98d92fa433d642139a3e259713a31cc2c3e Mon Sep 17 00:00:00 2001 From: jorgectf Date: Wed, 24 Mar 2021 18:16:36 +0100 Subject: [PATCH 040/168] Undo main Concepts.qll change --- python/ql/src/semmle/python/Concepts.qll | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/python/ql/src/semmle/python/Concepts.qll b/python/ql/src/semmle/python/Concepts.qll index ccae7251a2c..2706a497f9a 100644 --- a/python/ql/src/semmle/python/Concepts.qll +++ b/python/ql/src/semmle/python/Concepts.qll @@ -625,5 +625,4 @@ module Cryptography { final override int minimumSecureKeySize() { result = 224 } } } - } -} \ No newline at end of file + } \ No newline at end of file From d61adccd3c6b6511e106b106bfe2d6efef612c14 Mon Sep 17 00:00:00 2001 From: jorgectf Date: Wed, 24 Mar 2021 18:23:59 +0100 Subject: [PATCH 041/168] Take main Concepts.qll out of the PR --- python/ql/src/semmle/python/Concepts.qll | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/python/ql/src/semmle/python/Concepts.qll b/python/ql/src/semmle/python/Concepts.qll index 2706a497f9a..4be00ba5bf9 100644 --- a/python/ql/src/semmle/python/Concepts.qll +++ b/python/ql/src/semmle/python/Concepts.qll @@ -625,4 +625,5 @@ module Cryptography { final override int minimumSecureKeySize() { result = 224 } } } - } \ No newline at end of file + } +} From b5ea41fcca6d80ddeaa7209a8f7f0edaef5237f8 Mon Sep 17 00:00:00 2001 From: jorgectf Date: Wed, 24 Mar 2021 21:02:09 +0100 Subject: [PATCH 042/168] Fix CompiledRegex --- .../src/experimental/semmle/python/frameworks/Stdlib.qll | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/python/ql/src/experimental/semmle/python/frameworks/Stdlib.qll b/python/ql/src/experimental/semmle/python/frameworks/Stdlib.qll index edaa42fad8a..a6d848cc126 100644 --- a/python/ql/src/experimental/semmle/python/frameworks/Stdlib.qll +++ b/python/ql/src/experimental/semmle/python/frameworks/Stdlib.qll @@ -226,11 +226,10 @@ private module Stdlib { Attribute regexMethod; CompiledRegex() { - exists(DirectRegex reMethod, ReCompileCall compileCall | - this = reMethod and - reMethod.getRegexMethod() = compileCall.getRegexMethod() and + exists(ReCompileCall compileCall | regexNode = compileCall.getRegexNode() and - regexMethod = reMethod.getRegexMethod() + regexMethod = compileCall.getRegexMethod() and + this = compileCall ) } From ce23db2e9ca80497f71b7c9792f60582906db42b Mon Sep 17 00:00:00 2001 From: jorgectf Date: Wed, 24 Mar 2021 21:11:45 +0100 Subject: [PATCH 043/168] Move Sanitizer to ReEscapeCall --- .../semmle/python/frameworks/Stdlib.qll | 15 ++++++++++++++- .../python/security/injection/RegexInjection.qll | 8 +++----- 2 files changed, 17 insertions(+), 6 deletions(-) diff --git a/python/ql/src/experimental/semmle/python/frameworks/Stdlib.qll b/python/ql/src/experimental/semmle/python/frameworks/Stdlib.qll index a6d848cc126..d243eacd3d0 100644 --- a/python/ql/src/experimental/semmle/python/frameworks/Stdlib.qll +++ b/python/ql/src/experimental/semmle/python/frameworks/Stdlib.qll @@ -32,7 +32,7 @@ private module Stdlib { * WARNING: Only holds for a few predefined attributes. */ private DataFlow::Node re_attr(DataFlow::TypeTracker t, string attr_name) { - attr_name in ["match", "fullmatch", "search", "split", "findall", "finditer", "sub", "subn", "compile"] and + attr_name in ["match", "fullmatch", "search", "split", "findall", "finditer", "sub", "subn", "compile", "escape"] and ( t.start() and result = DataFlow::importNode("re" + "." + attr_name) @@ -181,6 +181,19 @@ private module Stdlib { override Attribute getRegexMethod() { result = node.getNode().getFunc().(Attribute) } } + /** + * A call to `re.escape` + * See https://docs.python.org/3/library/re.html#re.escape + */ + private class ReEscapeCall extends RegexExecution::Range, DataFlow::CfgNode { + override CallNode node; + + ReEscapeCall() { node.getFunction() = re_attr("escape").asCfgNode() } + + override DataFlow::Node getRegexNode() { result.asCfgNode() = node.getArg(0) } + override Attribute getRegexMethod() { result = node.getNode().getFunc().(Attribute) } + } + /** * A call to `re.compile` * See https://docs.python.org/3/library/re.html#re.match diff --git a/python/ql/src/experimental/semmle/python/security/injection/RegexInjection.qll b/python/ql/src/experimental/semmle/python/security/injection/RegexInjection.qll index 5e2cc684b43..0d42acbac49 100644 --- a/python/ql/src/experimental/semmle/python/security/injection/RegexInjection.qll +++ b/python/ql/src/experimental/semmle/python/security/injection/RegexInjection.qll @@ -5,6 +5,7 @@ import python import experimental.semmle.python.Concepts +import experimental.semmle.python.frameworks.Stdlib import semmle.python.dataflow.new.DataFlow import semmle.python.dataflow.new.TaintTracking import semmle.python.dataflow.new.RemoteFlowSources @@ -18,10 +19,7 @@ class RegexInjectionFlowConfig extends TaintTracking::Configuration { override predicate isSource(DataFlow::Node source) { source instanceof RemoteFlowSource } - override predicate isSink(DataFlow::Node sink) { sink = any(RegexExecution re).getRegexNode() } + override predicate isSink(DataFlow::Node sink) { sink = any(RegexExecution re).getRegexNode() } - override predicate isSanitizer(DataFlow::Node sanitizer) { - sanitizer = - API::moduleImport("re").getMember("escape").getACall().(DataFlow::CallCfgNode).getArg(0) - } + override predicate isSanitizer(DataFlow::Node sanitizer) { sanitizer instanceof ReEscapeCall } } From ee1d2b645b0a42a370c6c47fb721ecff3562b348 Mon Sep 17 00:00:00 2001 From: jorgectf Date: Thu, 25 Mar 2021 18:19:11 +0100 Subject: [PATCH 044/168] Delete DirectRegex and CompiledRegex --- .../semmle/python/frameworks/Stdlib.qll | 37 ------------------- 1 file changed, 37 deletions(-) diff --git a/python/ql/src/experimental/semmle/python/frameworks/Stdlib.qll b/python/ql/src/experimental/semmle/python/frameworks/Stdlib.qll index d243eacd3d0..fab8debb1b8 100644 --- a/python/ql/src/experimental/semmle/python/frameworks/Stdlib.qll +++ b/python/ql/src/experimental/semmle/python/frameworks/Stdlib.qll @@ -212,42 +212,5 @@ private module Stdlib { ) } } - - /** - * A class for modeling expressions immediately executing a regular expression. - * See `re_exec_attr()` - */ - private class DirectRegex extends DataFlow::CallCfgNode, RegexExecution::Range { - DataFlow::Node regexNode; - Attribute regexMethod; - - DirectRegex() { - // needs inheritance (?) - this = re_exec_attr() and regexNode = this.getRegexNode() and regexMethod = this.getRegexMethod() - } - - override DataFlow::Node getRegexNode() { result = regexNode } - override Attribute getRegexMethod() { result = regexMethod } - } - - /** - * A class for finding `ReCompileCall` whose `Attribute` is an instance of `DirectRegex`. - * See `ReCompileCall`, `DirectRegex`, `re_exec_attr()` - */ - private class CompiledRegex extends DataFlow::CallCfgNode, RegexExecution::Range { - DataFlow::Node regexNode; - Attribute regexMethod; - - CompiledRegex() { - exists(ReCompileCall compileCall | - regexNode = compileCall.getRegexNode() and - regexMethod = compileCall.getRegexMethod() and - this = compileCall - ) - } - - override DataFlow::Node getRegexNode() { result = regexNode } - override Attribute getRegexMethod() { result = regexMethod } - } } } \ No newline at end of file From 30554a16da55ef9d652ad60e6437593682b07c23 Mon Sep 17 00:00:00 2001 From: jorgectf Date: Thu, 25 Mar 2021 18:20:13 +0100 Subject: [PATCH 045/168] Format --- .../Security/CWE-730/RegexInjection.ql | 2 +- .../semmle/python/frameworks/Stdlib.qll | 24 ++++++++++++++----- 2 files changed, 19 insertions(+), 7 deletions(-) diff --git a/python/ql/src/experimental/Security/CWE-730/RegexInjection.ql b/python/ql/src/experimental/Security/CWE-730/RegexInjection.ql index ccb97fbee54..9d3da12f9bd 100644 --- a/python/ql/src/experimental/Security/CWE-730/RegexInjection.ql +++ b/python/ql/src/experimental/Security/CWE-730/RegexInjection.ql @@ -19,4 +19,4 @@ import DataFlow::PathGraph from RegexInjectionFlowConfig config, DataFlow::PathNode source, DataFlow::PathNode sink where config.hasFlowPath(source, sink) select sink.getNode(), source, sink, "$@ regular expression is constructed from a $@.", - sink.getNode(), "This", source.getNode(), "user-provided value" \ No newline at end of file + sink.getNode(), "This", source.getNode(), "user-provided value" diff --git a/python/ql/src/experimental/semmle/python/frameworks/Stdlib.qll b/python/ql/src/experimental/semmle/python/frameworks/Stdlib.qll index fab8debb1b8..4b5f8f63f6a 100644 --- a/python/ql/src/experimental/semmle/python/frameworks/Stdlib.qll +++ b/python/ql/src/experimental/semmle/python/frameworks/Stdlib.qll @@ -15,7 +15,6 @@ private module Stdlib { // re // --------------------------------------------------------------------------- private module Re { - /** Gets a reference to the `re` module. */ private DataFlow::Node re(DataFlow::TypeTracker t) { t.start() and @@ -32,7 +31,10 @@ private module Stdlib { * WARNING: Only holds for a few predefined attributes. */ private DataFlow::Node re_attr(DataFlow::TypeTracker t, string attr_name) { - attr_name in ["match", "fullmatch", "search", "split", "findall", "finditer", "sub", "subn", "compile", "escape"] and + attr_name in [ + "match", "fullmatch", "search", "split", "findall", "finditer", "sub", "subn", "compile", + "escape" + ] and ( t.start() and result = DataFlow::importNode("re" + "." + attr_name) @@ -87,6 +89,7 @@ private module Stdlib { ReMatchCall() { node.getFunction() = re_attr("match").asCfgNode() } override DataFlow::Node getRegexNode() { result.asCfgNode() = node.getArg(0) } + override Attribute getRegexMethod() { result = node.getNode().getFunc().(Attribute) } } @@ -100,6 +103,7 @@ private module Stdlib { ReFullMatchCall() { node.getFunction() = re_attr("fullmatch").asCfgNode() } override DataFlow::Node getRegexNode() { result.asCfgNode() = node.getArg(0) } + override Attribute getRegexMethod() { result = node.getNode().getFunc().(Attribute) } } @@ -113,6 +117,7 @@ private module Stdlib { ReSearchCall() { node.getFunction() = re_attr("search").asCfgNode() } override DataFlow::Node getRegexNode() { result.asCfgNode() = node.getArg(0) } + override Attribute getRegexMethod() { result = node.getNode().getFunc().(Attribute) } } @@ -126,6 +131,7 @@ private module Stdlib { ReSplitCall() { node.getFunction() = re_attr("split").asCfgNode() } override DataFlow::Node getRegexNode() { result.asCfgNode() = node.getArg(0) } + override Attribute getRegexMethod() { result = node.getNode().getFunc().(Attribute) } } @@ -139,6 +145,7 @@ private module Stdlib { ReFindAllCall() { node.getFunction() = re_attr("findall").asCfgNode() } override DataFlow::Node getRegexNode() { result.asCfgNode() = node.getArg(0) } + override Attribute getRegexMethod() { result = node.getNode().getFunc().(Attribute) } } @@ -152,6 +159,7 @@ private module Stdlib { ReFindIterCall() { node.getFunction() = re_attr("finditer").asCfgNode() } override DataFlow::Node getRegexNode() { result.asCfgNode() = node.getArg(0) } + override Attribute getRegexMethod() { result = node.getNode().getFunc().(Attribute) } } @@ -165,6 +173,7 @@ private module Stdlib { ReSubCall() { node.getFunction() = re_attr("sub").asCfgNode() } override DataFlow::Node getRegexNode() { result.asCfgNode() = node.getArg(0) } + override Attribute getRegexMethod() { result = node.getNode().getFunc().(Attribute) } } @@ -178,6 +187,7 @@ private module Stdlib { ReSubNCall() { node.getFunction() = re_attr("subn").asCfgNode() } override DataFlow::Node getRegexNode() { result.asCfgNode() = node.getArg(0) } + override Attribute getRegexMethod() { result = node.getNode().getFunc().(Attribute) } } @@ -191,6 +201,7 @@ private module Stdlib { ReEscapeCall() { node.getFunction() = re_attr("escape").asCfgNode() } override DataFlow::Node getRegexNode() { result.asCfgNode() = node.getArg(0) } + override Attribute getRegexMethod() { result = node.getNode().getFunc().(Attribute) } } @@ -204,13 +215,14 @@ private module Stdlib { ReCompileCall() { node.getFunction() = re_attr("compile").asCfgNode() } override DataFlow::Node getRegexNode() { result.asCfgNode() = node.getArg(0) } - override Attribute getRegexMethod() { - exists (DataFlow::AttrRead reMethod | + + override Attribute getRegexMethod() { + exists(DataFlow::AttrRead reMethod | reMethod = re_exec_attr() and node.getFunction() = reMethod.getObject().getALocalSource().asCfgNode() and result = reMethod.asExpr().(Attribute) ) - } + } } } -} \ No newline at end of file +} From 3d990c595097c4af80392cc0ac26988605d2f059 Mon Sep 17 00:00:00 2001 From: jorgectf Date: Fri, 26 Mar 2021 10:05:50 +0100 Subject: [PATCH 046/168] Get back to ApiGraphs --- .../experimental/semmle/python/Concepts.qll | 9 + .../semmle/python/frameworks/Stdlib.qll | 254 +++--------------- .../security/injection/RegexInjection.qll | 3 +- 3 files changed, 51 insertions(+), 215 deletions(-) diff --git a/python/ql/src/experimental/semmle/python/Concepts.qll b/python/ql/src/experimental/semmle/python/Concepts.qll index 172a7fc2003..8a88ffb2154 100644 --- a/python/ql/src/experimental/semmle/python/Concepts.qll +++ b/python/ql/src/experimental/semmle/python/Concepts.qll @@ -13,6 +13,7 @@ private import semmle.python.dataflow.new.DataFlow private import semmle.python.dataflow.new.RemoteFlowSources private import semmle.python.dataflow.new.TaintTracking private import experimental.semmle.python.Frameworks +private import semmle.python.ApiGraphs /** Provides classes for modeling Regular Expression-related APIs. */ module RegexExecution { @@ -24,6 +25,7 @@ module RegexExecution { */ abstract class Range extends DataFlow::Node { abstract DataFlow::Node getRegexNode(); + abstract Attribute getRegexMethod(); } } @@ -40,5 +42,12 @@ class RegexExecution extends DataFlow::Node { RegexExecution() { this = range } DataFlow::Node getRegexNode() { result = range.getRegexNode() } + Attribute getRegexMethod() { result = range.getRegexMethod() } } + +class RegexEscape extends DataFlow::Node { + RegexEscape() { + this = API::moduleImport("re").getMember("escape").getACall().(DataFlow::CallCfgNode).getArg(0) + } +} diff --git a/python/ql/src/experimental/semmle/python/frameworks/Stdlib.qll b/python/ql/src/experimental/semmle/python/frameworks/Stdlib.qll index 4b5f8f63f6a..5a3441bed83 100644 --- a/python/ql/src/experimental/semmle/python/frameworks/Stdlib.qll +++ b/python/ql/src/experimental/semmle/python/frameworks/Stdlib.qll @@ -8,221 +8,49 @@ private import semmle.python.dataflow.new.DataFlow private import semmle.python.dataflow.new.TaintTracking private import semmle.python.dataflow.new.RemoteFlowSources private import experimental.semmle.python.Concepts +private import semmle.python.ApiGraphs /** Provides models for the Python standard library. */ -private module Stdlib { - // --------------------------------------------------------------------------- - // re - // --------------------------------------------------------------------------- - private module Re { - /** Gets a reference to the `re` module. */ - private DataFlow::Node re(DataFlow::TypeTracker t) { - t.start() and - result = DataFlow::importNode("re") - or - exists(DataFlow::TypeTracker t2 | result = re(t2).track(t2, t)) - } - - /** Gets a reference to the `re` module. */ - DataFlow::Node re() { result = re(DataFlow::TypeTracker::end()) } - - /** - * Gets a reference to the attribute `attr_name` of the `re` module. - * WARNING: Only holds for a few predefined attributes. - */ - private DataFlow::Node re_attr(DataFlow::TypeTracker t, string attr_name) { - attr_name in [ - "match", "fullmatch", "search", "split", "findall", "finditer", "sub", "subn", "compile", - "escape" - ] and - ( - t.start() and - result = DataFlow::importNode("re" + "." + attr_name) - or - t.startInAttr(attr_name) and - result = re() - ) - or - // Due to bad performance when using normal setup with `re_attr(t2, attr_name).track(t2, t)` - // we have inlined that code and forced a join - exists(DataFlow::TypeTracker t2 | - exists(DataFlow::StepSummary summary | - re_attr_first_join(t2, attr_name, result, summary) and - t = t2.append(summary) - ) - ) - } - - pragma[nomagic] - private predicate re_attr_first_join( - DataFlow::TypeTracker t2, string attr_name, DataFlow::Node res, DataFlow::StepSummary summary - ) { - DataFlow::StepSummary::step(re_attr(t2, attr_name), res, summary) - } - - /** - * Gets a reference to the attribute `attr_name` of the `re` module. - * WARNING: Only holds for a few predefined attributes. - */ - private DataFlow::Node re_attr(string attr_name) { - result = re_attr(DataFlow::TypeTracker::end(), attr_name) - } - - /** - * Gets a reference to any `attr_name` of the `re` module that immediately executes an expression. - * WARNING: Only holds for a few predefined attributes. - */ - private DataFlow::Node re_exec_attr() { - exists(string attr_name | - attr_name in ["match", "fullmatch", "search", "split", "findall", "finditer", "sub", "subn"] and - result = re_attr(DataFlow::TypeTracker::end(), attr_name) - ) - } - - /** - * A call to `re.match` - * See https://docs.python.org/3/library/re.html#re.match - */ - private class ReMatchCall extends RegexExecution::Range, DataFlow::CfgNode { - override CallNode node; - - ReMatchCall() { node.getFunction() = re_attr("match").asCfgNode() } - - override DataFlow::Node getRegexNode() { result.asCfgNode() = node.getArg(0) } - - override Attribute getRegexMethod() { result = node.getNode().getFunc().(Attribute) } - } - - /** - * A call to `re.fullmatch` - * See https://docs.python.org/3/library/re.html#re.fullmatch - */ - private class ReFullMatchCall extends RegexExecution::Range, DataFlow::CfgNode { - override CallNode node; - - ReFullMatchCall() { node.getFunction() = re_attr("fullmatch").asCfgNode() } - - override DataFlow::Node getRegexNode() { result.asCfgNode() = node.getArg(0) } - - override Attribute getRegexMethod() { result = node.getNode().getFunc().(Attribute) } - } - - /** - * A call to `re.search` - * See https://docs.python.org/3/library/re.html#re.search - */ - private class ReSearchCall extends RegexExecution::Range, DataFlow::CfgNode { - override CallNode node; - - ReSearchCall() { node.getFunction() = re_attr("search").asCfgNode() } - - override DataFlow::Node getRegexNode() { result.asCfgNode() = node.getArg(0) } - - override Attribute getRegexMethod() { result = node.getNode().getFunc().(Attribute) } - } - - /** - * A call to `re.split` - * See https://docs.python.org/3/library/re.html#re.split - */ - private class ReSplitCall extends RegexExecution::Range, DataFlow::CfgNode { - override CallNode node; - - ReSplitCall() { node.getFunction() = re_attr("split").asCfgNode() } - - override DataFlow::Node getRegexNode() { result.asCfgNode() = node.getArg(0) } - - override Attribute getRegexMethod() { result = node.getNode().getFunc().(Attribute) } - } - - /** - * A call to `re.findall` - * See https://docs.python.org/3/library/re.html#re.findall - */ - private class ReFindAllCall extends RegexExecution::Range, DataFlow::CfgNode { - override CallNode node; - - ReFindAllCall() { node.getFunction() = re_attr("findall").asCfgNode() } - - override DataFlow::Node getRegexNode() { result.asCfgNode() = node.getArg(0) } - - override Attribute getRegexMethod() { result = node.getNode().getFunc().(Attribute) } - } - - /** - * A call to `re.finditer` - * See https://docs.python.org/3/library/re.html#re.finditer - */ - private class ReFindIterCall extends RegexExecution::Range, DataFlow::CfgNode { - override CallNode node; - - ReFindIterCall() { node.getFunction() = re_attr("finditer").asCfgNode() } - - override DataFlow::Node getRegexNode() { result.asCfgNode() = node.getArg(0) } - - override Attribute getRegexMethod() { result = node.getNode().getFunc().(Attribute) } - } - - /** - * A call to `re.sub` - * See https://docs.python.org/3/library/re.html#re.sub - */ - private class ReSubCall extends RegexExecution::Range, DataFlow::CfgNode { - override CallNode node; - - ReSubCall() { node.getFunction() = re_attr("sub").asCfgNode() } - - override DataFlow::Node getRegexNode() { result.asCfgNode() = node.getArg(0) } - - override Attribute getRegexMethod() { result = node.getNode().getFunc().(Attribute) } - } - - /** - * A call to `re.subn` - * See https://docs.python.org/3/library/re.html#re.subn - */ - private class ReSubNCall extends RegexExecution::Range, DataFlow::CfgNode { - override CallNode node; - - ReSubNCall() { node.getFunction() = re_attr("subn").asCfgNode() } - - override DataFlow::Node getRegexNode() { result.asCfgNode() = node.getArg(0) } - - override Attribute getRegexMethod() { result = node.getNode().getFunc().(Attribute) } - } - - /** - * A call to `re.escape` - * See https://docs.python.org/3/library/re.html#re.escape - */ - private class ReEscapeCall extends RegexExecution::Range, DataFlow::CfgNode { - override CallNode node; - - ReEscapeCall() { node.getFunction() = re_attr("escape").asCfgNode() } - - override DataFlow::Node getRegexNode() { result.asCfgNode() = node.getArg(0) } - - override Attribute getRegexMethod() { result = node.getNode().getFunc().(Attribute) } - } - - /** - * A call to `re.compile` - * See https://docs.python.org/3/library/re.html#re.match - */ - private class ReCompileCall extends RegexExecution::Range, DataFlow::CfgNode { - override CallNode node; - - ReCompileCall() { node.getFunction() = re_attr("compile").asCfgNode() } - - override DataFlow::Node getRegexNode() { result.asCfgNode() = node.getArg(0) } - - override Attribute getRegexMethod() { - exists(DataFlow::AttrRead reMethod | - reMethod = re_exec_attr() and - node.getFunction() = reMethod.getObject().getALocalSource().asCfgNode() and - result = reMethod.asExpr().(Attribute) - ) - } +private module Re { + /** List of re methods. */ + private class ReMethods extends string { + ReMethods() { + this in ["match", "fullmatch", "search", "split", "findall", "finditer", "sub", "subn"] } } + + private class DirectRegex extends DataFlow::CallCfgNode, RegexExecution::Range { + DataFlow::Node regexNode; + Attribute regexMethod; + + DirectRegex() { + this = API::moduleImport("re").getMember(any(ReMethods m)).getACall() and + regexNode = this.getArg(0) and + regexMethod = this.asExpr().(Attribute) + } + + override DataFlow::Node getRegexNode() { result = regexNode } + + override Attribute getRegexMethod() { result = regexMethod } + } + + private class CompiledRegex extends DataFlow::CallCfgNode, RegexExecution::Range { + DataFlow::Node regexNode; + Attribute regexMethod; + + CompiledRegex() { + exists(DataFlow::CallCfgNode patternCall, DirectRegex reMethod | + this = reMethod and + patternCall = API::moduleImport("re").getMember("compile").getACall() and + patternCall = reMethod.(DataFlow::AttrRead).getObject().getALocalSource() and + regexNode = patternCall.getArg(0) and + // regexMethod is *not* worked out outside class instanciation because `CompiledRegex` focuses on re.compile(pattern).ReMethod + regexMethod = reMethod.getRegexMethod() + ) + } + + override DataFlow::Node getRegexNode() { result = regexNode } + + override Attribute getRegexMethod() { result = regexMethod } + } } diff --git a/python/ql/src/experimental/semmle/python/security/injection/RegexInjection.qll b/python/ql/src/experimental/semmle/python/security/injection/RegexInjection.qll index 0d42acbac49..102fb80cfff 100644 --- a/python/ql/src/experimental/semmle/python/security/injection/RegexInjection.qll +++ b/python/ql/src/experimental/semmle/python/security/injection/RegexInjection.qll @@ -5,7 +5,6 @@ import python import experimental.semmle.python.Concepts -import experimental.semmle.python.frameworks.Stdlib import semmle.python.dataflow.new.DataFlow import semmle.python.dataflow.new.TaintTracking import semmle.python.dataflow.new.RemoteFlowSources @@ -21,5 +20,5 @@ class RegexInjectionFlowConfig extends TaintTracking::Configuration { override predicate isSink(DataFlow::Node sink) { sink = any(RegexExecution re).getRegexNode() } - override predicate isSanitizer(DataFlow::Node sanitizer) { sanitizer instanceof ReEscapeCall } + override predicate isSanitizer(DataFlow::Node sanitizer) { sanitizer instanceof RegexEscape } } From 805f86a5cf49a81fa47c616064fc88efa12aca12 Mon Sep 17 00:00:00 2001 From: jorgectf Date: Fri, 26 Mar 2021 10:15:12 +0100 Subject: [PATCH 047/168] Polish RegexEscape --- .../ql/src/experimental/semmle/python/Concepts.qll | 13 +++++++++++-- .../python/security/injection/RegexInjection.qll | 4 +++- 2 files changed, 14 insertions(+), 3 deletions(-) diff --git a/python/ql/src/experimental/semmle/python/Concepts.qll b/python/ql/src/experimental/semmle/python/Concepts.qll index 8a88ffb2154..b8cef8e4e11 100644 --- a/python/ql/src/experimental/semmle/python/Concepts.qll +++ b/python/ql/src/experimental/semmle/python/Concepts.qll @@ -46,8 +46,17 @@ class RegexExecution extends DataFlow::Node { Attribute getRegexMethod() { result = range.getRegexMethod() } } -class RegexEscape extends DataFlow::Node { +class RegexEscape extends DataFlow::CallCfgNode { + DataFlow::Node regexNode; + Attribute regexMethod; + RegexEscape() { - this = API::moduleImport("re").getMember("escape").getACall().(DataFlow::CallCfgNode).getArg(0) + this = API::moduleImport("re").getMember("escape").getACall() and + regexNode = this.getArg(0) and + regexMethod = this.asExpr().(Attribute) } + + DataFlow::Node getRegexNode() { result = regexNode } + + Attribute getRegexMethod() { result = regexMethod } } diff --git a/python/ql/src/experimental/semmle/python/security/injection/RegexInjection.qll b/python/ql/src/experimental/semmle/python/security/injection/RegexInjection.qll index 102fb80cfff..f82af085db0 100644 --- a/python/ql/src/experimental/semmle/python/security/injection/RegexInjection.qll +++ b/python/ql/src/experimental/semmle/python/security/injection/RegexInjection.qll @@ -20,5 +20,7 @@ class RegexInjectionFlowConfig extends TaintTracking::Configuration { override predicate isSink(DataFlow::Node sink) { sink = any(RegexExecution re).getRegexNode() } - override predicate isSanitizer(DataFlow::Node sanitizer) { sanitizer instanceof RegexEscape } + override predicate isSanitizer(DataFlow::Node sanitizer) { + sanitizer = sanitizer.(RegexEscape).getRegexNode() + } } From be09ffec3fbb31a69265911cad0d09989dfe5446 Mon Sep 17 00:00:00 2001 From: jorgectf Date: Fri, 26 Mar 2021 10:24:42 +0100 Subject: [PATCH 048/168] Create RegexEscape Range --- .../experimental/semmle/python/Concepts.qll | 42 +++++++++++++------ .../semmle/python/frameworks/Stdlib.qll | 15 +++++++ .../security/injection/RegexInjection.qll | 2 +- 3 files changed, 45 insertions(+), 14 deletions(-) diff --git a/python/ql/src/experimental/semmle/python/Concepts.qll b/python/ql/src/experimental/semmle/python/Concepts.qll index b8cef8e4e11..b4723c9e99d 100644 --- a/python/ql/src/experimental/semmle/python/Concepts.qll +++ b/python/ql/src/experimental/semmle/python/Concepts.qll @@ -18,7 +18,7 @@ private import semmle.python.ApiGraphs /** Provides classes for modeling Regular Expression-related APIs. */ module RegexExecution { /** - * A data-flow node that works with regular expressions. + * A data-flow node that works with regular expressions immediately executing an expression. * * Extend this class to model new APIs. If you want to refine existing API models, * extend `RegexExecution` instead. @@ -31,7 +31,7 @@ module RegexExecution { } /** - * A data-flow node that works with regular expressions. + * A data-flow node that works with regular expressions immediately executing an expression. * * Extend this class to refine existing API models. If you want to model new APIs, * extend `RegexExecution::Range` instead. @@ -46,17 +46,33 @@ class RegexExecution extends DataFlow::Node { Attribute getRegexMethod() { result = range.getRegexMethod() } } -class RegexEscape extends DataFlow::CallCfgNode { - DataFlow::Node regexNode; - Attribute regexMethod; +/** Provides classes for modeling Regular Expression escape-related APIs. */ +module RegexEscape { + /** + * A data-flow node that collects functions escaping regular expressions. + * + * Extend this class to model new APIs. If you want to refine existing API models, + * extend `RegexEscape` instead. + */ + abstract class Range extends DataFlow::Node { + abstract DataFlow::Node getRegexNode(); - RegexEscape() { - this = API::moduleImport("re").getMember("escape").getACall() and - regexNode = this.getArg(0) and - regexMethod = this.asExpr().(Attribute) + abstract Attribute getEscapeMethod(); } - - DataFlow::Node getRegexNode() { result = regexNode } - - Attribute getRegexMethod() { result = regexMethod } +} + +/** + * A data-flow node that collects functions escaping regular expressions. + * + * Extend this class to refine existing API models. If you want to model new APIs, + * extend `RegexEscape::Range` instead. + */ +class RegexEscape extends DataFlow::Node { + RegexEscape::Range range; + + RegexEscape() { this = range } + + DataFlow::Node getRegexNode() { result = range.getRegexNode() } + + Attribute getEscapeMethod() { result = range.getEscapeMethod() } } diff --git a/python/ql/src/experimental/semmle/python/frameworks/Stdlib.qll b/python/ql/src/experimental/semmle/python/frameworks/Stdlib.qll index 5a3441bed83..0b5c57c1c67 100644 --- a/python/ql/src/experimental/semmle/python/frameworks/Stdlib.qll +++ b/python/ql/src/experimental/semmle/python/frameworks/Stdlib.qll @@ -53,4 +53,19 @@ private module Re { override Attribute getRegexMethod() { result = regexMethod } } + + class ReEscape extends DataFlow::CallCfgNode, RegexEscape::Range { + DataFlow::Node regexNode; + Attribute escapeMethod; + + ReEscape() { + this = API::moduleImport("re").getMember("escape").getACall() and + regexNode = this.getArg(0) and + escapeMethod = this.asExpr().(Attribute) + } + + override DataFlow::Node getRegexNode() { result = regexNode } + + override Attribute getEscapeMethod() { result = escapeMethod } + } } diff --git a/python/ql/src/experimental/semmle/python/security/injection/RegexInjection.qll b/python/ql/src/experimental/semmle/python/security/injection/RegexInjection.qll index f82af085db0..fb2ef2ed192 100644 --- a/python/ql/src/experimental/semmle/python/security/injection/RegexInjection.qll +++ b/python/ql/src/experimental/semmle/python/security/injection/RegexInjection.qll @@ -21,6 +21,6 @@ class RegexInjectionFlowConfig extends TaintTracking::Configuration { override predicate isSink(DataFlow::Node sink) { sink = any(RegexExecution re).getRegexNode() } override predicate isSanitizer(DataFlow::Node sanitizer) { - sanitizer = sanitizer.(RegexEscape).getRegexNode() + sanitizer = any(RegexEscape reEscape).getRegexNode() } } From c127b109d0ccf29db088f2f2f0e4d58107444df1 Mon Sep 17 00:00:00 2001 From: jorgectf Date: Fri, 26 Mar 2021 16:21:34 +0100 Subject: [PATCH 049/168] Create re.compile().ReMethod test --- .../Security/CWE-730/{ => unit_tests}/re_bad.py | 8 ++++++-- .../Security/CWE-730/{ => unit_tests}/re_good.py | 9 +++++++-- 2 files changed, 13 insertions(+), 4 deletions(-) rename python/ql/src/experimental/Security/CWE-730/{ => unit_tests}/re_bad.py (72%) rename python/ql/src/experimental/Security/CWE-730/{ => unit_tests}/re_good.py (71%) diff --git a/python/ql/src/experimental/Security/CWE-730/re_bad.py b/python/ql/src/experimental/Security/CWE-730/unit_tests/re_bad.py similarity index 72% rename from python/ql/src/experimental/Security/CWE-730/re_bad.py rename to python/ql/src/experimental/Security/CWE-730/unit_tests/re_bad.py index 6a0aa7c6614..172fb94bab8 100644 --- a/python/ql/src/experimental/Security/CWE-730/re_bad.py +++ b/python/ql/src/experimental/Security/CWE-730/unit_tests/re_bad.py @@ -1,3 +1,5 @@ +# move outside test folder + from flask import request, Flask import re @@ -7,16 +9,18 @@ app = Flask(__name__) @app.route("/direct") def direct(): pattern = request.args['pattern'] - re.search(pattern, "") @app.route("/compile") def compile(): pattern = re.compile(request.args['pattern']) - pattern.search("") +@app.route("/compile_direct") +def compile_direct(): + re.compile(request.args['pattern']).search("") + # if __name__ == "__main__": # app.run(debug=True) diff --git a/python/ql/src/experimental/Security/CWE-730/re_good.py b/python/ql/src/experimental/Security/CWE-730/unit_tests/re_good.py similarity index 71% rename from python/ql/src/experimental/Security/CWE-730/re_good.py rename to python/ql/src/experimental/Security/CWE-730/unit_tests/re_good.py index ba09b1fe30b..a226d7a8899 100644 --- a/python/ql/src/experimental/Security/CWE-730/re_good.py +++ b/python/ql/src/experimental/Security/CWE-730/unit_tests/re_good.py @@ -1,3 +1,5 @@ +# move outside test folder + from flask import request, Flask import re @@ -7,16 +9,19 @@ app = Flask(__name__) @app.route("/direct") def direct(): pattern = re.escape(request.args['pattern']) - re.search(pattern, "") @app.route("/compile") def compile(): pattern = re.compile(re.escape(request.args['pattern'])) - pattern.search("") +@app.route("/compile_direct") +def compile_direct(): + re.compile(re.escape(request.args['pattern'])).search("") + + # if __name__ == "__main__": # app.run(debug=True) From 35f1c45d3234ddd679d852f8266d62b9449486bf Mon Sep 17 00:00:00 2001 From: jorgectf Date: Fri, 26 Mar 2021 17:01:53 +0100 Subject: [PATCH 050/168] Change from Attribute to DataFlow::CallCfgNode in getRegexMethod() --- .../src/experimental/semmle/python/Concepts.qll | 8 ++++---- .../semmle/python/frameworks/Stdlib.qll | 16 ++++++++-------- 2 files changed, 12 insertions(+), 12 deletions(-) diff --git a/python/ql/src/experimental/semmle/python/Concepts.qll b/python/ql/src/experimental/semmle/python/Concepts.qll index b4723c9e99d..8530047e60c 100644 --- a/python/ql/src/experimental/semmle/python/Concepts.qll +++ b/python/ql/src/experimental/semmle/python/Concepts.qll @@ -26,7 +26,7 @@ module RegexExecution { abstract class Range extends DataFlow::Node { abstract DataFlow::Node getRegexNode(); - abstract Attribute getRegexMethod(); + abstract DataFlow::CallCfgNode getRegexMethod(); } } @@ -43,7 +43,7 @@ class RegexExecution extends DataFlow::Node { DataFlow::Node getRegexNode() { result = range.getRegexNode() } - Attribute getRegexMethod() { result = range.getRegexMethod() } + DataFlow::CallCfgNode getRegexMethod() { result = range.getRegexMethod() } } /** Provides classes for modeling Regular Expression escape-related APIs. */ @@ -57,7 +57,7 @@ module RegexEscape { abstract class Range extends DataFlow::Node { abstract DataFlow::Node getRegexNode(); - abstract Attribute getEscapeMethod(); + abstract DataFlow::CallCfgNode getEscapeMethod(); } } @@ -74,5 +74,5 @@ class RegexEscape extends DataFlow::Node { DataFlow::Node getRegexNode() { result = range.getRegexNode() } - Attribute getEscapeMethod() { result = range.getEscapeMethod() } + DataFlow::CallCfgNode getEscapeMethod() { result = range.getEscapeMethod() } } diff --git a/python/ql/src/experimental/semmle/python/frameworks/Stdlib.qll b/python/ql/src/experimental/semmle/python/frameworks/Stdlib.qll index 0b5c57c1c67..635c7a5803f 100644 --- a/python/ql/src/experimental/semmle/python/frameworks/Stdlib.qll +++ b/python/ql/src/experimental/semmle/python/frameworks/Stdlib.qll @@ -21,22 +21,22 @@ private module Re { private class DirectRegex extends DataFlow::CallCfgNode, RegexExecution::Range { DataFlow::Node regexNode; - Attribute regexMethod; + DataFlow::CallCfgNode regexMethod; DirectRegex() { this = API::moduleImport("re").getMember(any(ReMethods m)).getACall() and regexNode = this.getArg(0) and - regexMethod = this.asExpr().(Attribute) + regexMethod = this } override DataFlow::Node getRegexNode() { result = regexNode } - override Attribute getRegexMethod() { result = regexMethod } + override DataFlow::CallCfgNode getRegexMethod() { result = regexMethod } } private class CompiledRegex extends DataFlow::CallCfgNode, RegexExecution::Range { DataFlow::Node regexNode; - Attribute regexMethod; + DataFlow::CallCfgNode regexMethod; CompiledRegex() { exists(DataFlow::CallCfgNode patternCall, DirectRegex reMethod | @@ -51,21 +51,21 @@ private module Re { override DataFlow::Node getRegexNode() { result = regexNode } - override Attribute getRegexMethod() { result = regexMethod } + override DataFlow::CallCfgNode getRegexMethod() { result = regexMethod } } class ReEscape extends DataFlow::CallCfgNode, RegexEscape::Range { DataFlow::Node regexNode; - Attribute escapeMethod; + DataFlow::CallCfgNode escapeMethod; ReEscape() { this = API::moduleImport("re").getMember("escape").getACall() and regexNode = this.getArg(0) and - escapeMethod = this.asExpr().(Attribute) + escapeMethod = this } override DataFlow::Node getRegexNode() { result = regexNode } - override Attribute getEscapeMethod() { result = escapeMethod } + override DataFlow::CallCfgNode getEscapeMethod() { result = escapeMethod } } } From 36cc7b5e3fee327db28144f142ac94268611a0c6 Mon Sep 17 00:00:00 2001 From: jorgectf Date: Fri, 26 Mar 2021 17:19:01 +0100 Subject: [PATCH 051/168] Fix CompiledRegex --- .../experimental/semmle/python/frameworks/Stdlib.qll | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/python/ql/src/experimental/semmle/python/frameworks/Stdlib.qll b/python/ql/src/experimental/semmle/python/frameworks/Stdlib.qll index 635c7a5803f..44305b0bc03 100644 --- a/python/ql/src/experimental/semmle/python/frameworks/Stdlib.qll +++ b/python/ql/src/experimental/semmle/python/frameworks/Stdlib.qll @@ -31,6 +31,7 @@ private module Re { override DataFlow::Node getRegexNode() { result = regexNode } + // pending obj.this discussion override DataFlow::CallCfgNode getRegexMethod() { result = regexMethod } } @@ -39,13 +40,13 @@ private module Re { DataFlow::CallCfgNode regexMethod; CompiledRegex() { - exists(DataFlow::CallCfgNode patternCall, DirectRegex reMethod | - this = reMethod and + exists(DataFlow::CallCfgNode patternCall, DataFlow::AttrRead reMethod | + this.getFunction() = reMethod and patternCall = API::moduleImport("re").getMember("compile").getACall() and - patternCall = reMethod.(DataFlow::AttrRead).getObject().getALocalSource() and + patternCall = reMethod.getObject().getALocalSource() and + reMethod.getAttributeName() instanceof ReMethods and regexNode = patternCall.getArg(0) and - // regexMethod is *not* worked out outside class instanciation because `CompiledRegex` focuses on re.compile(pattern).ReMethod - regexMethod = reMethod.getRegexMethod() + regexMethod = this ) } From 53d61c4fb68445ea88e8b8753aaf65341771dc78 Mon Sep 17 00:00:00 2001 From: jorgectf Date: Fri, 26 Mar 2021 19:18:14 +0100 Subject: [PATCH 052/168] Use custom Sink --- .../Security/CWE-730/RegexInjection.ql | 14 ++++++++++---- .../ql/src/experimental/semmle/python/Concepts.qll | 13 +++++++++++++ .../semmle/python/frameworks/Stdlib.qll | 2 ++ .../python/security/injection/RegexInjection.qll | 3 +-- 4 files changed, 26 insertions(+), 6 deletions(-) diff --git a/python/ql/src/experimental/Security/CWE-730/RegexInjection.ql b/python/ql/src/experimental/Security/CWE-730/RegexInjection.ql index 9d3da12f9bd..ab6d11de53c 100644 --- a/python/ql/src/experimental/Security/CWE-730/RegexInjection.ql +++ b/python/ql/src/experimental/Security/CWE-730/RegexInjection.ql @@ -16,7 +16,13 @@ import python import experimental.semmle.python.security.injection.RegexInjection import DataFlow::PathGraph -from RegexInjectionFlowConfig config, DataFlow::PathNode source, DataFlow::PathNode sink -where config.hasFlowPath(source, sink) -select sink.getNode(), source, sink, "$@ regular expression is constructed from a $@.", - sink.getNode(), "This", source.getNode(), "user-provided value" +from + RegexInjectionFlowConfig config, DataFlow::PathNode source, DataFlow::PathNode sink, + Attribute sinkAttribute +where + config.hasFlowPath(source, sink) and + sinkAttribute = sink.getNode().(RegexInjectionSink).getRegexMethod() +select sink.getNode(), source, sink, + "$@ regular expression is constructed from a $@ and executed by $@.", sink.getNode(), "This", + source.getNode(), "user-provided value", sinkAttribute, + sinkAttribute.getObject().toString() + "." + sinkAttribute.getName() diff --git a/python/ql/src/experimental/semmle/python/Concepts.qll b/python/ql/src/experimental/semmle/python/Concepts.qll index 8530047e60c..ab30e97f8d9 100644 --- a/python/ql/src/experimental/semmle/python/Concepts.qll +++ b/python/ql/src/experimental/semmle/python/Concepts.qll @@ -76,3 +76,16 @@ class RegexEscape extends DataFlow::Node { DataFlow::CallCfgNode getEscapeMethod() { result = range.getEscapeMethod() } } + +class RegexInjectionSink extends DataFlow::Node { + Attribute regexMethod; + + RegexInjectionSink() { + exists(RegexExecution reExec | + this = reExec.getRegexNode() and + regexMethod = reExec.getRegexMethod().getFunction().asExpr().(Attribute) + ) + } + + Attribute getRegexMethod() { result = regexMethod } +} diff --git a/python/ql/src/experimental/semmle/python/frameworks/Stdlib.qll b/python/ql/src/experimental/semmle/python/frameworks/Stdlib.qll index 44305b0bc03..5ba4a652244 100644 --- a/python/ql/src/experimental/semmle/python/frameworks/Stdlib.qll +++ b/python/ql/src/experimental/semmle/python/frameworks/Stdlib.qll @@ -24,6 +24,7 @@ private module Re { DataFlow::CallCfgNode regexMethod; DirectRegex() { + // this.getLocation().getFile().getBaseName().regexpMatch("^re_(good|bad)\\.py$") and // debug this = API::moduleImport("re").getMember(any(ReMethods m)).getACall() and regexNode = this.getArg(0) and regexMethod = this @@ -41,6 +42,7 @@ private module Re { CompiledRegex() { exists(DataFlow::CallCfgNode patternCall, DataFlow::AttrRead reMethod | + // this.getLocation().getFile().getBaseName().regexpMatch("^re_(good|bad)\\.py$") and // debug this.getFunction() = reMethod and patternCall = API::moduleImport("re").getMember("compile").getACall() and patternCall = reMethod.getObject().getALocalSource() and diff --git a/python/ql/src/experimental/semmle/python/security/injection/RegexInjection.qll b/python/ql/src/experimental/semmle/python/security/injection/RegexInjection.qll index fb2ef2ed192..c2112175bfa 100644 --- a/python/ql/src/experimental/semmle/python/security/injection/RegexInjection.qll +++ b/python/ql/src/experimental/semmle/python/security/injection/RegexInjection.qll @@ -8,7 +8,6 @@ import experimental.semmle.python.Concepts import semmle.python.dataflow.new.DataFlow import semmle.python.dataflow.new.TaintTracking import semmle.python.dataflow.new.RemoteFlowSources -import semmle.python.ApiGraphs /** * A taint-tracking configuration for detecting regular expression injections. @@ -18,7 +17,7 @@ class RegexInjectionFlowConfig extends TaintTracking::Configuration { override predicate isSource(DataFlow::Node source) { source instanceof RemoteFlowSource } - override predicate isSink(DataFlow::Node sink) { sink = any(RegexExecution re).getRegexNode() } + override predicate isSink(DataFlow::Node sink) { sink instanceof RegexInjectionSink } override predicate isSanitizer(DataFlow::Node sanitizer) { sanitizer = any(RegexEscape reEscape).getRegexNode() From 18ce257fc8bea35cdbc97437fd04dd1da9957a1e Mon Sep 17 00:00:00 2001 From: jorgectf Date: Sat, 27 Mar 2021 00:26:30 +0100 Subject: [PATCH 053/168] Move RegexInjectionSink to query config (qll) --- .../ql/src/experimental/semmle/python/Concepts.qll | 13 ------------- .../python/security/injection/RegexInjection.qll | 13 +++++++++++++ 2 files changed, 13 insertions(+), 13 deletions(-) diff --git a/python/ql/src/experimental/semmle/python/Concepts.qll b/python/ql/src/experimental/semmle/python/Concepts.qll index ab30e97f8d9..8530047e60c 100644 --- a/python/ql/src/experimental/semmle/python/Concepts.qll +++ b/python/ql/src/experimental/semmle/python/Concepts.qll @@ -76,16 +76,3 @@ class RegexEscape extends DataFlow::Node { DataFlow::CallCfgNode getEscapeMethod() { result = range.getEscapeMethod() } } - -class RegexInjectionSink extends DataFlow::Node { - Attribute regexMethod; - - RegexInjectionSink() { - exists(RegexExecution reExec | - this = reExec.getRegexNode() and - regexMethod = reExec.getRegexMethod().getFunction().asExpr().(Attribute) - ) - } - - Attribute getRegexMethod() { result = regexMethod } -} diff --git a/python/ql/src/experimental/semmle/python/security/injection/RegexInjection.qll b/python/ql/src/experimental/semmle/python/security/injection/RegexInjection.qll index c2112175bfa..358d884687b 100644 --- a/python/ql/src/experimental/semmle/python/security/injection/RegexInjection.qll +++ b/python/ql/src/experimental/semmle/python/security/injection/RegexInjection.qll @@ -9,6 +9,19 @@ import semmle.python.dataflow.new.DataFlow import semmle.python.dataflow.new.TaintTracking import semmle.python.dataflow.new.RemoteFlowSources +class RegexInjectionSink extends DataFlow::Node { + Attribute regexMethod; + + RegexInjectionSink() { + exists(RegexExecution reExec | + this = reExec.getRegexNode() and + regexMethod = reExec.getRegexMethod().getFunction().asExpr().(Attribute) + ) + } + + Attribute getRegexMethod() { result = regexMethod } +} + /** * A taint-tracking configuration for detecting regular expression injections. */ From e78e2ac2667740a7899436ffeae8bf9e1b4e2dfa Mon Sep 17 00:00:00 2001 From: jorgectf Date: Sat, 27 Mar 2021 00:36:00 +0100 Subject: [PATCH 054/168] Get rid of (get)regexMethod --- .../src/experimental/semmle/python/Concepts.qll | 8 -------- .../semmle/python/frameworks/Stdlib.qll | 16 +++------------- .../python/security/injection/RegexInjection.qll | 2 +- 3 files changed, 4 insertions(+), 22 deletions(-) diff --git a/python/ql/src/experimental/semmle/python/Concepts.qll b/python/ql/src/experimental/semmle/python/Concepts.qll index 8530047e60c..46bb8ad4ee3 100644 --- a/python/ql/src/experimental/semmle/python/Concepts.qll +++ b/python/ql/src/experimental/semmle/python/Concepts.qll @@ -25,8 +25,6 @@ module RegexExecution { */ abstract class Range extends DataFlow::Node { abstract DataFlow::Node getRegexNode(); - - abstract DataFlow::CallCfgNode getRegexMethod(); } } @@ -42,8 +40,6 @@ class RegexExecution extends DataFlow::Node { RegexExecution() { this = range } DataFlow::Node getRegexNode() { result = range.getRegexNode() } - - DataFlow::CallCfgNode getRegexMethod() { result = range.getRegexMethod() } } /** Provides classes for modeling Regular Expression escape-related APIs. */ @@ -56,8 +52,6 @@ module RegexEscape { */ abstract class Range extends DataFlow::Node { abstract DataFlow::Node getRegexNode(); - - abstract DataFlow::CallCfgNode getEscapeMethod(); } } @@ -73,6 +67,4 @@ class RegexEscape extends DataFlow::Node { RegexEscape() { this = range } DataFlow::Node getRegexNode() { result = range.getRegexNode() } - - DataFlow::CallCfgNode getEscapeMethod() { result = range.getEscapeMethod() } } diff --git a/python/ql/src/experimental/semmle/python/frameworks/Stdlib.qll b/python/ql/src/experimental/semmle/python/frameworks/Stdlib.qll index 5ba4a652244..28f07eddff4 100644 --- a/python/ql/src/experimental/semmle/python/frameworks/Stdlib.qll +++ b/python/ql/src/experimental/semmle/python/frameworks/Stdlib.qll @@ -26,14 +26,10 @@ private module Re { DirectRegex() { // this.getLocation().getFile().getBaseName().regexpMatch("^re_(good|bad)\\.py$") and // debug this = API::moduleImport("re").getMember(any(ReMethods m)).getACall() and - regexNode = this.getArg(0) and - regexMethod = this + regexNode = this.getArg(0) } override DataFlow::Node getRegexNode() { result = regexNode } - - // pending obj.this discussion - override DataFlow::CallCfgNode getRegexMethod() { result = regexMethod } } private class CompiledRegex extends DataFlow::CallCfgNode, RegexExecution::Range { @@ -47,14 +43,11 @@ private module Re { patternCall = API::moduleImport("re").getMember("compile").getACall() and patternCall = reMethod.getObject().getALocalSource() and reMethod.getAttributeName() instanceof ReMethods and - regexNode = patternCall.getArg(0) and - regexMethod = this + regexNode = patternCall.getArg(0) ) } override DataFlow::Node getRegexNode() { result = regexNode } - - override DataFlow::CallCfgNode getRegexMethod() { result = regexMethod } } class ReEscape extends DataFlow::CallCfgNode, RegexEscape::Range { @@ -63,12 +56,9 @@ private module Re { ReEscape() { this = API::moduleImport("re").getMember("escape").getACall() and - regexNode = this.getArg(0) and - escapeMethod = this + regexNode = this.getArg(0) } override DataFlow::Node getRegexNode() { result = regexNode } - - override DataFlow::CallCfgNode getEscapeMethod() { result = escapeMethod } } } diff --git a/python/ql/src/experimental/semmle/python/security/injection/RegexInjection.qll b/python/ql/src/experimental/semmle/python/security/injection/RegexInjection.qll index 358d884687b..b77eae22371 100644 --- a/python/ql/src/experimental/semmle/python/security/injection/RegexInjection.qll +++ b/python/ql/src/experimental/semmle/python/security/injection/RegexInjection.qll @@ -15,7 +15,7 @@ class RegexInjectionSink extends DataFlow::Node { RegexInjectionSink() { exists(RegexExecution reExec | this = reExec.getRegexNode() and - regexMethod = reExec.getRegexMethod().getFunction().asExpr().(Attribute) + regexMethod = reExec.asExpr().(Attribute) ) } From a5850f4a9908e99df007cabaeae2460d920b4772 Mon Sep 17 00:00:00 2001 From: jorgectf Date: Sat, 27 Mar 2021 11:32:58 +0100 Subject: [PATCH 055/168] Use getRegexModule to know used lib --- .../src/experimental/Security/CWE-730/RegexInjection.ql | 8 ++++---- python/ql/src/experimental/semmle/python/Concepts.qll | 4 ++++ .../src/experimental/semmle/python/frameworks/Stdlib.qll | 7 ++++--- .../semmle/python/security/injection/RegexInjection.qll | 6 +++--- 4 files changed, 15 insertions(+), 10 deletions(-) diff --git a/python/ql/src/experimental/Security/CWE-730/RegexInjection.ql b/python/ql/src/experimental/Security/CWE-730/RegexInjection.ql index ab6d11de53c..422c57c9bff 100644 --- a/python/ql/src/experimental/Security/CWE-730/RegexInjection.ql +++ b/python/ql/src/experimental/Security/CWE-730/RegexInjection.ql @@ -18,11 +18,11 @@ import DataFlow::PathGraph from RegexInjectionFlowConfig config, DataFlow::PathNode source, DataFlow::PathNode sink, - Attribute sinkAttribute + RegexInjectionSink castedSink where config.hasFlowPath(source, sink) and - sinkAttribute = sink.getNode().(RegexInjectionSink).getRegexMethod() + castedSink = sink.getNode() select sink.getNode(), source, sink, "$@ regular expression is constructed from a $@ and executed by $@.", sink.getNode(), "This", - source.getNode(), "user-provided value", sinkAttribute, - sinkAttribute.getObject().toString() + "." + sinkAttribute.getName() + source.getNode(), "user-provided value", castedSink, + castedSink.getRegexModule() + "." + castedSink.asExpr().(Attribute).getName() diff --git a/python/ql/src/experimental/semmle/python/Concepts.qll b/python/ql/src/experimental/semmle/python/Concepts.qll index 46bb8ad4ee3..6bdf1a37bb0 100644 --- a/python/ql/src/experimental/semmle/python/Concepts.qll +++ b/python/ql/src/experimental/semmle/python/Concepts.qll @@ -25,6 +25,8 @@ module RegexExecution { */ abstract class Range extends DataFlow::Node { abstract DataFlow::Node getRegexNode(); + + abstract string getRegexModule(); } } @@ -40,6 +42,8 @@ class RegexExecution extends DataFlow::Node { RegexExecution() { this = range } DataFlow::Node getRegexNode() { result = range.getRegexNode() } + + string getRegexModule() { result = range.getRegexModule() } } /** Provides classes for modeling Regular Expression escape-related APIs. */ diff --git a/python/ql/src/experimental/semmle/python/frameworks/Stdlib.qll b/python/ql/src/experimental/semmle/python/frameworks/Stdlib.qll index 28f07eddff4..a4a860711b8 100644 --- a/python/ql/src/experimental/semmle/python/frameworks/Stdlib.qll +++ b/python/ql/src/experimental/semmle/python/frameworks/Stdlib.qll @@ -21,15 +21,15 @@ private module Re { private class DirectRegex extends DataFlow::CallCfgNode, RegexExecution::Range { DataFlow::Node regexNode; - DataFlow::CallCfgNode regexMethod; DirectRegex() { - // this.getLocation().getFile().getBaseName().regexpMatch("^re_(good|bad)\\.py$") and // debug this = API::moduleImport("re").getMember(any(ReMethods m)).getACall() and regexNode = this.getArg(0) } override DataFlow::Node getRegexNode() { result = regexNode } + + override string getRegexModule() { result = "re" } } private class CompiledRegex extends DataFlow::CallCfgNode, RegexExecution::Range { @@ -38,7 +38,6 @@ private module Re { CompiledRegex() { exists(DataFlow::CallCfgNode patternCall, DataFlow::AttrRead reMethod | - // this.getLocation().getFile().getBaseName().regexpMatch("^re_(good|bad)\\.py$") and // debug this.getFunction() = reMethod and patternCall = API::moduleImport("re").getMember("compile").getACall() and patternCall = reMethod.getObject().getALocalSource() and @@ -48,6 +47,8 @@ private module Re { } override DataFlow::Node getRegexNode() { result = regexNode } + + override string getRegexModule() { result = "re" } } class ReEscape extends DataFlow::CallCfgNode, RegexEscape::Range { diff --git a/python/ql/src/experimental/semmle/python/security/injection/RegexInjection.qll b/python/ql/src/experimental/semmle/python/security/injection/RegexInjection.qll index b77eae22371..c4e06abf390 100644 --- a/python/ql/src/experimental/semmle/python/security/injection/RegexInjection.qll +++ b/python/ql/src/experimental/semmle/python/security/injection/RegexInjection.qll @@ -10,16 +10,16 @@ import semmle.python.dataflow.new.TaintTracking import semmle.python.dataflow.new.RemoteFlowSources class RegexInjectionSink extends DataFlow::Node { - Attribute regexMethod; + string regexModule; RegexInjectionSink() { exists(RegexExecution reExec | this = reExec.getRegexNode() and - regexMethod = reExec.asExpr().(Attribute) + regexModule = reExec.getRegexModule() ) } - Attribute getRegexMethod() { result = regexMethod } + string getRegexModule() { result = regexModule } } /** From f75110365fdc9cef4c18ba4a5395f1f8699bd54b Mon Sep 17 00:00:00 2001 From: jorgectf Date: Sat, 27 Mar 2021 19:54:30 +0100 Subject: [PATCH 056/168] Fix Sink utilization in select --- .../experimental/Security/CWE-730/RegexInjection.ql | 13 +++++-------- .../python/security/injection/RegexInjection.qll | 6 +++++- 2 files changed, 10 insertions(+), 9 deletions(-) diff --git a/python/ql/src/experimental/Security/CWE-730/RegexInjection.ql b/python/ql/src/experimental/Security/CWE-730/RegexInjection.ql index 422c57c9bff..73178ca1c03 100644 --- a/python/ql/src/experimental/Security/CWE-730/RegexInjection.ql +++ b/python/ql/src/experimental/Security/CWE-730/RegexInjection.ql @@ -16,13 +16,10 @@ import python import experimental.semmle.python.security.injection.RegexInjection import DataFlow::PathGraph -from - RegexInjectionFlowConfig config, DataFlow::PathNode source, DataFlow::PathNode sink, - RegexInjectionSink castedSink -where - config.hasFlowPath(source, sink) and - castedSink = sink.getNode() +from RegexInjectionFlowConfig config, DataFlow::PathNode source, DataFlow::PathNode sink +where config.hasFlowPath(source, sink) select sink.getNode(), source, sink, "$@ regular expression is constructed from a $@ and executed by $@.", sink.getNode(), "This", - source.getNode(), "user-provided value", castedSink, - castedSink.getRegexModule() + "." + castedSink.asExpr().(Attribute).getName() + source.getNode(), "user-provided value", sink.getNode(), + sink.getNode().(RegexInjectionSink).getRegexModule() + "." + + sink.getNode().(RegexInjectionSink).getRegexMethod().getName() diff --git a/python/ql/src/experimental/semmle/python/security/injection/RegexInjection.qll b/python/ql/src/experimental/semmle/python/security/injection/RegexInjection.qll index c4e06abf390..2f0fa71ef79 100644 --- a/python/ql/src/experimental/semmle/python/security/injection/RegexInjection.qll +++ b/python/ql/src/experimental/semmle/python/security/injection/RegexInjection.qll @@ -11,15 +11,19 @@ import semmle.python.dataflow.new.RemoteFlowSources class RegexInjectionSink extends DataFlow::Node { string regexModule; + Attribute regexMethod; RegexInjectionSink() { exists(RegexExecution reExec | this = reExec.getRegexNode() and - regexModule = reExec.getRegexModule() + regexModule = reExec.getRegexModule() and + regexMethod = reExec.(DataFlow::CallCfgNode).getFunction().asExpr().(Attribute) ) } string getRegexModule() { result = regexModule } + + Attribute getRegexMethod() { result = regexMethod } } /** From 66ee67a7817037495f28909d2c6b75be4006a7e7 Mon Sep 17 00:00:00 2001 From: jorgectf Date: Sat, 27 Mar 2021 19:59:39 +0100 Subject: [PATCH 057/168] Polished select statement --- .../Security/CWE-730/RegexInjection.ql | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/python/ql/src/experimental/Security/CWE-730/RegexInjection.ql b/python/ql/src/experimental/Security/CWE-730/RegexInjection.ql index 73178ca1c03..77355fdf30d 100644 --- a/python/ql/src/experimental/Security/CWE-730/RegexInjection.ql +++ b/python/ql/src/experimental/Security/CWE-730/RegexInjection.ql @@ -16,10 +16,14 @@ import python import experimental.semmle.python.security.injection.RegexInjection import DataFlow::PathGraph -from RegexInjectionFlowConfig config, DataFlow::PathNode source, DataFlow::PathNode sink -where config.hasFlowPath(source, sink) +from + RegexInjectionFlowConfig config, DataFlow::PathNode source, DataFlow::PathNode sink, + RegexInjectionSink castedSink, Attribute methodAttribute +where + config.hasFlowPath(source, sink) and + castedSink = sink.getNode() and + methodAttribute = castedSink.getRegexMethod() select sink.getNode(), source, sink, "$@ regular expression is constructed from a $@ and executed by $@.", sink.getNode(), "This", - source.getNode(), "user-provided value", sink.getNode(), - sink.getNode().(RegexInjectionSink).getRegexModule() + "." + - sink.getNode().(RegexInjectionSink).getRegexMethod().getName() + source.getNode(), "user-provided value", methodAttribute, + castedSink.getRegexModule() + "." + methodAttribute.getName() From c54f08f33a00571ac2b1454c87b2cefbfd3e8b4d Mon Sep 17 00:00:00 2001 From: jorgectf Date: Sat, 27 Mar 2021 20:13:32 +0100 Subject: [PATCH 058/168] Improve qhelp --- .../Security/CWE-730/RegexInjection.qhelp | 65 ++++++++++++++----- 1 file changed, 47 insertions(+), 18 deletions(-) diff --git a/python/ql/src/experimental/Security/CWE-730/RegexInjection.qhelp b/python/ql/src/experimental/Security/CWE-730/RegexInjection.qhelp index 2e3d238daa4..0fc0445e07b 100644 --- a/python/ql/src/experimental/Security/CWE-730/RegexInjection.qhelp +++ b/python/ql/src/experimental/Security/CWE-730/RegexInjection.qhelp @@ -1,23 +1,52 @@ - + - - -

    If a regular expression is built by a not escaped user-provided value, a user is likely to be able to cause a Denial of Service.

    -
    + +

    + Constructing a regular expression with unsanitized user input is dangerous as a malicious user may + be able to modify the meaning of the expression. In particular, such a user may be able to provide + a regular expression fragment that takes exponential time in the worst case, and use that to + perform a Denial of Service attack. +

    +
    - -

    In case user input must compose a regular expression, it should be escaped with functions such as re.escape. - + +

    + Before embedding user input into a regular expression, use a sanitization function such as + re.escape to escape meta-characters that have a special meaning regarding + regular expressions' syntax. +

    +
    - -
  • - OWASP - Regular Expression DoS -
  • -
  • - SonarSource - RSPEC-2631 -
  • -
    + +

    + The following examples are based on a simple Flask web server environment. +

    +

    + The following example shows a HTTP request parameter that is used to construct a regular expression + without sanitizing it first: +

    + +

    + Instead, the request parameter should be sanitized first, for example using the function + re.escape. This ensures that the user cannot insert characters which have a + special meaning in regular expressions. +

    + +
    + +
  • + OWASP: + Regular expression Denial of Service - ReDoS. +
  • +
  • + Wikipedia: ReDoS. +
  • +
  • + Python docs: re. +
  • +
  • + SonarSource: RSPEC-2631 +
  • +
    \ No newline at end of file From 0e169ba10ebfbc1c0fe56fc347d3abb857577b9f Mon Sep 17 00:00:00 2001 From: jorgectf Date: Sat, 27 Mar 2021 20:14:11 +0100 Subject: [PATCH 059/168] Format qhelp --- .../Security/CWE-730/RegexInjection.qhelp | 96 +++++++++---------- 1 file changed, 48 insertions(+), 48 deletions(-) diff --git a/python/ql/src/experimental/Security/CWE-730/RegexInjection.qhelp b/python/ql/src/experimental/Security/CWE-730/RegexInjection.qhelp index 0fc0445e07b..3e635017da1 100644 --- a/python/ql/src/experimental/Security/CWE-730/RegexInjection.qhelp +++ b/python/ql/src/experimental/Security/CWE-730/RegexInjection.qhelp @@ -1,52 +1,52 @@ - + + + +

    + Constructing a regular expression with unsanitized user input is dangerous as a malicious user may + be able to modify the meaning of the expression. In particular, such a user may be able to provide + a regular expression fragment that takes exponential time in the worst case, and use that to + perform a Denial of Service attack. +

    +
    - -

    - Constructing a regular expression with unsanitized user input is dangerous as a malicious user may - be able to modify the meaning of the expression. In particular, such a user may be able to provide - a regular expression fragment that takes exponential time in the worst case, and use that to - perform a Denial of Service attack. -

    -
    + +

    + Before embedding user input into a regular expression, use a sanitization function such as + re.escape to escape meta-characters that have a special meaning regarding + regular expressions' syntax. +

    +
    - -

    - Before embedding user input into a regular expression, use a sanitization function such as - re.escape to escape meta-characters that have a special meaning regarding - regular expressions' syntax. -

    -
    + +

    + The following examples are based on a simple Flask web server environment. +

    +

    + The following example shows a HTTP request parameter that is used to construct a regular expression + without sanitizing it first: +

    + +

    + Instead, the request parameter should be sanitized first, for example using the function + re.escape. This ensures that the user cannot insert characters which have a + special meaning in regular expressions. +

    + +
    - -

    - The following examples are based on a simple Flask web server environment. -

    -

    - The following example shows a HTTP request parameter that is used to construct a regular expression - without sanitizing it first: -

    - -

    - Instead, the request parameter should be sanitized first, for example using the function - re.escape. This ensures that the user cannot insert characters which have a - special meaning in regular expressions. -

    - -
    - - -
  • - OWASP: - Regular expression Denial of Service - ReDoS. -
  • -
  • - Wikipedia: ReDoS. -
  • -
  • - Python docs: re. -
  • -
  • - SonarSource: RSPEC-2631 -
  • -
    + +
  • + OWASP: + Regular expression Denial of Service - ReDoS. +
  • +
  • + Wikipedia: ReDoS. +
  • +
  • + Python docs: re. +
  • +
  • + SonarSource: RSPEC-2631 +
  • +
    \ No newline at end of file From d49c23fe67035e676148ee9b690b00e2e894ada0 Mon Sep 17 00:00:00 2001 From: jorgectf Date: Sat, 27 Mar 2021 20:26:55 +0100 Subject: [PATCH 060/168] Improve tests' readability --- .../Security/CWE-730/unit_tests/re_bad.py | 30 +++++++++++++---- .../Security/CWE-730/unit_tests/re_good.py | 32 +++++++++++++++---- 2 files changed, 48 insertions(+), 14 deletions(-) diff --git a/python/ql/src/experimental/Security/CWE-730/unit_tests/re_bad.py b/python/ql/src/experimental/Security/CWE-730/unit_tests/re_bad.py index 172fb94bab8..55b306c8857 100644 --- a/python/ql/src/experimental/Security/CWE-730/unit_tests/re_bad.py +++ b/python/ql/src/experimental/Security/CWE-730/unit_tests/re_bad.py @@ -1,5 +1,3 @@ -# move outside test folder - from flask import request, Flask import re @@ -8,19 +6,37 @@ app = Flask(__name__) @app.route("/direct") def direct(): - pattern = request.args['pattern'] - re.search(pattern, "") + """ + A RemoteFlowSource is used directly as re.search's pattern + """ + unsafe_pattern = request.args["pattern"] + re.search(unsafe_pattern, "") + + +# A RemoteFlowSource is used directly as re.compile's pattern @app.route("/compile") def compile(): - pattern = re.compile(request.args['pattern']) - pattern.search("") + """ + A RemoteFlowSource is used directly as re.compile's pattern + which also executes .search() + """ + + unsafe_pattern = request.args["pattern"] + compiled_pattern = re.compile(unsafe_pattern) + compiled_pattern.search("") @app.route("/compile_direct") def compile_direct(): - re.compile(request.args['pattern']).search("") + """ + A RemoteFlowSource is used directly as re.compile's pattern + which also executes .search() in the same line + """ + + unsafe_pattern = request.args["pattern"] + re.compile(unsafe_pattern).search("") # if __name__ == "__main__": # app.run(debug=True) diff --git a/python/ql/src/experimental/Security/CWE-730/unit_tests/re_good.py b/python/ql/src/experimental/Security/CWE-730/unit_tests/re_good.py index a226d7a8899..6dc58b87f85 100644 --- a/python/ql/src/experimental/Security/CWE-730/unit_tests/re_good.py +++ b/python/ql/src/experimental/Security/CWE-730/unit_tests/re_good.py @@ -1,5 +1,3 @@ -# move outside test folder - from flask import request, Flask import re @@ -8,19 +6,39 @@ app = Flask(__name__) @app.route("/direct") def direct(): - pattern = re.escape(request.args['pattern']) - re.search(pattern, "") + """ + A RemoteFlowSource is escaped by re.escape and then used as + re'search pattern + """ + + unsafe_pattern = request.args['pattern'] + safe_pattern = re.escape(unsafe_pattern) + re.search(safe_pattern, "") @app.route("/compile") def compile(): - pattern = re.compile(re.escape(request.args['pattern'])) - pattern.search("") + """ + A RemoteFlowSource is escaped by re.escape and used as re.compile's + pattern which also executes .search() + """ + + unsafe_pattern = request.args['pattern'] + safe_pattern = re.escape(unsafe_pattern) + compiled_pattern = re.compile(safe_pattern) + compiled_pattern.search("") @app.route("/compile_direct") def compile_direct(): - re.compile(re.escape(request.args['pattern'])).search("") + """ + A RemoteFlowSource is escaped by re.escape and then used as re.compile's + pattern which also executes .search() in the same line + """ + + unsafe_pattern = request.args['pattern'] + safe_pattern = re.escape(unsafe_pattern) + re.compile(safe_pattern).search("") # if __name__ == "__main__": From d4a89b2fd822689f626523d0da786f077ca7464f Mon Sep 17 00:00:00 2001 From: jorgectf Date: Sat, 27 Mar 2021 20:29:11 +0100 Subject: [PATCH 061/168] Fix qhelp typo while converting to python's regex injection --- .../ql/src/experimental/Security/CWE-730/RegexInjection.qhelp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/python/ql/src/experimental/Security/CWE-730/RegexInjection.qhelp b/python/ql/src/experimental/Security/CWE-730/RegexInjection.qhelp index 3e635017da1..3c1d5907a77 100644 --- a/python/ql/src/experimental/Security/CWE-730/RegexInjection.qhelp +++ b/python/ql/src/experimental/Security/CWE-730/RegexInjection.qhelp @@ -31,7 +31,7 @@ re.escape. This ensures that the user cannot insert characters which have a special meaning in regular expressions.

    - + From b6721971dd9149992fd53524a99f878c7220fcc2 Mon Sep 17 00:00:00 2001 From: jorgectf Date: Sat, 27 Mar 2021 20:54:46 +0100 Subject: [PATCH 062/168] Improve code comments --- .../experimental/semmle/python/Concepts.qll | 9 +++++ .../semmle/python/frameworks/Stdlib.qll | 38 ++++++++++++++++--- .../security/injection/RegexInjection.qll | 5 +++ 3 files changed, 46 insertions(+), 6 deletions(-) diff --git a/python/ql/src/experimental/semmle/python/Concepts.qll b/python/ql/src/experimental/semmle/python/Concepts.qll index 6bdf1a37bb0..b325925f321 100644 --- a/python/ql/src/experimental/semmle/python/Concepts.qll +++ b/python/ql/src/experimental/semmle/python/Concepts.qll @@ -24,8 +24,14 @@ module RegexExecution { * extend `RegexExecution` instead. */ abstract class Range extends DataFlow::Node { + /** + * Gets the argument containing the executed expression. + */ abstract DataFlow::Node getRegexNode(); + /** + * Gets the library used to execute the regular expression. + */ abstract string getRegexModule(); } } @@ -55,6 +61,9 @@ module RegexEscape { * extend `RegexEscape` instead. */ abstract class Range extends DataFlow::Node { + /** + * Gets the argument containing the escaped expression. + */ abstract DataFlow::Node getRegexNode(); } } diff --git a/python/ql/src/experimental/semmle/python/frameworks/Stdlib.qll b/python/ql/src/experimental/semmle/python/frameworks/Stdlib.qll index a4a860711b8..c825bcfc086 100644 --- a/python/ql/src/experimental/semmle/python/frameworks/Stdlib.qll +++ b/python/ql/src/experimental/semmle/python/frameworks/Stdlib.qll @@ -10,20 +10,33 @@ private import semmle.python.dataflow.new.RemoteFlowSources private import experimental.semmle.python.Concepts private import semmle.python.ApiGraphs -/** Provides models for the Python standard library. */ +/** + * Provides models for Python's `re` library. + * + * See https://docs.python.org/3/library/re.html + */ private module Re { - /** List of re methods. */ - private class ReMethods extends string { - ReMethods() { + /** + * List of `re` methods immediately executing an expression. + * + * See https://docs.python.org/3/library/re.html#module-contents + */ + private class RegexExecutionMethods extends string { + RegexExecutionMethods() { this in ["match", "fullmatch", "search", "split", "findall", "finditer", "sub", "subn"] } } + /** + * A class to find `re` methods immediately executing an expression. + * + * See `RegexExecutionMethods` + */ private class DirectRegex extends DataFlow::CallCfgNode, RegexExecution::Range { DataFlow::Node regexNode; DirectRegex() { - this = API::moduleImport("re").getMember(any(ReMethods m)).getACall() and + this = API::moduleImport("re").getMember(any(RegexExecutionMethods m)).getACall() and regexNode = this.getArg(0) } @@ -32,6 +45,14 @@ private module Re { override string getRegexModule() { result = "re" } } + /** + * A class to find `re` methods immediately executing an expression from a + * compiled expression by `re.compile`. + * + * See `RegexExecutionMethods` + * + * See https://docs.python.org/3/library/re.html#regular-expression-objects + */ private class CompiledRegex extends DataFlow::CallCfgNode, RegexExecution::Range { DataFlow::Node regexNode; DataFlow::CallCfgNode regexMethod; @@ -41,7 +62,7 @@ private module Re { this.getFunction() = reMethod and patternCall = API::moduleImport("re").getMember("compile").getACall() and patternCall = reMethod.getObject().getALocalSource() and - reMethod.getAttributeName() instanceof ReMethods and + reMethod.getAttributeName() instanceof RegexExecutionMethods and regexNode = patternCall.getArg(0) ) } @@ -51,6 +72,11 @@ private module Re { override string getRegexModule() { result = "re" } } + /** + * A class to find `re` methods escaping an expression. + * + * See https://docs.python.org/3/library/re.html#re.escape + */ class ReEscape extends DataFlow::CallCfgNode, RegexEscape::Range { DataFlow::Node regexNode; DataFlow::CallCfgNode escapeMethod; diff --git a/python/ql/src/experimental/semmle/python/security/injection/RegexInjection.qll b/python/ql/src/experimental/semmle/python/security/injection/RegexInjection.qll index 2f0fa71ef79..2ce6f06edc0 100644 --- a/python/ql/src/experimental/semmle/python/security/injection/RegexInjection.qll +++ b/python/ql/src/experimental/semmle/python/security/injection/RegexInjection.qll @@ -9,6 +9,11 @@ import semmle.python.dataflow.new.DataFlow import semmle.python.dataflow.new.TaintTracking import semmle.python.dataflow.new.RemoteFlowSources +/** + * A class to find methods executing regular expressions. + * + * See `RegexExecution` + */ class RegexInjectionSink extends DataFlow::Node { string regexModule; Attribute regexMethod; From 36555149245fc55d0fd72dc1953342956a386c71 Mon Sep 17 00:00:00 2001 From: jorgectf Date: Sat, 27 Mar 2021 21:01:07 +0100 Subject: [PATCH 063/168] Fix ambiguity --- python/ql/src/experimental/semmle/python/frameworks/Stdlib.qll | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/python/ql/src/experimental/semmle/python/frameworks/Stdlib.qll b/python/ql/src/experimental/semmle/python/frameworks/Stdlib.qll index c825bcfc086..849dd47467d 100644 --- a/python/ql/src/experimental/semmle/python/frameworks/Stdlib.qll +++ b/python/ql/src/experimental/semmle/python/frameworks/Stdlib.qll @@ -46,8 +46,7 @@ private module Re { } /** - * A class to find `re` methods immediately executing an expression from a - * compiled expression by `re.compile`. + * A class to find `re` methods immediately executing a compiled expression by `re.compile`. * * See `RegexExecutionMethods` * From fc27c6c5470043c4c6b5cf96d2abded89ef152e6 Mon Sep 17 00:00:00 2001 From: jorgectf Date: Sat, 27 Mar 2021 21:03:19 +0100 Subject: [PATCH 064/168] Fix RegexExecution ambiguity --- python/ql/src/experimental/semmle/python/Concepts.qll | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/python/ql/src/experimental/semmle/python/Concepts.qll b/python/ql/src/experimental/semmle/python/Concepts.qll index b325925f321..08131064912 100644 --- a/python/ql/src/experimental/semmle/python/Concepts.qll +++ b/python/ql/src/experimental/semmle/python/Concepts.qll @@ -18,7 +18,7 @@ private import semmle.python.ApiGraphs /** Provides classes for modeling Regular Expression-related APIs. */ module RegexExecution { /** - * A data-flow node that works with regular expressions immediately executing an expression. + * A data-flow node that collects methods immediately executing an expression. * * Extend this class to model new APIs. If you want to refine existing API models, * extend `RegexExecution` instead. @@ -37,7 +37,7 @@ module RegexExecution { } /** - * A data-flow node that works with regular expressions immediately executing an expression. + * A data-flow node that collect methods immediately executing an expression. * * Extend this class to refine existing API models. If you want to model new APIs, * extend `RegexExecution::Range` instead. From 03825a6052a1d9412e02aaca7990f094e596f9a3 Mon Sep 17 00:00:00 2001 From: jorgectf Date: Sat, 27 Mar 2021 21:05:49 +0100 Subject: [PATCH 065/168] Add comment to Sink's predicates --- .../semmle/python/security/injection/RegexInjection.qll | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/python/ql/src/experimental/semmle/python/security/injection/RegexInjection.qll b/python/ql/src/experimental/semmle/python/security/injection/RegexInjection.qll index 2ce6f06edc0..a7a0526f750 100644 --- a/python/ql/src/experimental/semmle/python/security/injection/RegexInjection.qll +++ b/python/ql/src/experimental/semmle/python/security/injection/RegexInjection.qll @@ -26,8 +26,14 @@ class RegexInjectionSink extends DataFlow::Node { ) } + /** + * Gets the argument containing the executed expression. + */ string getRegexModule() { result = regexModule } + /** + * Gets the mthod used to execute the regular expression. + */ Attribute getRegexMethod() { result = regexMethod } } From ec85ee453777eed7053859692a257cb89da5400f Mon Sep 17 00:00:00 2001 From: jorgectf Date: Sat, 27 Mar 2021 21:07:53 +0100 Subject: [PATCH 066/168] Sink's predicate typo --- .../semmle/python/security/injection/RegexInjection.qll | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/python/ql/src/experimental/semmle/python/security/injection/RegexInjection.qll b/python/ql/src/experimental/semmle/python/security/injection/RegexInjection.qll index a7a0526f750..7b7b08cacab 100644 --- a/python/ql/src/experimental/semmle/python/security/injection/RegexInjection.qll +++ b/python/ql/src/experimental/semmle/python/security/injection/RegexInjection.qll @@ -32,7 +32,7 @@ class RegexInjectionSink extends DataFlow::Node { string getRegexModule() { result = regexModule } /** - * Gets the mthod used to execute the regular expression. + * Gets the method used to execute the regular expression. */ Attribute getRegexMethod() { result = regexMethod } } From d401d18e7193558eedd1c17c82b6f46ef504d39f Mon Sep 17 00:00:00 2001 From: jorgectf Date: Sun, 28 Mar 2021 01:22:12 +0100 Subject: [PATCH 067/168] Add .expected and qlref --- .../Security/CWE-730/RegexInjection.expected | 27 +++++++++++++++++++ .../Security/CWE-730/RegexInjection.qlref | 1 + 2 files changed, 28 insertions(+) create mode 100644 python/ql/src/experimental/Security/CWE-730/RegexInjection.expected create mode 100644 python/ql/src/experimental/Security/CWE-730/RegexInjection.qlref diff --git a/python/ql/src/experimental/Security/CWE-730/RegexInjection.expected b/python/ql/src/experimental/Security/CWE-730/RegexInjection.expected new file mode 100644 index 00000000000..9886df2904d --- /dev/null +++ b/python/ql/src/experimental/Security/CWE-730/RegexInjection.expected @@ -0,0 +1,27 @@ +edges +| re_bad.py:13:22:13:28 | ControlFlowNode for request | re_bad.py:13:22:13:33 | ControlFlowNode for Attribute | +| re_bad.py:13:22:13:33 | ControlFlowNode for Attribute | re_bad.py:13:22:13:44 | ControlFlowNode for Subscript | +| re_bad.py:13:22:13:44 | ControlFlowNode for Subscript | re_bad.py:14:15:14:28 | ControlFlowNode for unsafe_pattern | +| re_bad.py:26:22:26:28 | ControlFlowNode for request | re_bad.py:26:22:26:33 | ControlFlowNode for Attribute | +| re_bad.py:26:22:26:33 | ControlFlowNode for Attribute | re_bad.py:26:22:26:44 | ControlFlowNode for Subscript | +| re_bad.py:26:22:26:44 | ControlFlowNode for Subscript | re_bad.py:27:35:27:48 | ControlFlowNode for unsafe_pattern | +| re_bad.py:38:22:38:28 | ControlFlowNode for request | re_bad.py:38:22:38:33 | ControlFlowNode for Attribute | +| re_bad.py:38:22:38:33 | ControlFlowNode for Attribute | re_bad.py:38:22:38:44 | ControlFlowNode for Subscript | +| re_bad.py:38:22:38:44 | ControlFlowNode for Subscript | re_bad.py:39:16:39:29 | ControlFlowNode for unsafe_pattern | +nodes +| re_bad.py:13:22:13:28 | ControlFlowNode for request | semmle.label | ControlFlowNode for request | +| re_bad.py:13:22:13:33 | ControlFlowNode for Attribute | semmle.label | ControlFlowNode for Attribute | +| re_bad.py:13:22:13:44 | ControlFlowNode for Subscript | semmle.label | ControlFlowNode for Subscript | +| re_bad.py:14:15:14:28 | ControlFlowNode for unsafe_pattern | semmle.label | ControlFlowNode for unsafe_pattern | +| re_bad.py:26:22:26:28 | ControlFlowNode for request | semmle.label | ControlFlowNode for request | +| re_bad.py:26:22:26:33 | ControlFlowNode for Attribute | semmle.label | ControlFlowNode for Attribute | +| re_bad.py:26:22:26:44 | ControlFlowNode for Subscript | semmle.label | ControlFlowNode for Subscript | +| re_bad.py:27:35:27:48 | ControlFlowNode for unsafe_pattern | semmle.label | ControlFlowNode for unsafe_pattern | +| re_bad.py:38:22:38:28 | ControlFlowNode for request | semmle.label | ControlFlowNode for request | +| re_bad.py:38:22:38:33 | ControlFlowNode for Attribute | semmle.label | ControlFlowNode for Attribute | +| re_bad.py:38:22:38:44 | ControlFlowNode for Subscript | semmle.label | ControlFlowNode for Subscript | +| re_bad.py:39:16:39:29 | ControlFlowNode for unsafe_pattern | semmle.label | ControlFlowNode for unsafe_pattern | +#select +| re_bad.py:14:15:14:28 | ControlFlowNode for unsafe_pattern | re_bad.py:13:22:13:28 | ControlFlowNode for request | re_bad.py:14:15:14:28 | ControlFlowNode for unsafe_pattern | $@ regular expression is constructed from a $@ and executed by $@. | re_bad.py:14:15:14:28 | ControlFlowNode for unsafe_pattern | This | re_bad.py:13:22:13:28 | ControlFlowNode for request | user-provided value | re_bad.py:14:5:14:13 | Attribute | re.search | +| re_bad.py:27:35:27:48 | ControlFlowNode for unsafe_pattern | re_bad.py:26:22:26:28 | ControlFlowNode for request | re_bad.py:27:35:27:48 | ControlFlowNode for unsafe_pattern | $@ regular expression is constructed from a $@ and executed by $@. | re_bad.py:27:35:27:48 | ControlFlowNode for unsafe_pattern | This | re_bad.py:26:22:26:28 | ControlFlowNode for request | user-provided value | re_bad.py:28:5:28:27 | Attribute | re.search | +| re_bad.py:39:16:39:29 | ControlFlowNode for unsafe_pattern | re_bad.py:38:22:38:28 | ControlFlowNode for request | re_bad.py:39:16:39:29 | ControlFlowNode for unsafe_pattern | $@ regular expression is constructed from a $@ and executed by $@. | re_bad.py:39:16:39:29 | ControlFlowNode for unsafe_pattern | This | re_bad.py:38:22:38:28 | ControlFlowNode for request | user-provided value | re_bad.py:39:5:39:37 | Attribute | re.search | diff --git a/python/ql/src/experimental/Security/CWE-730/RegexInjection.qlref b/python/ql/src/experimental/Security/CWE-730/RegexInjection.qlref new file mode 100644 index 00000000000..c0c506c4707 --- /dev/null +++ b/python/ql/src/experimental/Security/CWE-730/RegexInjection.qlref @@ -0,0 +1 @@ +experimental/Security/CWE-730/RegexInjection.ql From 81d23c066c4fbad99914aa3410e57e328c5c2375 Mon Sep 17 00:00:00 2001 From: jorgectf Date: Sun, 28 Mar 2021 01:37:55 +0100 Subject: [PATCH 068/168] Move tests and qlref from /src to /test --- .../query-tests}/Security/CWE-730/RegexInjection.qlref | 0 .../experimental/query-tests/Security/CWE-730}/re_bad.py | 0 .../experimental/query-tests/Security/CWE-730}/re_good.py | 0 3 files changed, 0 insertions(+), 0 deletions(-) rename python/ql/{src/experimental => test/experimental/query-tests}/Security/CWE-730/RegexInjection.qlref (100%) rename python/ql/{src/experimental/Security/CWE-730/unit_tests => test/experimental/query-tests/Security/CWE-730}/re_bad.py (100%) rename python/ql/{src/experimental/Security/CWE-730/unit_tests => test/experimental/query-tests/Security/CWE-730}/re_good.py (100%) diff --git a/python/ql/src/experimental/Security/CWE-730/RegexInjection.qlref b/python/ql/test/experimental/query-tests/Security/CWE-730/RegexInjection.qlref similarity index 100% rename from python/ql/src/experimental/Security/CWE-730/RegexInjection.qlref rename to python/ql/test/experimental/query-tests/Security/CWE-730/RegexInjection.qlref diff --git a/python/ql/src/experimental/Security/CWE-730/unit_tests/re_bad.py b/python/ql/test/experimental/query-tests/Security/CWE-730/re_bad.py similarity index 100% rename from python/ql/src/experimental/Security/CWE-730/unit_tests/re_bad.py rename to python/ql/test/experimental/query-tests/Security/CWE-730/re_bad.py diff --git a/python/ql/src/experimental/Security/CWE-730/unit_tests/re_good.py b/python/ql/test/experimental/query-tests/Security/CWE-730/re_good.py similarity index 100% rename from python/ql/src/experimental/Security/CWE-730/unit_tests/re_good.py rename to python/ql/test/experimental/query-tests/Security/CWE-730/re_good.py From d968eea9143fda8d6118d1cf906f0a23fd673065 Mon Sep 17 00:00:00 2001 From: jorgectf Date: Sun, 28 Mar 2021 18:36:59 +0200 Subject: [PATCH 069/168] Move expected to /test --- .../query-tests}/Security/CWE-730/RegexInjection.expected | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename python/ql/{src/experimental => test/experimental/query-tests}/Security/CWE-730/RegexInjection.expected (100%) diff --git a/python/ql/src/experimental/Security/CWE-730/RegexInjection.expected b/python/ql/test/experimental/query-tests/Security/CWE-730/RegexInjection.expected similarity index 100% rename from python/ql/src/experimental/Security/CWE-730/RegexInjection.expected rename to python/ql/test/experimental/query-tests/Security/CWE-730/RegexInjection.expected From 6a20a4dcc32f9dc0016e39446c579de4e5389b0e Mon Sep 17 00:00:00 2001 From: jorgectf Date: Mon, 29 Mar 2021 15:01:58 +0200 Subject: [PATCH 070/168] Add newline to qhelp --- .../ql/src/experimental/Security/CWE-730/RegexInjection.qhelp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/python/ql/src/experimental/Security/CWE-730/RegexInjection.qhelp b/python/ql/src/experimental/Security/CWE-730/RegexInjection.qhelp index 3c1d5907a77..94e66b2b89c 100644 --- a/python/ql/src/experimental/Security/CWE-730/RegexInjection.qhelp +++ b/python/ql/src/experimental/Security/CWE-730/RegexInjection.qhelp @@ -49,4 +49,4 @@ SonarSource: RSPEC-2631 -
    \ No newline at end of file +
    From 3fae3fd93e25a7913d762a00d0869a4c3cfabd13 Mon Sep 17 00:00:00 2001 From: jorgectf Date: Tue, 30 Mar 2021 17:39:27 +0200 Subject: [PATCH 071/168] Take ApiGraphs out of Concepts.qll --- python/ql/src/experimental/semmle/python/Concepts.qll | 1 - 1 file changed, 1 deletion(-) diff --git a/python/ql/src/experimental/semmle/python/Concepts.qll b/python/ql/src/experimental/semmle/python/Concepts.qll index 08131064912..07bbae2a060 100644 --- a/python/ql/src/experimental/semmle/python/Concepts.qll +++ b/python/ql/src/experimental/semmle/python/Concepts.qll @@ -13,7 +13,6 @@ private import semmle.python.dataflow.new.DataFlow private import semmle.python.dataflow.new.RemoteFlowSources private import semmle.python.dataflow.new.TaintTracking private import experimental.semmle.python.Frameworks -private import semmle.python.ApiGraphs /** Provides classes for modeling Regular Expression-related APIs. */ module RegexExecution { From 05ee853c4e380f5580b17620fce1e4aa4ca14ab6 Mon Sep 17 00:00:00 2001 From: jorgectf Date: Tue, 6 Apr 2021 15:52:31 +0200 Subject: [PATCH 072/168] Remove wrong comment --- .../ql/test/experimental/query-tests/Security/CWE-730/re_bad.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/python/ql/test/experimental/query-tests/Security/CWE-730/re_bad.py b/python/ql/test/experimental/query-tests/Security/CWE-730/re_bad.py index 55b306c8857..622eaf199f6 100644 --- a/python/ql/test/experimental/query-tests/Security/CWE-730/re_bad.py +++ b/python/ql/test/experimental/query-tests/Security/CWE-730/re_bad.py @@ -14,8 +14,6 @@ def direct(): re.search(unsafe_pattern, "") -# A RemoteFlowSource is used directly as re.compile's pattern - @app.route("/compile") def compile(): """ From 12ccd7e3b643899b1abff49056390e6adb40a763 Mon Sep 17 00:00:00 2001 From: jorgectf Date: Thu, 8 Apr 2021 22:20:09 +0200 Subject: [PATCH 073/168] Update .expected --- .../Security/CWE-730/RegexInjection.expected | 32 +++++++++---------- 1 file changed, 16 insertions(+), 16 deletions(-) diff --git a/python/ql/test/experimental/query-tests/Security/CWE-730/RegexInjection.expected b/python/ql/test/experimental/query-tests/Security/CWE-730/RegexInjection.expected index 9886df2904d..97ed0d1bdfa 100644 --- a/python/ql/test/experimental/query-tests/Security/CWE-730/RegexInjection.expected +++ b/python/ql/test/experimental/query-tests/Security/CWE-730/RegexInjection.expected @@ -2,26 +2,26 @@ edges | re_bad.py:13:22:13:28 | ControlFlowNode for request | re_bad.py:13:22:13:33 | ControlFlowNode for Attribute | | re_bad.py:13:22:13:33 | ControlFlowNode for Attribute | re_bad.py:13:22:13:44 | ControlFlowNode for Subscript | | re_bad.py:13:22:13:44 | ControlFlowNode for Subscript | re_bad.py:14:15:14:28 | ControlFlowNode for unsafe_pattern | -| re_bad.py:26:22:26:28 | ControlFlowNode for request | re_bad.py:26:22:26:33 | ControlFlowNode for Attribute | -| re_bad.py:26:22:26:33 | ControlFlowNode for Attribute | re_bad.py:26:22:26:44 | ControlFlowNode for Subscript | -| re_bad.py:26:22:26:44 | ControlFlowNode for Subscript | re_bad.py:27:35:27:48 | ControlFlowNode for unsafe_pattern | -| re_bad.py:38:22:38:28 | ControlFlowNode for request | re_bad.py:38:22:38:33 | ControlFlowNode for Attribute | -| re_bad.py:38:22:38:33 | ControlFlowNode for Attribute | re_bad.py:38:22:38:44 | ControlFlowNode for Subscript | -| re_bad.py:38:22:38:44 | ControlFlowNode for Subscript | re_bad.py:39:16:39:29 | ControlFlowNode for unsafe_pattern | +| re_bad.py:24:22:24:28 | ControlFlowNode for request | re_bad.py:24:22:24:33 | ControlFlowNode for Attribute | +| re_bad.py:24:22:24:33 | ControlFlowNode for Attribute | re_bad.py:24:22:24:44 | ControlFlowNode for Subscript | +| re_bad.py:24:22:24:44 | ControlFlowNode for Subscript | re_bad.py:25:35:25:48 | ControlFlowNode for unsafe_pattern | +| re_bad.py:36:22:36:28 | ControlFlowNode for request | re_bad.py:36:22:36:33 | ControlFlowNode for Attribute | +| re_bad.py:36:22:36:33 | ControlFlowNode for Attribute | re_bad.py:36:22:36:44 | ControlFlowNode for Subscript | +| re_bad.py:36:22:36:44 | ControlFlowNode for Subscript | re_bad.py:37:16:37:29 | ControlFlowNode for unsafe_pattern | nodes | re_bad.py:13:22:13:28 | ControlFlowNode for request | semmle.label | ControlFlowNode for request | | re_bad.py:13:22:13:33 | ControlFlowNode for Attribute | semmle.label | ControlFlowNode for Attribute | | re_bad.py:13:22:13:44 | ControlFlowNode for Subscript | semmle.label | ControlFlowNode for Subscript | | re_bad.py:14:15:14:28 | ControlFlowNode for unsafe_pattern | semmle.label | ControlFlowNode for unsafe_pattern | -| re_bad.py:26:22:26:28 | ControlFlowNode for request | semmle.label | ControlFlowNode for request | -| re_bad.py:26:22:26:33 | ControlFlowNode for Attribute | semmle.label | ControlFlowNode for Attribute | -| re_bad.py:26:22:26:44 | ControlFlowNode for Subscript | semmle.label | ControlFlowNode for Subscript | -| re_bad.py:27:35:27:48 | ControlFlowNode for unsafe_pattern | semmle.label | ControlFlowNode for unsafe_pattern | -| re_bad.py:38:22:38:28 | ControlFlowNode for request | semmle.label | ControlFlowNode for request | -| re_bad.py:38:22:38:33 | ControlFlowNode for Attribute | semmle.label | ControlFlowNode for Attribute | -| re_bad.py:38:22:38:44 | ControlFlowNode for Subscript | semmle.label | ControlFlowNode for Subscript | -| re_bad.py:39:16:39:29 | ControlFlowNode for unsafe_pattern | semmle.label | ControlFlowNode for unsafe_pattern | +| re_bad.py:24:22:24:28 | ControlFlowNode for request | semmle.label | ControlFlowNode for request | +| re_bad.py:24:22:24:33 | ControlFlowNode for Attribute | semmle.label | ControlFlowNode for Attribute | +| re_bad.py:24:22:24:44 | ControlFlowNode for Subscript | semmle.label | ControlFlowNode for Subscript | +| re_bad.py:25:35:25:48 | ControlFlowNode for unsafe_pattern | semmle.label | ControlFlowNode for unsafe_pattern | +| re_bad.py:36:22:36:28 | ControlFlowNode for request | semmle.label | ControlFlowNode for request | +| re_bad.py:36:22:36:33 | ControlFlowNode for Attribute | semmle.label | ControlFlowNode for Attribute | +| re_bad.py:36:22:36:44 | ControlFlowNode for Subscript | semmle.label | ControlFlowNode for Subscript | +| re_bad.py:37:16:37:29 | ControlFlowNode for unsafe_pattern | semmle.label | ControlFlowNode for unsafe_pattern | #select | re_bad.py:14:15:14:28 | ControlFlowNode for unsafe_pattern | re_bad.py:13:22:13:28 | ControlFlowNode for request | re_bad.py:14:15:14:28 | ControlFlowNode for unsafe_pattern | $@ regular expression is constructed from a $@ and executed by $@. | re_bad.py:14:15:14:28 | ControlFlowNode for unsafe_pattern | This | re_bad.py:13:22:13:28 | ControlFlowNode for request | user-provided value | re_bad.py:14:5:14:13 | Attribute | re.search | -| re_bad.py:27:35:27:48 | ControlFlowNode for unsafe_pattern | re_bad.py:26:22:26:28 | ControlFlowNode for request | re_bad.py:27:35:27:48 | ControlFlowNode for unsafe_pattern | $@ regular expression is constructed from a $@ and executed by $@. | re_bad.py:27:35:27:48 | ControlFlowNode for unsafe_pattern | This | re_bad.py:26:22:26:28 | ControlFlowNode for request | user-provided value | re_bad.py:28:5:28:27 | Attribute | re.search | -| re_bad.py:39:16:39:29 | ControlFlowNode for unsafe_pattern | re_bad.py:38:22:38:28 | ControlFlowNode for request | re_bad.py:39:16:39:29 | ControlFlowNode for unsafe_pattern | $@ regular expression is constructed from a $@ and executed by $@. | re_bad.py:39:16:39:29 | ControlFlowNode for unsafe_pattern | This | re_bad.py:38:22:38:28 | ControlFlowNode for request | user-provided value | re_bad.py:39:5:39:37 | Attribute | re.search | +| re_bad.py:25:35:25:48 | ControlFlowNode for unsafe_pattern | re_bad.py:24:22:24:28 | ControlFlowNode for request | re_bad.py:25:35:25:48 | ControlFlowNode for unsafe_pattern | $@ regular expression is constructed from a $@ and executed by $@. | re_bad.py:25:35:25:48 | ControlFlowNode for unsafe_pattern | This | re_bad.py:24:22:24:28 | ControlFlowNode for request | user-provided value | re_bad.py:26:5:26:27 | Attribute | re.search | +| re_bad.py:37:16:37:29 | ControlFlowNode for unsafe_pattern | re_bad.py:36:22:36:28 | ControlFlowNode for request | re_bad.py:37:16:37:29 | ControlFlowNode for unsafe_pattern | $@ regular expression is constructed from a $@ and executed by $@. | re_bad.py:37:16:37:29 | ControlFlowNode for unsafe_pattern | This | re_bad.py:36:22:36:28 | ControlFlowNode for request | user-provided value | re_bad.py:37:5:37:37 | Attribute | re.search | From c4322848ec8525dc70ed5c7ee7d6381362621aeb Mon Sep 17 00:00:00 2001 From: jorgectf Date: Thu, 8 Apr 2021 22:42:35 +0200 Subject: [PATCH 074/168] Polish qhelp --- .../Security/CWE-730/RegexInjection.qhelp | 87 +++++++++---------- .../experimental/Security/CWE-730/re_bad.py | 15 ++++ .../experimental/Security/CWE-730/re_good.py | 17 ++++ 3 files changed, 72 insertions(+), 47 deletions(-) create mode 100644 python/ql/src/experimental/Security/CWE-730/re_bad.py create mode 100644 python/ql/src/experimental/Security/CWE-730/re_good.py diff --git a/python/ql/src/experimental/Security/CWE-730/RegexInjection.qhelp b/python/ql/src/experimental/Security/CWE-730/RegexInjection.qhelp index 94e66b2b89c..f19f0744469 100644 --- a/python/ql/src/experimental/Security/CWE-730/RegexInjection.qhelp +++ b/python/ql/src/experimental/Security/CWE-730/RegexInjection.qhelp @@ -1,52 +1,45 @@ - + - -

    - Constructing a regular expression with unsanitized user input is dangerous as a malicious user may - be able to modify the meaning of the expression. In particular, such a user may be able to provide - a regular expression fragment that takes exponential time in the worst case, and use that to - perform a Denial of Service attack. -

    -
    + +

    +Constructing a regular expression with unsanitized user input is dangerous as a malicious user may +be able to modify the meaning of the expression. In particular, such a user may be able to provide +a regular expression fragment that takes exponential time in the worst case, and use that to +perform a Denial of Service attack. +

    +
    - -

    - Before embedding user input into a regular expression, use a sanitization function such as - re.escape to escape meta-characters that have a special meaning regarding - regular expressions' syntax. -

    -
    + +

    +Before embedding user input into a regular expression, use a sanitization function such as +re.escape to escape meta-characters that have a special meaning regarding +regular expressions' syntax. +

    +
    - -

    - The following examples are based on a simple Flask web server environment. -

    -

    - The following example shows a HTTP request parameter that is used to construct a regular expression - without sanitizing it first: -

    - -

    - Instead, the request parameter should be sanitized first, for example using the function - re.escape. This ensures that the user cannot insert characters which have a - special meaning in regular expressions. -

    - -
    + +

    +The following examples are based on a simple Flask web server environment. +

    +

    +The following example shows a HTTP request parameter that is used to construct a regular expression +without sanitizing it first: +

    + +

    +Instead, the request parameter should be sanitized first, for example using the function +re.escape. This ensures that the user cannot insert characters which have a +special meaning in regular expressions. +

    + +
    - -
  • - OWASP: - Regular expression Denial of Service - ReDoS. -
  • -
  • - Wikipedia: ReDoS. -
  • -
  • - Python docs: re. -
  • -
  • - SonarSource: RSPEC-2631 -
  • -
    + +
  • OWASP: Regular expression Denial of Service - ReDoS.
  • +
  • Wikipedia: ReDoS.
  • +
  • Python docs: re.
  • +
  • SonarSource: RSPEC-2631.
  • +
    diff --git a/python/ql/src/experimental/Security/CWE-730/re_bad.py b/python/ql/src/experimental/Security/CWE-730/re_bad.py new file mode 100644 index 00000000000..3befaba9a01 --- /dev/null +++ b/python/ql/src/experimental/Security/CWE-730/re_bad.py @@ -0,0 +1,15 @@ +from flask import request, Flask +import re + + +@app.route("/direct") +def direct(): + unsafe_pattern = request.args["pattern"] + re.search(unsafe_pattern, "") + + +@app.route("/compile") +def compile(): + unsafe_pattern = request.args["pattern"] + compiled_pattern = re.compile(unsafe_pattern) + compiled_pattern.search("") diff --git a/python/ql/src/experimental/Security/CWE-730/re_good.py b/python/ql/src/experimental/Security/CWE-730/re_good.py new file mode 100644 index 00000000000..cdc9a7ac158 --- /dev/null +++ b/python/ql/src/experimental/Security/CWE-730/re_good.py @@ -0,0 +1,17 @@ +from flask import request, Flask +import re + + +@app.route("/direct") +def direct(): + unsafe_pattern = request.args['pattern'] + safe_pattern = re.escape(unsafe_pattern) + re.search(safe_pattern, "") + + +@app.route("/compile") +def compile(): + unsafe_pattern = request.args['pattern'] + safe_pattern = re.escape(unsafe_pattern) + compiled_pattern = re.compile(safe_pattern) + compiled_pattern.search("") From c0c71c509c4f9bbf31b665763ed89deeebae90c3 Mon Sep 17 00:00:00 2001 From: Jorge <46056498+jorgectf@users.noreply.github.com> Date: Tue, 27 Apr 2021 19:21:43 +0200 Subject: [PATCH 075/168] Apply suggestions from code review Update `RegexExecution` docs and use `flowsTo()` instead of `getALocalSource()`. Co-authored-by: yoff --- python/ql/src/experimental/semmle/python/Concepts.qll | 2 +- python/ql/src/experimental/semmle/python/frameworks/Stdlib.qll | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/python/ql/src/experimental/semmle/python/Concepts.qll b/python/ql/src/experimental/semmle/python/Concepts.qll index 07bbae2a060..e11e21da914 100644 --- a/python/ql/src/experimental/semmle/python/Concepts.qll +++ b/python/ql/src/experimental/semmle/python/Concepts.qll @@ -17,7 +17,7 @@ private import experimental.semmle.python.Frameworks /** Provides classes for modeling Regular Expression-related APIs. */ module RegexExecution { /** - * A data-flow node that collects methods immediately executing an expression. + * A data-flow node that executes a regular expression. * * Extend this class to model new APIs. If you want to refine existing API models, * extend `RegexExecution` instead. diff --git a/python/ql/src/experimental/semmle/python/frameworks/Stdlib.qll b/python/ql/src/experimental/semmle/python/frameworks/Stdlib.qll index 849dd47467d..1e3478c55f5 100644 --- a/python/ql/src/experimental/semmle/python/frameworks/Stdlib.qll +++ b/python/ql/src/experimental/semmle/python/frameworks/Stdlib.qll @@ -60,7 +60,7 @@ private module Re { exists(DataFlow::CallCfgNode patternCall, DataFlow::AttrRead reMethod | this.getFunction() = reMethod and patternCall = API::moduleImport("re").getMember("compile").getACall() and - patternCall = reMethod.getObject().getALocalSource() and + patternCall.flowsTo(reMethod.getObject()) and reMethod.getAttributeName() instanceof RegexExecutionMethods and regexNode = patternCall.getArg(0) ) From 20b532ec5e3b57790cf20ea7ebc587b9fcadc10d Mon Sep 17 00:00:00 2001 From: jorgectf Date: Tue, 27 Apr 2021 19:24:25 +0200 Subject: [PATCH 076/168] Update to-cast sink's naming Signed-off-by: jorgectf --- .../src/experimental/Security/CWE-730/RegexInjection.ql | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/python/ql/src/experimental/Security/CWE-730/RegexInjection.ql b/python/ql/src/experimental/Security/CWE-730/RegexInjection.ql index 77355fdf30d..7725f636eb0 100644 --- a/python/ql/src/experimental/Security/CWE-730/RegexInjection.ql +++ b/python/ql/src/experimental/Security/CWE-730/RegexInjection.ql @@ -18,12 +18,12 @@ import DataFlow::PathGraph from RegexInjectionFlowConfig config, DataFlow::PathNode source, DataFlow::PathNode sink, - RegexInjectionSink castedSink, Attribute methodAttribute + RegexInjectionSink regexInjectionSink, Attribute methodAttribute where config.hasFlowPath(source, sink) and - castedSink = sink.getNode() and - methodAttribute = castedSink.getRegexMethod() + regexInjectionSink = sink.getNode() and + methodAttribute = regexInjectionSink.getRegexMethod() select sink.getNode(), source, sink, "$@ regular expression is constructed from a $@ and executed by $@.", sink.getNode(), "This", source.getNode(), "user-provided value", methodAttribute, - castedSink.getRegexModule() + "." + methodAttribute.getName() + regexInjectionSink.getRegexModule() + "." + methodAttribute.getName() From 8a800986a290a89492df3d6161d4547d80015bf4 Mon Sep 17 00:00:00 2001 From: jorgectf Date: Tue, 27 Apr 2021 19:26:04 +0200 Subject: [PATCH 077/168] Remove unused class variables Signed-off-by: jorgectf --- python/ql/src/experimental/semmle/python/frameworks/Stdlib.qll | 2 -- 1 file changed, 2 deletions(-) diff --git a/python/ql/src/experimental/semmle/python/frameworks/Stdlib.qll b/python/ql/src/experimental/semmle/python/frameworks/Stdlib.qll index 1e3478c55f5..c7a8842cad7 100644 --- a/python/ql/src/experimental/semmle/python/frameworks/Stdlib.qll +++ b/python/ql/src/experimental/semmle/python/frameworks/Stdlib.qll @@ -54,7 +54,6 @@ private module Re { */ private class CompiledRegex extends DataFlow::CallCfgNode, RegexExecution::Range { DataFlow::Node regexNode; - DataFlow::CallCfgNode regexMethod; CompiledRegex() { exists(DataFlow::CallCfgNode patternCall, DataFlow::AttrRead reMethod | @@ -78,7 +77,6 @@ private module Re { */ class ReEscape extends DataFlow::CallCfgNode, RegexEscape::Range { DataFlow::Node regexNode; - DataFlow::CallCfgNode escapeMethod; ReEscape() { this = API::moduleImport("re").getMember("escape").getACall() and From 21e01b809fe169f281dbc539e1cc6460dd22edf3 Mon Sep 17 00:00:00 2001 From: jorgectf Date: Tue, 27 Apr 2021 19:52:26 +0200 Subject: [PATCH 078/168] Add code example in CompiledRegex Signed-off-by: jorgectf --- .../experimental/semmle/python/frameworks/Stdlib.qll | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/python/ql/src/experimental/semmle/python/frameworks/Stdlib.qll b/python/ql/src/experimental/semmle/python/frameworks/Stdlib.qll index c7a8842cad7..9bda2cb3158 100644 --- a/python/ql/src/experimental/semmle/python/frameworks/Stdlib.qll +++ b/python/ql/src/experimental/semmle/python/frameworks/Stdlib.qll @@ -48,6 +48,17 @@ private module Re { /** * A class to find `re` methods immediately executing a compiled expression by `re.compile`. * + * Given the following example: + * + * ```py + * pattern = re.compile(input) + * input.match(s) + * ``` + * + * `patternCall` refers to `re.compile(input)`, + * `regexNode` refers to `input` and + * `this` will refer to `input.match(s)` + * * See `RegexExecutionMethods` * * See https://docs.python.org/3/library/re.html#regular-expression-objects From e8347c2c20e6b8c4c150e443372159bcb3034abf Mon Sep 17 00:00:00 2001 From: Tom Hvitved Date: Tue, 27 Apr 2021 10:43:45 +0200 Subject: [PATCH 079/168] C++: Update data-flow caching --- .../cpp/dataflow/internal/DataFlowUtil.qll | 1 - .../dataflow/internal/TaintTrackingUtil.qll | 1 + .../cpp/ir/dataflow/DefaultTaintTracking.qll | 233 ++++++++++-------- .../ir/dataflow/internal/DataFlowDispatch.qll | 4 +- .../cpp/ir/dataflow/internal/DataFlowUtil.qll | 21 +- 5 files changed, 143 insertions(+), 117 deletions(-) diff --git a/cpp/ql/src/semmle/code/cpp/dataflow/internal/DataFlowUtil.qll b/cpp/ql/src/semmle/code/cpp/dataflow/internal/DataFlowUtil.qll index 690f24fc59a..75e8c8922d0 100644 --- a/cpp/ql/src/semmle/code/cpp/dataflow/internal/DataFlowUtil.qll +++ b/cpp/ql/src/semmle/code/cpp/dataflow/internal/DataFlowUtil.qll @@ -526,7 +526,6 @@ predicate localFlowStep(Node nodeFrom, Node nodeTo) { * This is the local flow predicate that's used as a building block in global * data flow. It may have less flow than the `localFlowStep` predicate. */ -cached predicate simpleLocalFlowStep(Node nodeFrom, Node nodeTo) { // Expr -> Expr exprToExprStep_nocfg(nodeFrom.asExpr(), nodeTo.asExpr()) diff --git a/cpp/ql/src/semmle/code/cpp/dataflow/internal/TaintTrackingUtil.qll b/cpp/ql/src/semmle/code/cpp/dataflow/internal/TaintTrackingUtil.qll index 591f461c8eb..6216045db32 100644 --- a/cpp/ql/src/semmle/code/cpp/dataflow/internal/TaintTrackingUtil.qll +++ b/cpp/ql/src/semmle/code/cpp/dataflow/internal/TaintTrackingUtil.qll @@ -45,6 +45,7 @@ predicate defaultTaintSanitizer(DataFlow::Node node) { none() } * local data flow steps. That is, `nodeFrom` and `nodeTo` are likely to represent * different objects. */ +cached predicate localAdditionalTaintStep(DataFlow::Node nodeFrom, DataFlow::Node nodeTo) { // Taint can flow through expressions that alter the value but preserve // more than one bit of it _or_ expressions that follow data through diff --git a/cpp/ql/src/semmle/code/cpp/ir/dataflow/DefaultTaintTracking.qll b/cpp/ql/src/semmle/code/cpp/ir/dataflow/DefaultTaintTracking.qll index 3092031cbc7..49d11a7e3cc 100644 --- a/cpp/ql/src/semmle/code/cpp/ir/dataflow/DefaultTaintTracking.qll +++ b/cpp/ql/src/semmle/code/cpp/ir/dataflow/DefaultTaintTracking.qll @@ -165,105 +165,132 @@ private predicate nodeIsBarrierEqualityCandidate( any(IRGuardCondition guard).ensuresEq(access, _, _, node.asInstruction().getBlock(), true) } -private predicate nodeIsBarrier(DataFlow::Node node) { - exists(Variable checkedVar | - readsVariable(node.asInstruction(), checkedVar) and - hasUpperBoundsCheck(checkedVar) - ) - or - exists(Variable checkedVar, Operand access | - /* - * This node is guarded by a condition that forces the accessed variable - * to equal something else. For example: - * ``` - * x = taintsource() - * if (x == 10) { - * taintsink(x); // not considered tainted - * } - * ``` - */ - - nodeIsBarrierEqualityCandidate(node, access, checkedVar) and - readsVariable(access.getDef(), checkedVar) - ) -} - -private predicate nodeIsBarrierIn(DataFlow::Node node) { - // don't use dataflow into taint sources, as this leads to duplicate results. - exists(Expr source | isUserInput(source, _) | - node = DataFlow::exprNode(source) +cached +private module Cached { + cached + predicate nodeIsBarrier(DataFlow::Node node) { + exists(Variable checkedVar | + readsVariable(node.asInstruction(), checkedVar) and + hasUpperBoundsCheck(checkedVar) + ) or - // This case goes together with the similar (but not identical) rule in - // `getNodeForSource`. - node = DataFlow::definitionByReferenceNodeFromArgument(source) - ) - or - // don't use dataflow into binary instructions if both operands are unpredictable - exists(BinaryInstruction iTo | - iTo = node.asInstruction() and - not predictableInstruction(iTo.getLeft()) and - not predictableInstruction(iTo.getRight()) and - // propagate taint from either the pointer or the offset, regardless of predictability - not iTo instanceof PointerArithmeticInstruction - ) - or - // don't use dataflow through calls to pure functions if two or more operands - // are unpredictable - exists(Instruction iFrom1, Instruction iFrom2, CallInstruction iTo | - iTo = node.asInstruction() and - isPureFunction(iTo.getStaticCallTarget().getName()) and - iFrom1 = iTo.getAnArgument() and - iFrom2 = iTo.getAnArgument() and - not predictableInstruction(iFrom1) and - not predictableInstruction(iFrom2) and - iFrom1 != iFrom2 - ) + exists(Variable checkedVar, Operand access | + /* + * This node is guarded by a condition that forces the accessed variable + * to equal something else. For example: + * ``` + * x = taintsource() + * if (x == 10) { + * taintsink(x); // not considered tainted + * } + * ``` + */ + + nodeIsBarrierEqualityCandidate(node, access, checkedVar) and + readsVariable(access.getDef(), checkedVar) + ) + } + + cached + predicate nodeIsBarrierIn(DataFlow::Node node) { + // don't use dataflow into taint sources, as this leads to duplicate results. + exists(Expr source | isUserInput(source, _) | + node = DataFlow::exprNode(source) + or + // This case goes together with the similar (but not identical) rule in + // `getNodeForSource`. + node = DataFlow::definitionByReferenceNodeFromArgument(source) + ) + or + // don't use dataflow into binary instructions if both operands are unpredictable + exists(BinaryInstruction iTo | + iTo = node.asInstruction() and + not predictableInstruction(iTo.getLeft()) and + not predictableInstruction(iTo.getRight()) and + // propagate taint from either the pointer or the offset, regardless of predictability + not iTo instanceof PointerArithmeticInstruction + ) + or + // don't use dataflow through calls to pure functions if two or more operands + // are unpredictable + exists(Instruction iFrom1, Instruction iFrom2, CallInstruction iTo | + iTo = node.asInstruction() and + isPureFunction(iTo.getStaticCallTarget().getName()) and + iFrom1 = iTo.getAnArgument() and + iFrom2 = iTo.getAnArgument() and + not predictableInstruction(iFrom1) and + not predictableInstruction(iFrom2) and + iFrom1 != iFrom2 + ) + } + + cached + Element adjustedSink(DataFlow::Node sink) { + // TODO: is it more appropriate to use asConvertedExpr here and avoid + // `getConversion*`? Or will that cause us to miss some cases where there's + // flow to a conversion (like a `ReferenceDereferenceExpr`) and we want to + // pretend there was flow to the converted `Expr` for the sake of + // compatibility. + sink.asExpr().getConversion*() = result + or + // For compatibility, send flow from arguments to parameters, even for + // functions with no body. + exists(FunctionCall call, int i | + sink.asExpr() = call.getArgument(i) and + result = resolveCall(call).getParameter(i) + ) + or + // For compatibility, send flow into a `Variable` if there is flow to any + // Load or Store of that variable. + exists(CopyInstruction copy | + copy.getSourceValue() = sink.asInstruction() and + ( + readsVariable(copy, result) or + writesVariable(copy, result) + ) and + not hasUpperBoundsCheck(result) + ) + or + // For compatibility, send flow into a `NotExpr` even if it's part of a + // short-circuiting condition and thus might get skipped. + result.(NotExpr).getOperand() = sink.asExpr() + or + // Taint postfix and prefix crement operations when their operand is tainted. + result.(CrementOperation).getAnOperand() = sink.asExpr() + or + // Taint `e1 += e2`, `e &= e2` and friends when `e1` or `e2` is tainted. + result.(AssignOperation).getAnOperand() = sink.asExpr() + or + result = + sink.asOperand() + .(SideEffectOperand) + .getUse() + .(ReadSideEffectInstruction) + .getArgumentDef() + .getUnconvertedResultExpression() + } + + /** + * Step to return value of a modeled function when an input taints the + * dereference of the return value. + */ + cached + predicate additionalTaintStep(DataFlow::Node n1, DataFlow::Node n2) { + exists(CallInstruction call, Function func, FunctionInput modelIn, FunctionOutput modelOut | + n1.asOperand() = callInput(call, modelIn) and + ( + func.(TaintFunction).hasTaintFlow(modelIn, modelOut) + or + func.(DataFlowFunction).hasDataFlow(modelIn, modelOut) + ) and + call.getStaticCallTarget() = func and + modelOut.isReturnValueDeref() and + call = n2.asInstruction() + ) + } } -private Element adjustedSink(DataFlow::Node sink) { - // TODO: is it more appropriate to use asConvertedExpr here and avoid - // `getConversion*`? Or will that cause us to miss some cases where there's - // flow to a conversion (like a `ReferenceDereferenceExpr`) and we want to - // pretend there was flow to the converted `Expr` for the sake of - // compatibility. - sink.asExpr().getConversion*() = result - or - // For compatibility, send flow from arguments to parameters, even for - // functions with no body. - exists(FunctionCall call, int i | - sink.asExpr() = call.getArgument(i) and - result = resolveCall(call).getParameter(i) - ) - or - // For compatibility, send flow into a `Variable` if there is flow to any - // Load or Store of that variable. - exists(CopyInstruction copy | - copy.getSourceValue() = sink.asInstruction() and - ( - readsVariable(copy, result) or - writesVariable(copy, result) - ) and - not hasUpperBoundsCheck(result) - ) - or - // For compatibility, send flow into a `NotExpr` even if it's part of a - // short-circuiting condition and thus might get skipped. - result.(NotExpr).getOperand() = sink.asExpr() - or - // Taint postfix and prefix crement operations when their operand is tainted. - result.(CrementOperation).getAnOperand() = sink.asExpr() - or - // Taint `e1 += e2`, `e &= e2` and friends when `e1` or `e2` is tainted. - result.(AssignOperation).getAnOperand() = sink.asExpr() - or - result = - sink.asOperand() - .(SideEffectOperand) - .getUse() - .(ReadSideEffectInstruction) - .getArgumentDef() - .getUnconvertedResultExpression() -} +private import Cached /** * Holds if `tainted` may contain taint from `source`. @@ -402,19 +429,7 @@ module TaintedWithPath { readsVariable(n2.asInstruction(), n1.asVariable().(GlobalOrNamespaceVariable)) ) or - // Step to return value of a modeled function when an input taints the - // dereference of the return value - exists(CallInstruction call, Function func, FunctionInput modelIn, FunctionOutput modelOut | - n1.asOperand() = callInput(call, modelIn) and - ( - func.(TaintFunction).hasTaintFlow(modelIn, modelOut) - or - func.(DataFlowFunction).hasDataFlow(modelIn, modelOut) - ) and - call.getStaticCallTarget() = func and - modelOut.isReturnValueDeref() and - call = n2.asInstruction() - ) + additionalTaintStep(n1, n2) } override predicate isSanitizer(DataFlow::Node node) { diff --git a/cpp/ql/src/semmle/code/cpp/ir/dataflow/internal/DataFlowDispatch.qll b/cpp/ql/src/semmle/code/cpp/ir/dataflow/internal/DataFlowDispatch.qll index e927634fec2..99d8555f8ca 100644 --- a/cpp/ql/src/semmle/code/cpp/ir/dataflow/internal/DataFlowDispatch.qll +++ b/cpp/ql/src/semmle/code/cpp/ir/dataflow/internal/DataFlowDispatch.qll @@ -2,11 +2,14 @@ private import cpp private import semmle.code.cpp.ir.IR private import semmle.code.cpp.ir.dataflow.DataFlow private import semmle.code.cpp.ir.dataflow.internal.DataFlowPrivate +private import DataFlowImplCommon as DataFlowImplCommon /** * Gets a function that might be called by `call`. */ +cached Function viableCallable(CallInstruction call) { + DataFlowImplCommon::forceCachingInSameStage() and result = call.getStaticCallTarget() or // If the target of the call does not have a body in the snapshot, it might @@ -43,7 +46,6 @@ private module VirtualDispatch { abstract DataFlow::Node getDispatchValue(); /** Gets a candidate target for this call. */ - cached abstract Function resolve(); /** diff --git a/cpp/ql/src/semmle/code/cpp/ir/dataflow/internal/DataFlowUtil.qll b/cpp/ql/src/semmle/code/cpp/ir/dataflow/internal/DataFlowUtil.qll index f5fb7309cff..8ed61da4c92 100644 --- a/cpp/ql/src/semmle/code/cpp/ir/dataflow/internal/DataFlowUtil.qll +++ b/cpp/ql/src/semmle/code/cpp/ir/dataflow/internal/DataFlowUtil.qll @@ -12,10 +12,20 @@ private import semmle.code.cpp.controlflow.IRGuards private import semmle.code.cpp.models.interfaces.DataFlow cached -private newtype TIRDataFlowNode = - TInstructionNode(Instruction i) or - TOperandNode(Operand op) or - TVariableNode(Variable var) +private module Cached { + cached + newtype TIRDataFlowNode = + TInstructionNode(Instruction i) or + TOperandNode(Operand op) or + TVariableNode(Variable var) + + cached + predicate localFlowStepCached(Node nodeFrom, Node nodeTo) { + simpleLocalFlowStep(nodeFrom, nodeTo) + } +} + +private import Cached /** * A node in a data flow graph. @@ -590,7 +600,7 @@ Node uninitializedNode(LocalVariable v) { none() } * Holds if data flows from `nodeFrom` to `nodeTo` in exactly one local * (intra-procedural) step. */ -predicate localFlowStep(Node nodeFrom, Node nodeTo) { simpleLocalFlowStep(nodeFrom, nodeTo) } +predicate localFlowStep = localFlowStepCached/2; /** * INTERNAL: do not use. @@ -598,7 +608,6 @@ predicate localFlowStep(Node nodeFrom, Node nodeTo) { simpleLocalFlowStep(nodeFr * This is the local flow predicate that's used as a building block in global * data flow. It may have less flow than the `localFlowStep` predicate. */ -cached predicate simpleLocalFlowStep(Node nodeFrom, Node nodeTo) { // Operand -> Instruction flow simpleInstructionLocalFlowStep(nodeFrom.asOperand(), nodeTo.asInstruction()) From c35a2b959af56993a7cc659d09323a3328b62959 Mon Sep 17 00:00:00 2001 From: Tom Hvitved Date: Tue, 27 Apr 2021 11:37:17 +0200 Subject: [PATCH 080/168] Python: Update data-flow caching --- .../dataflow/new/internal/DataFlowPrivate.qll | 2 - .../new/internal/TaintTrackingPrivate.qll | 62 ++++++++++--------- 2 files changed, 34 insertions(+), 30 deletions(-) diff --git a/python/ql/src/semmle/python/dataflow/new/internal/DataFlowPrivate.qll b/python/ql/src/semmle/python/dataflow/new/internal/DataFlowPrivate.qll index 9b0a8267270..a6640f41dc0 100644 --- a/python/ql/src/semmle/python/dataflow/new/internal/DataFlowPrivate.qll +++ b/python/ql/src/semmle/python/dataflow/new/internal/DataFlowPrivate.qll @@ -228,7 +228,6 @@ module EssaFlow { * data flow. It is a strict subset of the `localFlowStep` predicate, as it * excludes SSA flow through instance fields. */ -cached predicate simpleLocalFlowStep(Node nodeFrom, Node nodeTo) { // If there is ESSA-flow out of a node `node`, we want flow // both out of `node` and any post-update node of `node`. @@ -1559,7 +1558,6 @@ predicate kwUnpackReadStep(CfgNode nodeFrom, DictionaryElementContent c, Node no * any value stored inside `f` is cleared at the pre-update node associated with `x` * in `x.f = newValue`. */ -cached predicate clearsContent(Node n, Content c) { exists(CallNode call, CallableValue callable, string name | call_unpacks(call, _, callable, name, _) and diff --git a/python/ql/src/semmle/python/dataflow/new/internal/TaintTrackingPrivate.qll b/python/ql/src/semmle/python/dataflow/new/internal/TaintTrackingPrivate.qll index f12d97d2fa5..a6e169243db 100644 --- a/python/ql/src/semmle/python/dataflow/new/internal/TaintTrackingPrivate.qll +++ b/python/ql/src/semmle/python/dataflow/new/internal/TaintTrackingPrivate.qll @@ -9,36 +9,42 @@ private import semmle.python.dataflow.new.internal.TaintTrackingPublic */ predicate defaultTaintSanitizer(DataFlow::Node node) { none() } -/** - * Holds if the additional step from `nodeFrom` to `nodeTo` should be included in all - * global taint flow configurations. - */ -predicate defaultAdditionalTaintStep(DataFlow::Node nodeFrom, DataFlow::Node nodeTo) { - localAdditionalTaintStep(nodeFrom, nodeTo) - or - any(AdditionalTaintStep a).step(nodeFrom, nodeTo) +private module Cached { + /** + * Holds if the additional step from `nodeFrom` to `nodeTo` should be included in all + * global taint flow configurations. + */ + cached + predicate defaultAdditionalTaintStep(DataFlow::Node nodeFrom, DataFlow::Node nodeTo) { + localAdditionalTaintStep(nodeFrom, nodeTo) + or + any(AdditionalTaintStep a).step(nodeFrom, nodeTo) + } + + /** + * Holds if taint can flow in one local step from `nodeFrom` to `nodeTo` excluding + * local data flow steps. That is, `nodeFrom` and `nodeTo` are likely to represent + * different objects. + */ + cached + predicate localAdditionalTaintStep(DataFlow::Node nodeFrom, DataFlow::Node nodeTo) { + concatStep(nodeFrom, nodeTo) + or + subscriptStep(nodeFrom, nodeTo) + or + stringManipulation(nodeFrom, nodeTo) + or + containerStep(nodeFrom, nodeTo) + or + copyStep(nodeFrom, nodeTo) + or + forStep(nodeFrom, nodeTo) + or + unpackingAssignmentStep(nodeFrom, nodeTo) + } } -/** - * Holds if taint can flow in one local step from `nodeFrom` to `nodeTo` excluding - * local data flow steps. That is, `nodeFrom` and `nodeTo` are likely to represent - * different objects. - */ -predicate localAdditionalTaintStep(DataFlow::Node nodeFrom, DataFlow::Node nodeTo) { - concatStep(nodeFrom, nodeTo) - or - subscriptStep(nodeFrom, nodeTo) - or - stringManipulation(nodeFrom, nodeTo) - or - containerStep(nodeFrom, nodeTo) - or - copyStep(nodeFrom, nodeTo) - or - forStep(nodeFrom, nodeTo) - or - unpackingAssignmentStep(nodeFrom, nodeTo) -} +import Cached /** * Holds if taint can flow from `nodeFrom` to `nodeTo` with a step related to concatenation. From 213d011a8cfcd3179c7db3922fbe259ddc48df0d Mon Sep 17 00:00:00 2001 From: jorgectf Date: Thu, 29 Apr 2021 11:10:03 +0200 Subject: [PATCH 081/168] Edit code example in CompiledRegex Signed-off-by: jorgectf --- .../src/experimental/semmle/python/frameworks/Stdlib.qll | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/python/ql/src/experimental/semmle/python/frameworks/Stdlib.qll b/python/ql/src/experimental/semmle/python/frameworks/Stdlib.qll index 9bda2cb3158..05ea4f62630 100644 --- a/python/ql/src/experimental/semmle/python/frameworks/Stdlib.qll +++ b/python/ql/src/experimental/semmle/python/frameworks/Stdlib.qll @@ -52,12 +52,13 @@ private module Re { * * ```py * pattern = re.compile(input) - * input.match(s) + * pattern.match(s) * ``` * - * `patternCall` refers to `re.compile(input)`, - * `regexNode` refers to `input` and - * `this` will refer to `input.match(s)` + * This class will identify that `re.compile` compiles `input` and afterwards + * executes `re`'s `match`. As a result, `this` will refer to `pattern.match(s)` + * and `this.getRegexNode()` will return the node for `input` (`re.compile`'s first argument) + * * * See `RegexExecutionMethods` * From bd4b1893733ccdff3bc4ef564d03a15400e9b00b Mon Sep 17 00:00:00 2001 From: Jorge <46056498+jorgectf@users.noreply.github.com> Date: Thu, 29 Apr 2021 16:26:28 +0200 Subject: [PATCH 082/168] Polish documentation consistency Co-authored-by: yoff --- python/ql/src/experimental/semmle/python/Concepts.qll | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/python/ql/src/experimental/semmle/python/Concepts.qll b/python/ql/src/experimental/semmle/python/Concepts.qll index e11e21da914..0661ebc5890 100644 --- a/python/ql/src/experimental/semmle/python/Concepts.qll +++ b/python/ql/src/experimental/semmle/python/Concepts.qll @@ -36,7 +36,7 @@ module RegexExecution { } /** - * A data-flow node that collect methods immediately executing an expression. + * A data-flow node that executes a regular expression. * * Extend this class to refine existing API models. If you want to model new APIs, * extend `RegexExecution::Range` instead. @@ -54,7 +54,7 @@ class RegexExecution extends DataFlow::Node { /** Provides classes for modeling Regular Expression escape-related APIs. */ module RegexEscape { /** - * A data-flow node that collects functions escaping regular expressions. + * A data-flow node that escapes a regular expression. * * Extend this class to model new APIs. If you want to refine existing API models, * extend `RegexEscape` instead. @@ -68,7 +68,7 @@ module RegexEscape { } /** - * A data-flow node that collects functions escaping regular expressions. + * A data-flow node that escapes a regular expression. * * Extend this class to refine existing API models. If you want to model new APIs, * extend `RegexEscape::Range` instead. From 8c3980d80b88e56db5c01265a9b2ca2c86bff197 Mon Sep 17 00:00:00 2001 From: ihsinme Date: Sun, 2 May 2021 22:54:43 +0300 Subject: [PATCH 083/168] Update cpp/ql/src/experimental/Security/CWE/CWE-1126/DeclarationOfVariableWithUnnecessarilyWideScope.c Co-authored-by: Geoffrey White <40627776+geoffw0@users.noreply.github.com> --- .../CWE-1126/DeclarationOfVariableWithUnnecessarilyWideScope.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cpp/ql/src/experimental/Security/CWE/CWE-1126/DeclarationOfVariableWithUnnecessarilyWideScope.c b/cpp/ql/src/experimental/Security/CWE/CWE-1126/DeclarationOfVariableWithUnnecessarilyWideScope.c index b09971b5328..53a50841977 100644 --- a/cpp/ql/src/experimental/Security/CWE/CWE-1126/DeclarationOfVariableWithUnnecessarilyWideScope.c +++ b/cpp/ql/src/experimental/Security/CWE/CWE-1126/DeclarationOfVariableWithUnnecessarilyWideScope.c @@ -3,7 +3,7 @@ while(intIndex > 2) ... intIndex--; ... -} // GOOD: coreten cycle +} // GOOD: correct cycle ... while(intIndex > 2) { From 0935c5a0f2a2fa2404eac279823ca2faecf6ab30 Mon Sep 17 00:00:00 2001 From: ihsinme Date: Sun, 2 May 2021 22:58:30 +0300 Subject: [PATCH 084/168] Update DeclarationOfVariableWithUnnecessarilyWideScope.ql --- .../CWE-1126/DeclarationOfVariableWithUnnecessarilyWideScope.ql | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cpp/ql/src/experimental/Security/CWE/CWE-1126/DeclarationOfVariableWithUnnecessarilyWideScope.ql b/cpp/ql/src/experimental/Security/CWE/CWE-1126/DeclarationOfVariableWithUnnecessarilyWideScope.ql index a253a5e0599..9acc1d35d81 100644 --- a/cpp/ql/src/experimental/Security/CWE/CWE-1126/DeclarationOfVariableWithUnnecessarilyWideScope.ql +++ b/cpp/ql/src/experimental/Security/CWE/CWE-1126/DeclarationOfVariableWithUnnecessarilyWideScope.ql @@ -26,7 +26,7 @@ class DangerousWhileLoop extends WhileStmt { exp = this.getCondition().getAChild*() and not exp instanceof PointerFieldAccess and not exp instanceof ValueFieldAccess and - exp.toString() = dl.getName() and + exp.(VariableAccess).getTarget().getName() = dl.getName() and not exp.getParent*() instanceof CrementOperation and not exp.getParent*() instanceof Assignment and not exp.getParent*() instanceof FunctionCall From 21f43252e6d48cc3638690225e762fc092c75f99 Mon Sep 17 00:00:00 2001 From: ihsinme Date: Sun, 2 May 2021 22:59:04 +0300 Subject: [PATCH 085/168] Update DeclarationOfVariableWithUnnecessarilyWideScope.expected --- .../DeclarationOfVariableWithUnnecessarilyWideScope.expected | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cpp/ql/test/experimental/query-tests/Security/CWE/CWE-1126/semmle/tests/DeclarationOfVariableWithUnnecessarilyWideScope.expected b/cpp/ql/test/experimental/query-tests/Security/CWE/CWE-1126/semmle/tests/DeclarationOfVariableWithUnnecessarilyWideScope.expected index 7b540a33384..4b29dad8779 100644 --- a/cpp/ql/test/experimental/query-tests/Security/CWE/CWE-1126/semmle/tests/DeclarationOfVariableWithUnnecessarilyWideScope.expected +++ b/cpp/ql/test/experimental/query-tests/Security/CWE/CWE-1126/semmle/tests/DeclarationOfVariableWithUnnecessarilyWideScope.expected @@ -1 +1 @@ -| test.c:12:9:12:16 | intIndex | A variable with this name is used in the loop condition. | +| test.c:13:9:13:16 | intIndex | A variable with this name is used in the loop condition. | From bb97507ebc195ecdb43126c4fcab6741b274791e Mon Sep 17 00:00:00 2001 From: ihsinme Date: Sun, 2 May 2021 22:59:56 +0300 Subject: [PATCH 086/168] Update test.c --- .../Security/CWE/CWE-1126/semmle/tests/test.c | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/cpp/ql/test/experimental/query-tests/Security/CWE/CWE-1126/semmle/tests/test.c b/cpp/ql/test/experimental/query-tests/Security/CWE/CWE-1126/semmle/tests/test.c index 090bed34d45..325c278f697 100644 --- a/cpp/ql/test/experimental/query-tests/Security/CWE/CWE-1126/semmle/tests/test.c +++ b/cpp/ql/test/experimental/query-tests/Security/CWE/CWE-1126/semmle/tests/test.c @@ -6,10 +6,27 @@ void workFunction_0(char *s) { buf[intIndex] = 1; intIndex--; } + intIndex = 10; while(intIndex > 2) { buf[intIndex] = 1; int intIndex; // BAD intIndex--; } + intIndex = 10; + while(intIndex > 2) // GOOD + { + buf[intIndex] = 1; + intIndex -= 2; + int intIndex; + intIndex--; + } + intIndex = 10; + while(intIndex > 2) // GOOD + { + buf[intIndex] = 1; + --intIndex; + int intIndex; + intIndex--; + } } From 4709e8139d88ff445352716fdc6177e8a4995d70 Mon Sep 17 00:00:00 2001 From: luchua-bc Date: Mon, 3 May 2021 01:43:56 +0000 Subject: [PATCH 087/168] JPython code injection --- .../CWE/CWE-094/JPythonInjection.java | 48 ++++ .../CWE/CWE-094/JPythonInjection.qhelp | 34 +++ .../Security/CWE/CWE-094/JPythonInjection.ql | 68 +++++ .../CWE-094/JPythonInjection.expected | 11 + .../security/CWE-094/JPythonInjection.java | 64 +++++ .../security/CWE-094/JPythonInjection.qlref | 1 + .../query-tests/security/CWE-094/options | 2 +- .../jpython-2.7.2/org/python/core/PyCode.java | 43 +++ .../org/python/core/PyException.java | 12 + .../org/python/core/PyObject.java | 11 + .../org/python/core/PySystemState.java | 177 ++++++++++++ .../org/python/core/ThreadState.java | 28 ++ .../org/python/util/PythonInterpreter.java | 252 ++++++++++++++++++ 13 files changed, 750 insertions(+), 1 deletion(-) create mode 100644 java/ql/src/experimental/Security/CWE/CWE-094/JPythonInjection.java create mode 100644 java/ql/src/experimental/Security/CWE/CWE-094/JPythonInjection.qhelp create mode 100644 java/ql/src/experimental/Security/CWE/CWE-094/JPythonInjection.ql create mode 100644 java/ql/test/experimental/query-tests/security/CWE-094/JPythonInjection.expected create mode 100644 java/ql/test/experimental/query-tests/security/CWE-094/JPythonInjection.java create mode 100644 java/ql/test/experimental/query-tests/security/CWE-094/JPythonInjection.qlref create mode 100644 java/ql/test/stubs/jpython-2.7.2/org/python/core/PyCode.java create mode 100644 java/ql/test/stubs/jpython-2.7.2/org/python/core/PyException.java create mode 100644 java/ql/test/stubs/jpython-2.7.2/org/python/core/PyObject.java create mode 100644 java/ql/test/stubs/jpython-2.7.2/org/python/core/PySystemState.java create mode 100644 java/ql/test/stubs/jpython-2.7.2/org/python/core/ThreadState.java create mode 100644 java/ql/test/stubs/jpython-2.7.2/org/python/util/PythonInterpreter.java diff --git a/java/ql/src/experimental/Security/CWE/CWE-094/JPythonInjection.java b/java/ql/src/experimental/Security/CWE/CWE-094/JPythonInjection.java new file mode 100644 index 00000000000..13db6830a71 --- /dev/null +++ b/java/ql/src/experimental/Security/CWE/CWE-094/JPythonInjection.java @@ -0,0 +1,48 @@ +public class JPythonInjection extends HttpServlet { + protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { + response.setContentType("text/plain"); + String code = request.getParameter("code"); + PythonInterpreter interpreter = null; + ByteArrayOutputStream out = new ByteArrayOutputStream(); + + try { + interpreter = new PythonInterpreter(); + interpreter.setOut(out); + interpreter.setErr(out); + + // BAD: allow arbitrary JPython expression to execute + interpreter.exec(code); + out.flush(); + + response.getWriter().print(out.toString()); + } catch(PyException ex) { + response.getWriter().println(ex.getMessage()); + } finally { + if (interpreter != null) { + interpreter.close(); + } + out.close(); + } + } + + protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { + response.setContentType("text/plain"); + String code = request.getParameter("code"); + PythonInterpreter interpreter = null; + + try { + interpreter = new PythonInterpreter(); + // BAD: allow arbitrary JPython expression to evaluate + PyObject py = interpreter.eval(code); + + response.getWriter().print(py.toString()); + } catch(PyException ex) { + response.getWriter().println(ex.getMessage()); + } finally { + if (interpreter != null) { + interpreter.close(); + } + } + } +} + diff --git a/java/ql/src/experimental/Security/CWE/CWE-094/JPythonInjection.qhelp b/java/ql/src/experimental/Security/CWE/CWE-094/JPythonInjection.qhelp new file mode 100644 index 00000000000..dddbb2d618a --- /dev/null +++ b/java/ql/src/experimental/Security/CWE/CWE-094/JPythonInjection.qhelp @@ -0,0 +1,34 @@ + + + + +

    Python has been the most widely used programming language in recent years, and JPython + is a popular Java implementation of Python. It allows embedded Python scripting inside + Java applications and provides an interactive interpreter that can be used to interact + with Java packages or with running Java applications. If an expression is built using + attacker-controlled data and then evaluated, it may allow the attacker to run arbitrary + code.

    +
    + + +

    In general, including user input in JPython expression should be avoided. If user input + must be included in an expression, it should be then evaluated in a safe context that + doesn't allow arbitrary code invocation.

    +
    + + +

    The following code could execute random code in JPython Interpreter

    + +
    + + +
  • + JPython Organization: JPython and Java Integration +
  • +
  • + PortSwigger: Python code injection +
  • +
    +
    diff --git a/java/ql/src/experimental/Security/CWE/CWE-094/JPythonInjection.ql b/java/ql/src/experimental/Security/CWE/CWE-094/JPythonInjection.ql new file mode 100644 index 00000000000..a6621d89c26 --- /dev/null +++ b/java/ql/src/experimental/Security/CWE/CWE-094/JPythonInjection.ql @@ -0,0 +1,68 @@ +/** + * @name Injection in JPython + * @description Evaluation of a user-controlled malicious expression in JPython + * may lead to remote code execution. + * @kind path-problem + * @id java/jpython-injection + * @tags security + * external/cwe/cwe-094 + * external/cwe/cwe-095 + */ + +import java +import semmle.code.java.dataflow.FlowSources +import DataFlow::PathGraph + +/** The class `org.python.util.PythonInterpreter`. */ +class PythonInterpreter extends RefType { + PythonInterpreter() { this.hasQualifiedName("org.python.util", "PythonInterpreter") } +} + +/** A method that evaluates, compiles or executes a JPython expression. */ +class InterpretExprMethod extends Method { + InterpretExprMethod() { + this.getDeclaringType().getAnAncestor*() instanceof PythonInterpreter and + ( + hasName("exec") or + hasName("eval") or + hasName("compile") + ) + } +} + +/** Holds if a JPython expression if evaluated, compiled or executed. */ +predicate runCode(MethodAccess ma, Expr sink) { + exists(Method m | m = ma.getMethod() | + m instanceof InterpretExprMethod and + sink = ma.getArgument(0) + ) +} + +/** Sink of an expression interpreted by JPython interpreter. */ +class CodeInjectionSink extends DataFlow::ExprNode { + CodeInjectionSink() { runCode(_, this.getExpr()) } + + MethodAccess getMethodAccess() { runCode(result, this.getExpr()) } +} + +class CodeInjectionConfiguration extends TaintTracking::Configuration { + CodeInjectionConfiguration() { this = "CodeInjectionConfiguration" } + + override predicate isSource(DataFlow::Node source) { + source instanceof RemoteFlowSource + or + source instanceof LocalUserInput + } + + override predicate isSink(DataFlow::Node sink) { sink instanceof CodeInjectionSink } + + override predicate isAdditionalTaintStep(DataFlow::Node node1, DataFlow::Node node2) { + // @RequestBody MyQueryObj query; interpreter.exec(query.getInterpreterCode()); + exists(MethodAccess ma | ma.getQualifier() = node1.asExpr() and ma = node2.asExpr()) + } +} + +from DataFlow::PathNode source, DataFlow::PathNode sink, CodeInjectionConfiguration conf +where conf.hasFlowPath(source, sink) +select sink.getNode().(CodeInjectionSink).getMethodAccess(), source, sink, "JPython evaluate $@.", + source.getNode(), "user input" diff --git a/java/ql/test/experimental/query-tests/security/CWE-094/JPythonInjection.expected b/java/ql/test/experimental/query-tests/security/CWE-094/JPythonInjection.expected new file mode 100644 index 00000000000..f5816001939 --- /dev/null +++ b/java/ql/test/experimental/query-tests/security/CWE-094/JPythonInjection.expected @@ -0,0 +1,11 @@ +edges +| JPythonInjection.java:22:23:22:50 | getParameter(...) : String | JPythonInjection.java:30:28:30:31 | code | +| JPythonInjection.java:47:21:47:48 | getParameter(...) : String | JPythonInjection.java:52:40:52:43 | code | +nodes +| JPythonInjection.java:22:23:22:50 | getParameter(...) : String | semmle.label | getParameter(...) : String | +| JPythonInjection.java:30:28:30:31 | code | semmle.label | code | +| JPythonInjection.java:47:21:47:48 | getParameter(...) : String | semmle.label | getParameter(...) : String | +| JPythonInjection.java:52:40:52:43 | code | semmle.label | code | +#select +| JPythonInjection.java:30:11:30:32 | exec(...) | JPythonInjection.java:22:23:22:50 | getParameter(...) : String | JPythonInjection.java:30:28:30:31 | code | JPython evaluate $@. | JPythonInjection.java:22:23:22:50 | getParameter(...) | user input | +| JPythonInjection.java:52:23:52:44 | eval(...) | JPythonInjection.java:47:21:47:48 | getParameter(...) : String | JPythonInjection.java:52:40:52:43 | code | JPython evaluate $@. | JPythonInjection.java:47:21:47:48 | getParameter(...) | user input | diff --git a/java/ql/test/experimental/query-tests/security/CWE-094/JPythonInjection.java b/java/ql/test/experimental/query-tests/security/CWE-094/JPythonInjection.java new file mode 100644 index 00000000000..a0515eb4212 --- /dev/null +++ b/java/ql/test/experimental/query-tests/security/CWE-094/JPythonInjection.java @@ -0,0 +1,64 @@ +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import javax.servlet.ServletException; +import javax.servlet.http.HttpServlet; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +import org.python.core.PyObject; +import org.python.core.PyException; +import org.python.util.PythonInterpreter; + +public class JPythonInjection extends HttpServlet { + private static final long serialVersionUID = 1L; + + public JPythonInjection() { + super(); + } + + // BAD: allow arbitrary JPython expression to execute + protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { + response.setContentType("text/plain"); + String code = request.getParameter("code"); + PythonInterpreter interpreter = null; + ByteArrayOutputStream out = new ByteArrayOutputStream(); + + try { + interpreter = new PythonInterpreter(); + interpreter.setOut(out); + interpreter.setErr(out); + interpreter.exec(code); + out.flush(); + + response.getWriter().print(out.toString()); + } catch(PyException ex) { + response.getWriter().println(ex.getMessage()); + } finally { + if (interpreter != null) { + interpreter.close(); + } + out.close(); + } + } + + // BAD: allow arbitrary JPython expression to evaluate + protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { + response.setContentType("text/plain"); + String code = request.getParameter("code"); + PythonInterpreter interpreter = null; + + try { + interpreter = new PythonInterpreter(); + PyObject py = interpreter.eval(code); + + response.getWriter().print(py.toString()); + } catch(PyException ex) { + response.getWriter().println(ex.getMessage()); + } finally { + if (interpreter != null) { + interpreter.close(); + } + } + } +} + diff --git a/java/ql/test/experimental/query-tests/security/CWE-094/JPythonInjection.qlref b/java/ql/test/experimental/query-tests/security/CWE-094/JPythonInjection.qlref new file mode 100644 index 00000000000..80217a193bd --- /dev/null +++ b/java/ql/test/experimental/query-tests/security/CWE-094/JPythonInjection.qlref @@ -0,0 +1 @@ +experimental/Security/CWE/CWE-094/JPythonInjection.ql \ No newline at end of file diff --git a/java/ql/test/experimental/query-tests/security/CWE-094/options b/java/ql/test/experimental/query-tests/security/CWE-094/options index 18e3518fc97..ccf3a24f215 100644 --- a/java/ql/test/experimental/query-tests/security/CWE-094/options +++ b/java/ql/test/experimental/query-tests/security/CWE-094/options @@ -1,2 +1,2 @@ -//semmle-extractor-options: --javac-args -cp ${testdir}/../../../../stubs/springframework-5.2.3:${testdir}/../../../../stubs/mvel2-2.4.7:${testdir}/../../../../stubs/jsr223-api:${testdir}/../../../../stubs/apache-commons-jexl-2.1.1:${testdir}/../../../../stubs/apache-commons-jexl-3.1:${testdir}/../../../../stubs/scriptengine:${testdir}/../../../../stubs/java-ee-el:${testdir}/../../../../stubs/juel-2.2:${testdir}/../../../stubs/groovy-all-3.0.7:${testdir}/../../../../stubs/servlet-api-2.4 +//semmle-extractor-options: --javac-args -cp ${testdir}/../../../../stubs/springframework-5.2.3:${testdir}/../../../../stubs/mvel2-2.4.7:${testdir}/../../../../stubs/jsr223-api:${testdir}/../../../../stubs/apache-commons-jexl-2.1.1:${testdir}/../../../../stubs/apache-commons-jexl-3.1:${testdir}/../../../../stubs/scriptengine:${testdir}/../../../../stubs/java-ee-el:${testdir}/../../../../stubs/juel-2.2:${testdir}/../../../stubs/groovy-all-3.0.7:${testdir}/../../../../stubs/servlet-api-2.4:${testdir}/../../../../stubs/jpython-2.7.2 diff --git a/java/ql/test/stubs/jpython-2.7.2/org/python/core/PyCode.java b/java/ql/test/stubs/jpython-2.7.2/org/python/core/PyCode.java new file mode 100644 index 00000000000..9b7c99f94fa --- /dev/null +++ b/java/ql/test/stubs/jpython-2.7.2/org/python/core/PyCode.java @@ -0,0 +1,43 @@ +// Copyright (c) Corporation for National Research Initiatives +package org.python.core; + +/** + * A super class for all python code implementations. + */ +public abstract class PyCode extends PyObject +{ + abstract public PyObject call(ThreadState state, + PyObject args[], String keywords[], + PyObject globals, PyObject[] defaults, + PyObject closure); + + abstract public PyObject call(ThreadState state, + PyObject self, PyObject args[], + String keywords[], + PyObject globals, PyObject[] defaults, + PyObject closure); + + abstract public PyObject call(ThreadState state, + PyObject globals, PyObject[] defaults, + PyObject closure); + + abstract public PyObject call(ThreadState state, + PyObject arg1, PyObject globals, + PyObject[] defaults, PyObject closure); + + abstract public PyObject call(ThreadState state, + PyObject arg1, PyObject arg2, + PyObject globals, PyObject[] defaults, + PyObject closure); + + abstract public PyObject call(ThreadState state, + PyObject arg1, PyObject arg2, PyObject arg3, + PyObject globals, PyObject[] defaults, + PyObject closure); + + abstract public PyObject call(ThreadState state, + PyObject arg1, PyObject arg2, PyObject arg3, PyObject arg4, + PyObject globals, PyObject[] defaults, + PyObject closure); + +} diff --git a/java/ql/test/stubs/jpython-2.7.2/org/python/core/PyException.java b/java/ql/test/stubs/jpython-2.7.2/org/python/core/PyException.java new file mode 100644 index 00000000000..3a0a6c52c69 --- /dev/null +++ b/java/ql/test/stubs/jpython-2.7.2/org/python/core/PyException.java @@ -0,0 +1,12 @@ +// Copyright (c) Corporation for National Research Initiatives +package org.python.core; +import java.io.*; + +/** + * A wrapper for all python exception. Note that the well-known python exceptions are not + * subclasses of PyException. Instead the python exception class is stored in the type + * field and value or class instance is stored in the value field. + */ +public class PyException extends RuntimeException +{ +} diff --git a/java/ql/test/stubs/jpython-2.7.2/org/python/core/PyObject.java b/java/ql/test/stubs/jpython-2.7.2/org/python/core/PyObject.java new file mode 100644 index 00000000000..00993123461 --- /dev/null +++ b/java/ql/test/stubs/jpython-2.7.2/org/python/core/PyObject.java @@ -0,0 +1,11 @@ +// Copyright (c) Corporation for National Research Initiatives +package org.python.core; + +import java.io.Serializable; + +/** + * All objects known to the Jython runtime system are represented by an instance of the class + * {@code PyObject} or one of its subclasses. + */ +public class PyObject implements Serializable { +} diff --git a/java/ql/test/stubs/jpython-2.7.2/org/python/core/PySystemState.java b/java/ql/test/stubs/jpython-2.7.2/org/python/core/PySystemState.java new file mode 100644 index 00000000000..8444bbba70e --- /dev/null +++ b/java/ql/test/stubs/jpython-2.7.2/org/python/core/PySystemState.java @@ -0,0 +1,177 @@ +// Copyright (c) Corporation for National Research Initiatives +package org.python.core; + +import java.io.File; +import java.io.IOException; +import java.util.Properties; + +/** + * The "sys" module. + */ +// xxx Many have lamented, this should really be a module! +// but it will require some refactoring to see this wish come true. +public class PySystemState extends PyObject { + public PySystemState() { + } + + public static void classDictInit(PyObject dict) { + } + + public ClassLoader getSyspathJavaLoader() { + return null; + } + + // xxx fix this accessors + public PyObject __findattr_ex__(String name) { + return null; + } + + public void __setattr__(String name, PyObject value) { + } + + public void __delattr__(String name) { + } + + public PyObject gettrace() { + return null; + } + + public void settrace(PyObject tracefunc) { + } + + /** + * Change the current working directory to the specified path. + * + * path is assumed to be absolute and canonical (via os.path.realpath). + * + * @param path a path String + */ + public void setCurrentWorkingDir(String path) { + } + + /** + * Return a string representing the current working directory. + * + * @return a path String + */ + public String getCurrentWorkingDir() { + return null; + } + + /** + * Resolve a path. Returns the full path taking the current working directory into account. + * + * @param path a path String + * @return a resolved path String + */ + public String getPath(String path) { + return null; + } + + /** + * Resolve a path, returning a {@link File}, taking the current working directory into account. + * + * @param path a path String + * @return a resolved File + */ + public File getFile(String path) { + return null; + } + + public ClassLoader getClassLoader() { + return null; + } + + public void setClassLoader(ClassLoader classLoader) { + } + + public static Properties getBaseProperties() { + return null; + } + + public static synchronized void initialize() { + } + + public static synchronized void initialize(Properties preProperties, + Properties postProperties) { + } + + public static synchronized void initialize(Properties preProperties, Properties postProperties, + String[] argv) { + } + + public static synchronized void initialize(Properties preProperties, Properties postProperties, + String[] argv, ClassLoader classLoader) { + } + + /** + * Add a classpath directory to the list of places that are searched for java packages. + *

    + * Note. Classes found in directory and sub-directory are not made available to jython by + * this call. It only makes the java package found in the directory available. This call is + * mostly useful if jython is embedded in an application that deals with its own class loaders. + * A servlet container is a very good example. Calling + * {@code add_classdir("/WEB-INF/classes")} makes the java packages in WEB-INF classes + * available to jython import. However the actual class loading is completely handled by the + * servlet container's context classloader. + */ + public static void add_classdir(String directoryPath) { + } + + /** + * Add a .jar and .zip directory to the list of places that are searched for java .jar and .zip + * files. The .jar and .zip files found will not be cached. + *

    + * Note. Classes in .jar and .zip files found in the directory are not made available to + * jython by this call. See the note for add_classdir(dir) for more details. + * + * @param directoryPath The name of a directory. + * + * @see #add_classdir + */ + public static void add_extdir(String directoryPath) { + } + + /** + * Add a .jar and .zip directory to the list of places that are searched for java .jar and .zip + * files. + *

    + * Note. Classes in .jar and .zip files found in the directory are not made available to + * jython by this call. See the note for add_classdir(dir) for more details. + * + * @param directoryPath The name of a directory. + * @param cache Controls if the packages in the zip and jar file should be cached. + * + * @see #add_classdir + */ + public static void add_extdir(String directoryPath, boolean cache) { + } + + // Not public by design. We can't rebind the displayhook if + // a reflected function is inserted in the class dict. + + /** + * Exit a Python program with the given status. + * + * @param status the value to exit with + * @throws PyException {@code SystemExit} always throws this exception. When caught at top level + * the program will exit. + */ + public static void exit(PyObject status) { + } + + /** + * Exit a Python program with the status 0. + */ + public static void exit() { + } + + public static void exc_clear() { + } + + public void cleanup() { + } + + public void close() { + } +} diff --git a/java/ql/test/stubs/jpython-2.7.2/org/python/core/ThreadState.java b/java/ql/test/stubs/jpython-2.7.2/org/python/core/ThreadState.java new file mode 100644 index 00000000000..920270fe053 --- /dev/null +++ b/java/ql/test/stubs/jpython-2.7.2/org/python/core/ThreadState.java @@ -0,0 +1,28 @@ +// Copyright (c) Corporation for National Research Initiatives +package org.python.core; + +// a ThreadState refers to one PySystemState; this weak ref allows for tracking all ThreadState objects +// that refer to a given PySystemState + +public class ThreadState { + + public PyException exception; + + public ThreadState(PySystemState systemState) { + setSystemState(systemState); + } + + public void setSystemState(PySystemState systemState) { + } + + public PySystemState getSystemState() { + return null; + } + + public boolean enterRepr(PyObject obj) { + return false; + } + + public void exitRepr(PyObject obj) { + } +} diff --git a/java/ql/test/stubs/jpython-2.7.2/org/python/util/PythonInterpreter.java b/java/ql/test/stubs/jpython-2.7.2/org/python/util/PythonInterpreter.java new file mode 100644 index 00000000000..92c50917b59 --- /dev/null +++ b/java/ql/test/stubs/jpython-2.7.2/org/python/util/PythonInterpreter.java @@ -0,0 +1,252 @@ +package org.python.util; + +import java.io.Closeable; +import java.io.Reader; +import java.io.StringReader; +import java.util.Properties; + +import org.python.core.PyCode; +import org.python.core.PyObject; + +/** + * The PythonInterpreter class is a standard wrapper for a Jython interpreter for embedding in a + * Java application. + */ +public class PythonInterpreter implements Closeable { + + /** + * Initializes the Jython runtime. This should only be called once, before any other Python + * objects (including PythonInterpreter) are created. + * + * @param preProperties A set of properties. Typically System.getProperties() is used. + * preProperties override properties from the registry file. + * @param postProperties Another set of properties. Values like python.home, python.path and all + * other values from the registry files can be added to this property set. + * postProperties override system properties and registry properties. + * @param argv Command line arguments, assigned to sys.argv. + */ + public static void + initialize(Properties preProperties, Properties postProperties, String[] argv) { + } + + /** + * Creates a new interpreter with an empty local namespace. + */ + public PythonInterpreter() { + } + + /** + * Creates a new interpreter with the ability to maintain a separate local namespace for each + * thread (set by invoking setLocals()). + * + * @param dict a Python mapping object (e.g., a dictionary) for use as the default namespace + */ + public static PythonInterpreter threadLocalStateInterpreter(PyObject dict) { + return null; + } + + /** + * Creates a new interpreter with a specified local namespace. + * + * @param dict a Python mapping object (e.g., a dictionary) for use as the namespace + */ + public PythonInterpreter(PyObject dict) { + } + + /** + * Sets a Python object to use for the standard output stream, sys.stdout. This + * stream is used in a byte-oriented way (mostly) that depends on the type of file-like object. + * The behaviour as implemented is: + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + *
    Stream behaviour for various object types
    Python type of object o written
    str/bytesunicodeAny other type
    {@link PyFile}as bytes directlyrespect {@link PyFile#encoding}call str(o) first
    {@link PyFileWriter}each byte value as a charwrite as Java charscall o.toString() first
    Other {@link PyObject} finvoke f.write(str(o))invoke f.write(o)invoke f.write(str(o))
    + * + * @param outStream Python file-like object to use as the output stream + */ + public void setOut(PyObject outStream) { + } + + /** + * Sets a {@link java.io.Writer} to use for the standard output stream, sys.stdout. + * The behaviour as implemented is to output each object o by calling + * o.toString() and writing this as UTF-16. + * + * @param outStream to use as the output stream + */ + public void setOut(java.io.Writer outStream) { + } + + /** + * Sets a {@link java.io.OutputStream} to use for the standard output stream. + * + * @param outStream OutputStream to use as output stream + */ + public void setOut(java.io.OutputStream outStream) { + } + + /** + * Sets a Python object to use for the standard output stream, sys.stderr. This + * stream is used in a byte-oriented way (mostly) that depends on the type of file-like object, + * in the same way as {@link #setOut(PyObject)}. + * + * @param outStream Python file-like object to use as the error output stream + */ + public void setErr(PyObject outStream) { + } + + /** + * Sets a {@link java.io.Writer} to use for the standard output stream, sys.stdout. + * The behaviour as implemented is to output each object o by calling + * o.toString() and writing this as UTF-16. + * + * @param outStream to use as the error output stream + */ + public void setErr(java.io.Writer outStream) { + } + + public void setErr(java.io.OutputStream outStream) { + } + + /** + * Evaluates a string as a Python expression and returns the result. + */ + public PyObject eval(String s) { + return null; + } + + /** + * Evaluates a Python code object and returns the result. + */ + public PyObject eval(PyObject code) { + return null; + } + + /** + * Executes a string of Python source in the local namespace. + * + * In some environments, such as Windows, Unicode characters in the script will be converted + * into ascii question marks (?). This can be avoided by first compiling the fragment using + * PythonInterpreter.compile(), and using the overridden form of this method which takes a + * PyCode object. Code page declarations are not supported. + */ + public void exec(String s) { + } + + /** + * Executes a Python code object in the local namespace. + */ + public void exec(PyObject code) { + } + + /** + * Executes a file of Python source in the local namespace. + */ + public void execfile(String filename) { + } + + public void execfile(java.io.InputStream s) { + } + + public void execfile(java.io.InputStream s, String name) { + } + + /** + * Compiles a string of Python source as either an expression (if possible) or a module. + * + * Designed for use by a JSR 223 implementation: "the Scripting API does not distinguish between + * scripts which return values and those which do not, nor do they make the corresponding + * distinction between evaluating or executing objects." (SCR.4.2.1) + */ + public PyCode compile(String script) { + return null; + } + + public PyCode compile(Reader reader) { + return null; + } + + public PyCode compile(String script, String filename) { + return null; + } + + public PyCode compile(Reader reader, String filename) { + return null; + } + + /** + * Sets a variable in the local namespace. + * + * @param name the name of the variable + * @param value the object to set the variable to (as converted to an appropriate Python object) + */ + public void set(String name, Object value) { + } + + /** + * Sets a variable in the local namespace. + * + * @param name the name of the variable + * @param value the Python object to set the variable to + */ + public void set(String name, PyObject value) { + } + + /** + * Returns the value of a variable in the local namespace. + * + * @param name the name of the variable + * @return the value of the variable, or null if that name isn't assigned + */ + public PyObject get(String name) { + return null; + } + + /** + * Returns the value of a variable in the local namespace. + * + * The value will be returned as an instance of the given Java class. + * interp.get("foo", Object.class) will return the most appropriate generic Java + * object. + * + * @param name the name of the variable + * @param javaclass the class of object to return + * @return the value of the variable as the given class, or null if that name isn't assigned + */ + public T get(String name, Class javaclass) { + return null; + } + + public void cleanup() { + } + + public void close() { + } +} From 703fbf139aaf828b1bd954f5b60714036a99df64 Mon Sep 17 00:00:00 2001 From: luchua-bc Date: Tue, 4 May 2021 02:54:49 +0000 Subject: [PATCH 088/168] Add more methods and update the library name --- .../CWE/CWE-094/JPythonInjection.java | 48 ------ .../CWE/CWE-094/JPythonInjection.qhelp | 34 ----- .../Security/CWE/CWE-094/JPythonInjection.ql | 68 --------- .../Security/CWE/CWE-094/JythonInjection.java | 47 ++++++ .../CWE/CWE-094/JythonInjection.qhelp | 34 +++++ .../Security/CWE/CWE-094/JythonInjection.ql | 123 +++++++++++++++ .../CWE-094/JPythonInjection.expected | 11 -- .../security/CWE-094/JPythonInjection.java | 64 -------- .../security/CWE-094/JPythonInjection.qlref | 1 - .../security/CWE-094/JythonInjection.expected | 21 +++ .../security/CWE-094/JythonInjection.java | 144 ++++++++++++++++++ .../security/CWE-094/JythonInjection.qlref | 1 + .../query-tests/security/CWE-094/options | 2 +- .../org/python/antlr/base/mod.java | 5 + .../org/python/core/BytecodeLoader.java | 47 ++++++ .../org/python/core/CompileMode.java | 11 ++ .../org/python/core/CompilerFlags.java | 17 +++ .../jython-2.7.2/org/python/core/Py.java | 134 ++++++++++++++++ .../org/python/core/PyCode.java | 0 .../org/python/core/PyException.java | 0 .../org/python/core/PyObject.java | 0 .../org/python/core/PySystemState.java | 0 .../org/python/core/ThreadState.java | 0 .../python/util/InteractiveInterpreter.java | 114 ++++++++++++++ .../org/python/util/PythonInterpreter.java | 0 25 files changed, 699 insertions(+), 227 deletions(-) delete mode 100644 java/ql/src/experimental/Security/CWE/CWE-094/JPythonInjection.java delete mode 100644 java/ql/src/experimental/Security/CWE/CWE-094/JPythonInjection.qhelp delete mode 100644 java/ql/src/experimental/Security/CWE/CWE-094/JPythonInjection.ql create mode 100644 java/ql/src/experimental/Security/CWE/CWE-094/JythonInjection.java create mode 100644 java/ql/src/experimental/Security/CWE/CWE-094/JythonInjection.qhelp create mode 100644 java/ql/src/experimental/Security/CWE/CWE-094/JythonInjection.ql delete mode 100644 java/ql/test/experimental/query-tests/security/CWE-094/JPythonInjection.expected delete mode 100644 java/ql/test/experimental/query-tests/security/CWE-094/JPythonInjection.java delete mode 100644 java/ql/test/experimental/query-tests/security/CWE-094/JPythonInjection.qlref create mode 100644 java/ql/test/experimental/query-tests/security/CWE-094/JythonInjection.expected create mode 100644 java/ql/test/experimental/query-tests/security/CWE-094/JythonInjection.java create mode 100644 java/ql/test/experimental/query-tests/security/CWE-094/JythonInjection.qlref create mode 100644 java/ql/test/stubs/jython-2.7.2/org/python/antlr/base/mod.java create mode 100644 java/ql/test/stubs/jython-2.7.2/org/python/core/BytecodeLoader.java create mode 100644 java/ql/test/stubs/jython-2.7.2/org/python/core/CompileMode.java create mode 100644 java/ql/test/stubs/jython-2.7.2/org/python/core/CompilerFlags.java create mode 100644 java/ql/test/stubs/jython-2.7.2/org/python/core/Py.java rename java/ql/test/stubs/{jpython-2.7.2 => jython-2.7.2}/org/python/core/PyCode.java (100%) rename java/ql/test/stubs/{jpython-2.7.2 => jython-2.7.2}/org/python/core/PyException.java (100%) rename java/ql/test/stubs/{jpython-2.7.2 => jython-2.7.2}/org/python/core/PyObject.java (100%) rename java/ql/test/stubs/{jpython-2.7.2 => jython-2.7.2}/org/python/core/PySystemState.java (100%) rename java/ql/test/stubs/{jpython-2.7.2 => jython-2.7.2}/org/python/core/ThreadState.java (100%) create mode 100644 java/ql/test/stubs/jython-2.7.2/org/python/util/InteractiveInterpreter.java rename java/ql/test/stubs/{jpython-2.7.2 => jython-2.7.2}/org/python/util/PythonInterpreter.java (100%) diff --git a/java/ql/src/experimental/Security/CWE/CWE-094/JPythonInjection.java b/java/ql/src/experimental/Security/CWE/CWE-094/JPythonInjection.java deleted file mode 100644 index 13db6830a71..00000000000 --- a/java/ql/src/experimental/Security/CWE/CWE-094/JPythonInjection.java +++ /dev/null @@ -1,48 +0,0 @@ -public class JPythonInjection extends HttpServlet { - protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { - response.setContentType("text/plain"); - String code = request.getParameter("code"); - PythonInterpreter interpreter = null; - ByteArrayOutputStream out = new ByteArrayOutputStream(); - - try { - interpreter = new PythonInterpreter(); - interpreter.setOut(out); - interpreter.setErr(out); - - // BAD: allow arbitrary JPython expression to execute - interpreter.exec(code); - out.flush(); - - response.getWriter().print(out.toString()); - } catch(PyException ex) { - response.getWriter().println(ex.getMessage()); - } finally { - if (interpreter != null) { - interpreter.close(); - } - out.close(); - } - } - - protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { - response.setContentType("text/plain"); - String code = request.getParameter("code"); - PythonInterpreter interpreter = null; - - try { - interpreter = new PythonInterpreter(); - // BAD: allow arbitrary JPython expression to evaluate - PyObject py = interpreter.eval(code); - - response.getWriter().print(py.toString()); - } catch(PyException ex) { - response.getWriter().println(ex.getMessage()); - } finally { - if (interpreter != null) { - interpreter.close(); - } - } - } -} - diff --git a/java/ql/src/experimental/Security/CWE/CWE-094/JPythonInjection.qhelp b/java/ql/src/experimental/Security/CWE/CWE-094/JPythonInjection.qhelp deleted file mode 100644 index dddbb2d618a..00000000000 --- a/java/ql/src/experimental/Security/CWE/CWE-094/JPythonInjection.qhelp +++ /dev/null @@ -1,34 +0,0 @@ - - - - -

    Python has been the most widely used programming language in recent years, and JPython - is a popular Java implementation of Python. It allows embedded Python scripting inside - Java applications and provides an interactive interpreter that can be used to interact - with Java packages or with running Java applications. If an expression is built using - attacker-controlled data and then evaluated, it may allow the attacker to run arbitrary - code.

    - - - -

    In general, including user input in JPython expression should be avoided. If user input - must be included in an expression, it should be then evaluated in a safe context that - doesn't allow arbitrary code invocation.

    -
    - - -

    The following code could execute random code in JPython Interpreter

    - -
    - - -
  • - JPython Organization: JPython and Java Integration -
  • -
  • - PortSwigger: Python code injection -
  • -
    -
    diff --git a/java/ql/src/experimental/Security/CWE/CWE-094/JPythonInjection.ql b/java/ql/src/experimental/Security/CWE/CWE-094/JPythonInjection.ql deleted file mode 100644 index a6621d89c26..00000000000 --- a/java/ql/src/experimental/Security/CWE/CWE-094/JPythonInjection.ql +++ /dev/null @@ -1,68 +0,0 @@ -/** - * @name Injection in JPython - * @description Evaluation of a user-controlled malicious expression in JPython - * may lead to remote code execution. - * @kind path-problem - * @id java/jpython-injection - * @tags security - * external/cwe/cwe-094 - * external/cwe/cwe-095 - */ - -import java -import semmle.code.java.dataflow.FlowSources -import DataFlow::PathGraph - -/** The class `org.python.util.PythonInterpreter`. */ -class PythonInterpreter extends RefType { - PythonInterpreter() { this.hasQualifiedName("org.python.util", "PythonInterpreter") } -} - -/** A method that evaluates, compiles or executes a JPython expression. */ -class InterpretExprMethod extends Method { - InterpretExprMethod() { - this.getDeclaringType().getAnAncestor*() instanceof PythonInterpreter and - ( - hasName("exec") or - hasName("eval") or - hasName("compile") - ) - } -} - -/** Holds if a JPython expression if evaluated, compiled or executed. */ -predicate runCode(MethodAccess ma, Expr sink) { - exists(Method m | m = ma.getMethod() | - m instanceof InterpretExprMethod and - sink = ma.getArgument(0) - ) -} - -/** Sink of an expression interpreted by JPython interpreter. */ -class CodeInjectionSink extends DataFlow::ExprNode { - CodeInjectionSink() { runCode(_, this.getExpr()) } - - MethodAccess getMethodAccess() { runCode(result, this.getExpr()) } -} - -class CodeInjectionConfiguration extends TaintTracking::Configuration { - CodeInjectionConfiguration() { this = "CodeInjectionConfiguration" } - - override predicate isSource(DataFlow::Node source) { - source instanceof RemoteFlowSource - or - source instanceof LocalUserInput - } - - override predicate isSink(DataFlow::Node sink) { sink instanceof CodeInjectionSink } - - override predicate isAdditionalTaintStep(DataFlow::Node node1, DataFlow::Node node2) { - // @RequestBody MyQueryObj query; interpreter.exec(query.getInterpreterCode()); - exists(MethodAccess ma | ma.getQualifier() = node1.asExpr() and ma = node2.asExpr()) - } -} - -from DataFlow::PathNode source, DataFlow::PathNode sink, CodeInjectionConfiguration conf -where conf.hasFlowPath(source, sink) -select sink.getNode().(CodeInjectionSink).getMethodAccess(), source, sink, "JPython evaluate $@.", - source.getNode(), "user input" diff --git a/java/ql/src/experimental/Security/CWE/CWE-094/JythonInjection.java b/java/ql/src/experimental/Security/CWE/CWE-094/JythonInjection.java new file mode 100644 index 00000000000..fca518443d1 --- /dev/null +++ b/java/ql/src/experimental/Security/CWE/CWE-094/JythonInjection.java @@ -0,0 +1,47 @@ +public class JythonInjection extends HttpServlet { + protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { + response.setContentType("text/plain"); + String code = request.getParameter("code"); + PythonInterpreter interpreter = null; + ByteArrayOutputStream out = new ByteArrayOutputStream(); + + try { + interpreter = new PythonInterpreter(); + interpreter.setOut(out); + interpreter.setErr(out); + + // BAD: allow arbitrary Jython expression to execute + interpreter.exec(code); + out.flush(); + + response.getWriter().print(out.toString()); + } catch(PyException ex) { + response.getWriter().println(ex.getMessage()); + } finally { + if (interpreter != null) { + interpreter.close(); + } + out.close(); + } + } + + protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { + response.setContentType("text/plain"); + String code = request.getParameter("code"); + PythonInterpreter interpreter = null; + + try { + interpreter = new PythonInterpreter(); + // BAD: allow arbitrary Jython expression to evaluate + PyObject py = interpreter.eval(code); + + response.getWriter().print(py.toString()); + } catch(PyException ex) { + response.getWriter().println(ex.getMessage()); + } finally { + if (interpreter != null) { + interpreter.close(); + } + } + } +} diff --git a/java/ql/src/experimental/Security/CWE/CWE-094/JythonInjection.qhelp b/java/ql/src/experimental/Security/CWE/CWE-094/JythonInjection.qhelp new file mode 100644 index 00000000000..8916296f93b --- /dev/null +++ b/java/ql/src/experimental/Security/CWE/CWE-094/JythonInjection.qhelp @@ -0,0 +1,34 @@ + + + + +

    Python has been the most widely used programming language in recent years, and Jython + (formerly known as JPython) is a popular Java implementation of Python. It allows + embedded Python scripting inside Java applications and provides an interactive interpreter + that can be used to interact with Java packages or with running Java applications. If an + expression is built using attacker-controlled data and then evaluated, it may allow the + attacker to run arbitrary code.

    +
    + + +

    In general, including user input in Jython expression should be avoided. If user input + must be included in an expression, it should be then evaluated in a safe context that + doesn't allow arbitrary code invocation.

    +
    + + +

    The following code could execute arbitrary code in Jython Interpreter

    + +
    + + +
  • + Jython Organization: Jython and Java Integration +
  • +
  • + PortSwigger: Python code injection +
  • +
    +
    diff --git a/java/ql/src/experimental/Security/CWE/CWE-094/JythonInjection.ql b/java/ql/src/experimental/Security/CWE/CWE-094/JythonInjection.ql new file mode 100644 index 00000000000..088c33e00fd --- /dev/null +++ b/java/ql/src/experimental/Security/CWE/CWE-094/JythonInjection.ql @@ -0,0 +1,123 @@ +/** + * @name Injection in Jython + * @description Evaluation of a user-controlled malicious expression in Java Python + * interpreter may lead to remote code execution. + * @kind path-problem + * @id java/jython-injection + * @tags security + * external/cwe/cwe-094 + * external/cwe/cwe-095 + */ + +import java +import semmle.code.java.dataflow.FlowSources +import DataFlow::PathGraph + +/** The class `org.python.util.PythonInterpreter`. */ +class PythonInterpreter extends RefType { + PythonInterpreter() { this.hasQualifiedName("org.python.util", "PythonInterpreter") } +} + +/** A method that evaluates, compiles or executes a Jython expression. */ +class InterpretExprMethod extends Method { + InterpretExprMethod() { + this.getDeclaringType().getAnAncestor*() instanceof PythonInterpreter and + ( + getName().matches("exec%") or + hasName("eval") or + hasName("compile") or + getName().matches("run%") + ) + } +} + +/** The class `org.python.core.BytecodeLoader`. */ +class BytecodeLoader extends RefType { + BytecodeLoader() { this.hasQualifiedName("org.python.core", "BytecodeLoader") } +} + +/** Holds if a Jython expression if evaluated, compiled or executed. */ +predicate runCode(MethodAccess ma, Expr sink) { + exists(Method m | m = ma.getMethod() | + m instanceof InterpretExprMethod and + sink = ma.getArgument(0) + ) +} + +/** A method that loads Java class data. */ +class LoadClassMethod extends Method { + LoadClassMethod() { + this.getDeclaringType().getAnAncestor*() instanceof BytecodeLoader and + ( + hasName("makeClass") or + hasName("makeCode") + ) + } +} + +/** Holds if a Java class file is loaded. */ +predicate loadClass(MethodAccess ma, Expr sink) { + exists(Method m, int i | m = ma.getMethod() | + m instanceof LoadClassMethod and + m.getParameter(i).getType() instanceof Array and // makeClass(java.lang.String name, byte[] data, ...) + sink = ma.getArgument(i) + ) +} + +/** The class `org.python.core.Py`. */ +class Py extends RefType { + Py() { this.hasQualifiedName("org.python.core", "Py") } +} + +/** A method that compiles code with `Py`. */ +class PyCompileMethod extends Method { + PyCompileMethod() { + this.getDeclaringType().getAnAncestor*() instanceof Py and + getName().matches("compile%") + } +} + +/** Holds if source code is compiled with `PyCompileMethod`. */ +predicate compile(MethodAccess ma, Expr sink) { + exists(Method m | m = ma.getMethod() | + m instanceof PyCompileMethod and + sink = ma.getArgument(0) + ) +} + +/** Sink of an expression loaded by Jython. */ +class CodeInjectionSink extends DataFlow::ExprNode { + CodeInjectionSink() { + runCode(_, this.getExpr()) or + loadClass(_, this.getExpr()) or + compile(_, this.getExpr()) + } + + MethodAccess getMethodAccess() { + runCode(result, this.getExpr()) or + loadClass(result, this.getExpr()) or + compile(result, this.getExpr()) + } +} + +class CodeInjectionConfiguration extends TaintTracking::Configuration { + CodeInjectionConfiguration() { this = "CodeInjectionConfiguration" } + + override predicate isSource(DataFlow::Node source) { + source instanceof RemoteFlowSource + or + source instanceof LocalUserInput + } + + override predicate isSink(DataFlow::Node sink) { sink instanceof CodeInjectionSink } + + override predicate isAdditionalTaintStep(DataFlow::Node node1, DataFlow::Node node2) { + // @RequestBody MyQueryObj query; interpreter.exec(query.getInterpreterCode()); + exists(MethodAccess ma | ma.getQualifier() = node1.asExpr() and ma = node2.asExpr()) + } +} + +from DataFlow::PathNode source, DataFlow::PathNode sink, CodeInjectionConfiguration conf +where conf.hasFlowPath(source, sink) +select sink.getNode().(CodeInjectionSink).getMethodAccess(), source, sink, "Jython evaluate $@.", + source.getNode(), "user input" diff --git a/java/ql/test/experimental/query-tests/security/CWE-094/JPythonInjection.expected b/java/ql/test/experimental/query-tests/security/CWE-094/JPythonInjection.expected deleted file mode 100644 index f5816001939..00000000000 --- a/java/ql/test/experimental/query-tests/security/CWE-094/JPythonInjection.expected +++ /dev/null @@ -1,11 +0,0 @@ -edges -| JPythonInjection.java:22:23:22:50 | getParameter(...) : String | JPythonInjection.java:30:28:30:31 | code | -| JPythonInjection.java:47:21:47:48 | getParameter(...) : String | JPythonInjection.java:52:40:52:43 | code | -nodes -| JPythonInjection.java:22:23:22:50 | getParameter(...) : String | semmle.label | getParameter(...) : String | -| JPythonInjection.java:30:28:30:31 | code | semmle.label | code | -| JPythonInjection.java:47:21:47:48 | getParameter(...) : String | semmle.label | getParameter(...) : String | -| JPythonInjection.java:52:40:52:43 | code | semmle.label | code | -#select -| JPythonInjection.java:30:11:30:32 | exec(...) | JPythonInjection.java:22:23:22:50 | getParameter(...) : String | JPythonInjection.java:30:28:30:31 | code | JPython evaluate $@. | JPythonInjection.java:22:23:22:50 | getParameter(...) | user input | -| JPythonInjection.java:52:23:52:44 | eval(...) | JPythonInjection.java:47:21:47:48 | getParameter(...) : String | JPythonInjection.java:52:40:52:43 | code | JPython evaluate $@. | JPythonInjection.java:47:21:47:48 | getParameter(...) | user input | diff --git a/java/ql/test/experimental/query-tests/security/CWE-094/JPythonInjection.java b/java/ql/test/experimental/query-tests/security/CWE-094/JPythonInjection.java deleted file mode 100644 index a0515eb4212..00000000000 --- a/java/ql/test/experimental/query-tests/security/CWE-094/JPythonInjection.java +++ /dev/null @@ -1,64 +0,0 @@ -import java.io.ByteArrayOutputStream; -import java.io.IOException; -import javax.servlet.ServletException; -import javax.servlet.http.HttpServlet; -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; - -import org.python.core.PyObject; -import org.python.core.PyException; -import org.python.util.PythonInterpreter; - -public class JPythonInjection extends HttpServlet { - private static final long serialVersionUID = 1L; - - public JPythonInjection() { - super(); - } - - // BAD: allow arbitrary JPython expression to execute - protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { - response.setContentType("text/plain"); - String code = request.getParameter("code"); - PythonInterpreter interpreter = null; - ByteArrayOutputStream out = new ByteArrayOutputStream(); - - try { - interpreter = new PythonInterpreter(); - interpreter.setOut(out); - interpreter.setErr(out); - interpreter.exec(code); - out.flush(); - - response.getWriter().print(out.toString()); - } catch(PyException ex) { - response.getWriter().println(ex.getMessage()); - } finally { - if (interpreter != null) { - interpreter.close(); - } - out.close(); - } - } - - // BAD: allow arbitrary JPython expression to evaluate - protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { - response.setContentType("text/plain"); - String code = request.getParameter("code"); - PythonInterpreter interpreter = null; - - try { - interpreter = new PythonInterpreter(); - PyObject py = interpreter.eval(code); - - response.getWriter().print(py.toString()); - } catch(PyException ex) { - response.getWriter().println(ex.getMessage()); - } finally { - if (interpreter != null) { - interpreter.close(); - } - } - } -} - diff --git a/java/ql/test/experimental/query-tests/security/CWE-094/JPythonInjection.qlref b/java/ql/test/experimental/query-tests/security/CWE-094/JPythonInjection.qlref deleted file mode 100644 index 80217a193bd..00000000000 --- a/java/ql/test/experimental/query-tests/security/CWE-094/JPythonInjection.qlref +++ /dev/null @@ -1 +0,0 @@ -experimental/Security/CWE/CWE-094/JPythonInjection.ql \ No newline at end of file diff --git a/java/ql/test/experimental/query-tests/security/CWE-094/JythonInjection.expected b/java/ql/test/experimental/query-tests/security/CWE-094/JythonInjection.expected new file mode 100644 index 00000000000..4f66cc83fbd --- /dev/null +++ b/java/ql/test/experimental/query-tests/security/CWE-094/JythonInjection.expected @@ -0,0 +1,21 @@ +edges +| JythonInjection.java:28:23:28:50 | getParameter(...) : String | JythonInjection.java:36:30:36:33 | code | +| JythonInjection.java:53:23:53:50 | getParameter(...) : String | JythonInjection.java:58:44:58:47 | code | +| JythonInjection.java:73:23:73:50 | getParameter(...) : String | JythonInjection.java:81:35:81:38 | code | +| JythonInjection.java:97:23:97:50 | getParameter(...) : String | JythonInjection.java:106:61:106:75 | getBytes(...) | +nodes +| JythonInjection.java:28:23:28:50 | getParameter(...) : String | semmle.label | getParameter(...) : String | +| JythonInjection.java:36:30:36:33 | code | semmle.label | code | +| JythonInjection.java:53:23:53:50 | getParameter(...) : String | semmle.label | getParameter(...) : String | +| JythonInjection.java:58:44:58:47 | code | semmle.label | code | +| JythonInjection.java:73:23:73:50 | getParameter(...) : String | semmle.label | getParameter(...) : String | +| JythonInjection.java:81:35:81:38 | code | semmle.label | code | +| JythonInjection.java:97:23:97:50 | getParameter(...) : String | semmle.label | getParameter(...) : String | +| JythonInjection.java:106:61:106:75 | getBytes(...) | semmle.label | getBytes(...) | +| JythonInjection.java:131:40:131:63 | getInputStream(...) | semmle.label | getInputStream(...) | +#select +| JythonInjection.java:36:13:36:34 | exec(...) | JythonInjection.java:28:23:28:50 | getParameter(...) : String | JythonInjection.java:36:30:36:33 | code | Jython evaluate $@. | JythonInjection.java:28:23:28:50 | getParameter(...) | user input | +| JythonInjection.java:58:27:58:48 | eval(...) | JythonInjection.java:53:23:53:50 | getParameter(...) : String | JythonInjection.java:58:44:58:47 | code | Jython evaluate $@. | JythonInjection.java:53:23:53:50 | getParameter(...) | user input | +| JythonInjection.java:81:13:81:39 | runsource(...) | JythonInjection.java:73:23:73:50 | getParameter(...) : String | JythonInjection.java:81:35:81:38 | code | Jython evaluate $@. | JythonInjection.java:73:23:73:50 | getParameter(...) | user input | +| JythonInjection.java:106:29:106:134 | makeCode(...) | JythonInjection.java:97:23:97:50 | getParameter(...) : String | JythonInjection.java:106:61:106:75 | getBytes(...) | Jython evaluate $@. | JythonInjection.java:97:23:97:50 | getParameter(...) | user input | +| JythonInjection.java:131:29:131:109 | compile(...) | JythonInjection.java:131:40:131:63 | getInputStream(...) | JythonInjection.java:131:40:131:63 | getInputStream(...) | Jython evaluate $@. | JythonInjection.java:131:40:131:63 | getInputStream(...) | user input | diff --git a/java/ql/test/experimental/query-tests/security/CWE-094/JythonInjection.java b/java/ql/test/experimental/query-tests/security/CWE-094/JythonInjection.java new file mode 100644 index 00000000000..682e8af5113 --- /dev/null +++ b/java/ql/test/experimental/query-tests/security/CWE-094/JythonInjection.java @@ -0,0 +1,144 @@ +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.InputStream; + +import javax.servlet.ServletException; +import javax.servlet.http.HttpServlet; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +import org.python.core.BytecodeLoader; +import org.python.core.Py; +import org.python.core.PyCode; +import org.python.core.PyException; +import org.python.core.PyObject; +import org.python.util.InteractiveInterpreter; +import org.python.util.PythonInterpreter; + +public class JythonInjection extends HttpServlet { + private static final long serialVersionUID = 1L; + + public JythonInjection() { + super(); + } + + // BAD: allow arbitrary Jython expression to execute + protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { + response.setContentType("text/plain"); + String code = request.getParameter("code"); + PythonInterpreter interpreter = null; + ByteArrayOutputStream out = new ByteArrayOutputStream(); + + try { + interpreter = new PythonInterpreter(); + interpreter.setOut(out); + interpreter.setErr(out); + interpreter.exec(code); + out.flush(); + + response.getWriter().print(out.toString()); + } catch(PyException ex) { + response.getWriter().println(ex.getMessage()); + } finally { + if (interpreter != null) { + interpreter.close(); + } + out.close(); + } + } + + // BAD: allow arbitrary Jython expression to evaluate + protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { + response.setContentType("text/plain"); + String code = request.getParameter("code"); + PythonInterpreter interpreter = null; + + try { + interpreter = new PythonInterpreter(); + PyObject py = interpreter.eval(code); + + response.getWriter().print(py.toString()); + } catch(PyException ex) { + response.getWriter().println(ex.getMessage()); + } finally { + if (interpreter != null) { + interpreter.close(); + } + } + } + + // BAD: allow arbitrary Jython expression to run + protected void doPut(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { + response.setContentType("text/plain"); + String code = request.getParameter("code"); + InteractiveInterpreter interpreter = null; + ByteArrayOutputStream out = new ByteArrayOutputStream(); + + try { + interpreter = new InteractiveInterpreter(); + interpreter.setOut(out); + interpreter.setErr(out); + interpreter.runsource(code); + out.flush(); + + response.getWriter().print(out.toString()); + } catch(PyException ex) { + response.getWriter().println(ex.getMessage()); + } finally { + if (interpreter != null) { + interpreter.close(); + } + } + } + + // BAD: load arbitrary class file to execute + protected void doTrace(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { + response.setContentType("text/plain"); + String code = request.getParameter("code"); + PythonInterpreter interpreter = null; + ByteArrayOutputStream out = new ByteArrayOutputStream(); + + try { + interpreter = new PythonInterpreter(); + interpreter.setOut(out); + interpreter.setErr(out); + + PyCode pyCode = BytecodeLoader.makeCode("test", code.getBytes(), getServletContext().getRealPath("/com/example/test.pyc")); + interpreter.exec(pyCode); + out.flush(); + + response.getWriter().print(out.toString()); + } catch(PyException ex) { + response.getWriter().println(ex.getMessage()); + } finally { + if (interpreter != null) { + interpreter.close(); + } + } + } + + // BAD: Compile Python code to execute + protected void doHead(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { + response.setContentType("text/plain"); + PythonInterpreter interpreter = null; + ByteArrayOutputStream out = new ByteArrayOutputStream(); + + try { + interpreter = new PythonInterpreter(); + interpreter.setOut(out); + interpreter.setErr(out); + + PyCode pyCode = Py.compile(request.getInputStream(), "Test.py", org.python.core.CompileMode.eval); + interpreter.exec(pyCode); + out.flush(); + + response.getWriter().print(out.toString()); + } catch(PyException ex) { + response.getWriter().println(ex.getMessage()); + } finally { + if (interpreter != null) { + interpreter.close(); + } + } + } +} diff --git a/java/ql/test/experimental/query-tests/security/CWE-094/JythonInjection.qlref b/java/ql/test/experimental/query-tests/security/CWE-094/JythonInjection.qlref new file mode 100644 index 00000000000..0ba9fd60621 --- /dev/null +++ b/java/ql/test/experimental/query-tests/security/CWE-094/JythonInjection.qlref @@ -0,0 +1 @@ +experimental/Security/CWE/CWE-094/JythonInjection.ql \ No newline at end of file diff --git a/java/ql/test/experimental/query-tests/security/CWE-094/options b/java/ql/test/experimental/query-tests/security/CWE-094/options index ccf3a24f215..95bc9acaa08 100644 --- a/java/ql/test/experimental/query-tests/security/CWE-094/options +++ b/java/ql/test/experimental/query-tests/security/CWE-094/options @@ -1,2 +1,2 @@ -//semmle-extractor-options: --javac-args -cp ${testdir}/../../../../stubs/springframework-5.2.3:${testdir}/../../../../stubs/mvel2-2.4.7:${testdir}/../../../../stubs/jsr223-api:${testdir}/../../../../stubs/apache-commons-jexl-2.1.1:${testdir}/../../../../stubs/apache-commons-jexl-3.1:${testdir}/../../../../stubs/scriptengine:${testdir}/../../../../stubs/java-ee-el:${testdir}/../../../../stubs/juel-2.2:${testdir}/../../../stubs/groovy-all-3.0.7:${testdir}/../../../../stubs/servlet-api-2.4:${testdir}/../../../../stubs/jpython-2.7.2 +//semmle-extractor-options: --javac-args -cp ${testdir}/../../../../stubs/springframework-5.2.3:${testdir}/../../../../stubs/mvel2-2.4.7:${testdir}/../../../../stubs/jsr223-api:${testdir}/../../../../stubs/apache-commons-jexl-2.1.1:${testdir}/../../../../stubs/apache-commons-jexl-3.1:${testdir}/../../../../stubs/scriptengine:${testdir}/../../../../stubs/java-ee-el:${testdir}/../../../../stubs/juel-2.2:${testdir}/../../../stubs/groovy-all-3.0.7:${testdir}/../../../../stubs/servlet-api-2.4:${testdir}/../../../../stubs/jython-2.7.2 diff --git a/java/ql/test/stubs/jython-2.7.2/org/python/antlr/base/mod.java b/java/ql/test/stubs/jython-2.7.2/org/python/antlr/base/mod.java new file mode 100644 index 00000000000..785212f62fa --- /dev/null +++ b/java/ql/test/stubs/jython-2.7.2/org/python/antlr/base/mod.java @@ -0,0 +1,5 @@ +// Autogenerated AST node +package org.python.antlr.base; + +public abstract class mod { +} diff --git a/java/ql/test/stubs/jython-2.7.2/org/python/core/BytecodeLoader.java b/java/ql/test/stubs/jython-2.7.2/org/python/core/BytecodeLoader.java new file mode 100644 index 00000000000..e414216ed03 --- /dev/null +++ b/java/ql/test/stubs/jython-2.7.2/org/python/core/BytecodeLoader.java @@ -0,0 +1,47 @@ +// Copyright (c) Corporation for National Research Initiatives +package org.python.core; + +import java.util.List; + +/** + * Utility class for loading compiled Python modules and Java classes defined in Python modules. + */ +public class BytecodeLoader { + + /** + * Turn the Java class file data into a Java class. + * + * @param name fully-qualified binary name of the class + * @param data a class file as a byte array + * @param referents super-classes and interfaces that the new class will reference. + */ + @SuppressWarnings("unchecked") + public static Class makeClass(String name, byte[] data, Class... referents) { + return null; + } + + /** + * Turn the Java class file data into a Java class. + * + * @param name the name of the class + * @param referents super-classes and interfaces that the new class will reference. + * @param data a class file as a byte array + */ + public static Class makeClass(String name, List> referents, byte[] data) { + return null; + } + + /** + * Turn the Java class file data for a compiled Python module into a {@code PyCode} object, by + * constructing an instance of the named class and calling the instance's + * {@link PyRunnable#getMain()}. + * + * @param name fully-qualified binary name of the class + * @param data a class file as a byte array + * @param filename to provide to the constructor of the named class + * @return the {@code PyCode} object produced by the named class' {@code getMain} + */ + public static PyCode makeCode(String name, byte[] data, String filename) { + return null; + } +} diff --git a/java/ql/test/stubs/jython-2.7.2/org/python/core/CompileMode.java b/java/ql/test/stubs/jython-2.7.2/org/python/core/CompileMode.java new file mode 100644 index 00000000000..cf7ad1e7201 --- /dev/null +++ b/java/ql/test/stubs/jython-2.7.2/org/python/core/CompileMode.java @@ -0,0 +1,11 @@ +package org.python.core; + +public enum CompileMode { + eval, + single, + exec; + + public static CompileMode getMode(String mode) { + return null; + } +} diff --git a/java/ql/test/stubs/jython-2.7.2/org/python/core/CompilerFlags.java b/java/ql/test/stubs/jython-2.7.2/org/python/core/CompilerFlags.java new file mode 100644 index 00000000000..916b93c84ea --- /dev/null +++ b/java/ql/test/stubs/jython-2.7.2/org/python/core/CompilerFlags.java @@ -0,0 +1,17 @@ +// At some future point this will also be extended - in conjunction with +// Py#compileFlags - to add +// support for a compiler factory that user code can choose in place of the +// normal compiler. +// (Perhaps a better name might have been "CompilerOptions".) + +package org.python.core; + +import java.io.Serializable; + +public class CompilerFlags implements Serializable { + public CompilerFlags() { + } + + public CompilerFlags(int co_flags) { + } +} diff --git a/java/ql/test/stubs/jython-2.7.2/org/python/core/Py.java b/java/ql/test/stubs/jython-2.7.2/org/python/core/Py.java new file mode 100644 index 00000000000..cc0c9f1e4bd --- /dev/null +++ b/java/ql/test/stubs/jython-2.7.2/org/python/core/Py.java @@ -0,0 +1,134 @@ +// Copyright (c) Corporation for National Research Initiatives +package org.python.core; + +import java.io.InputStream; +import java.io.Serializable; + +import org.python.antlr.base.mod; + +public final class Py { + /** + Convert a given PyObject to an instance of a Java class. + Identical to o.__tojava__(c) except that it will + raise a TypeError if the conversion fails. + @param o the PyObject to convert. + @param c the class to convert it to. + **/ + @SuppressWarnings("unchecked") + public static T tojava(PyObject o, Class c) { + return null; + } + + // ??pending: was @deprecated but is actually used by proxie code. + // Can get rid of it? + public static Object tojava(PyObject o, String s) { + return null; + } + + /** + * Uses the PyObjectAdapter passed to {@link PySystemState#initialize} to turn o into a PyObject. + * + * @see ClassicPyObjectAdapter - default PyObjectAdapter type + */ + public static PyObject java2py(Object o) { + return null; + } + + /** + * Uses the PyObjectAdapter passed to {@link PySystemState#initialize} to turn + * objects into an array of PyObjects. + * + * @see ClassicPyObjectAdapter - default PyObjectAdapter type + */ + public static PyObject[] javas2pys(Object... objects) { + return null; + } + + public static PyObject makeClass(String name, PyObject[] bases, PyCode code, + PyObject[] closure_cells) { + return null; + } + + public static PyObject makeClass(String name, PyObject base, PyObject dict) { + return null; + } + + /** + * Create a new Python class. + * + * @param name the String name of the class + * @param bases an array of PyObject base classes + * @param dict the class's namespace, containing the class body + * definition + * @return a new Python Class PyObject + */ + public static PyObject makeClass(String name, PyObject[] bases, PyObject dict) { + return null; + } + + public static CompilerFlags getCompilerFlags() { + return null; + } + + public static CompilerFlags getCompilerFlags(int flags, boolean dont_inherit) { + return null; + } + + public static CompilerFlags getCompilerFlags(CompilerFlags flags, boolean dont_inherit) { + return null; + } + + // w/o compiler-flags + public static PyCode compile(InputStream istream, String filename, CompileMode kind) { + return null; + } + + /** + * Entry point for compiling modules. + * + * @param node Module node, coming from the parsing process + * @param name Internal name for the compiled code. Typically generated by + * calling {@link #getName()}. + * @param filename Source file name + * @param linenumbers True to track source line numbers on the generated + * code + * @param printResults True to call the sys.displayhook on the result of + * the code + * @param cflags Compiler flags + * @return Code object for the compiled module + */ + public static PyCode compile_flags(mod node, String name, String filename, + boolean linenumbers, boolean printResults, + CompilerFlags cflags) { + return null; + } + + public static PyCode compile_flags(mod node, String filename, + CompileMode kind, CompilerFlags cflags) { + return null; + } + + /** + * Compiles python source code coming from a file or another external stream + */ + public static PyCode compile_flags(InputStream istream, String filename, + CompileMode kind, CompilerFlags cflags) { + return null; + } + + /** + * Compiles python source code coming from String (raw bytes) data. + * + * If the String is properly decoded (from PyUnicode) the PyCF_SOURCE_IS_UTF8 flag + * should be specified. + */ + public static PyCode compile_flags(String data, String filename, + CompileMode kind, CompilerFlags cflags) { + return null; + } + + public static PyObject compile_command_flags(String string, String filename, + CompileMode kind, CompilerFlags cflags, boolean stdprompt) { + return null; + } +} diff --git a/java/ql/test/stubs/jpython-2.7.2/org/python/core/PyCode.java b/java/ql/test/stubs/jython-2.7.2/org/python/core/PyCode.java similarity index 100% rename from java/ql/test/stubs/jpython-2.7.2/org/python/core/PyCode.java rename to java/ql/test/stubs/jython-2.7.2/org/python/core/PyCode.java diff --git a/java/ql/test/stubs/jpython-2.7.2/org/python/core/PyException.java b/java/ql/test/stubs/jython-2.7.2/org/python/core/PyException.java similarity index 100% rename from java/ql/test/stubs/jpython-2.7.2/org/python/core/PyException.java rename to java/ql/test/stubs/jython-2.7.2/org/python/core/PyException.java diff --git a/java/ql/test/stubs/jpython-2.7.2/org/python/core/PyObject.java b/java/ql/test/stubs/jython-2.7.2/org/python/core/PyObject.java similarity index 100% rename from java/ql/test/stubs/jpython-2.7.2/org/python/core/PyObject.java rename to java/ql/test/stubs/jython-2.7.2/org/python/core/PyObject.java diff --git a/java/ql/test/stubs/jpython-2.7.2/org/python/core/PySystemState.java b/java/ql/test/stubs/jython-2.7.2/org/python/core/PySystemState.java similarity index 100% rename from java/ql/test/stubs/jpython-2.7.2/org/python/core/PySystemState.java rename to java/ql/test/stubs/jython-2.7.2/org/python/core/PySystemState.java diff --git a/java/ql/test/stubs/jpython-2.7.2/org/python/core/ThreadState.java b/java/ql/test/stubs/jython-2.7.2/org/python/core/ThreadState.java similarity index 100% rename from java/ql/test/stubs/jpython-2.7.2/org/python/core/ThreadState.java rename to java/ql/test/stubs/jython-2.7.2/org/python/core/ThreadState.java diff --git a/java/ql/test/stubs/jython-2.7.2/org/python/util/InteractiveInterpreter.java b/java/ql/test/stubs/jython-2.7.2/org/python/util/InteractiveInterpreter.java new file mode 100644 index 00000000000..b12fa617227 --- /dev/null +++ b/java/ql/test/stubs/jython-2.7.2/org/python/util/InteractiveInterpreter.java @@ -0,0 +1,114 @@ +// Copyright (c) Corporation for National Research Initiatives +package org.python.util; + +import org.python.core.*; + +/** + * This class provides the interface for compiling and running code that supports an interactive + * interpreter. + */ +// Based on CPython-1.5.2's code module +public class InteractiveInterpreter extends PythonInterpreter { + + /** + * Construct an InteractiveInterpreter with all default characteristics: default state (from + * {@link Py#getSystemState()}), and a new empty dictionary of local variables. + * */ + public InteractiveInterpreter() { + } + + /** + * Construct an InteractiveInterpreter with state (from {@link Py#getSystemState()}), and the + * specified dictionary of local variables. + * + * @param locals dictionary to use, or if null, a new empty one will be created + */ + public InteractiveInterpreter(PyObject locals) { + } + + /** + * Construct an InteractiveInterpreter with, and system state the specified dictionary of local + * variables. + * + * @param locals dictionary to use, or if null, a new empty one will be created + * @param systemState interpreter state, or if null use {@link Py#getSystemState()} + */ + public InteractiveInterpreter(PyObject locals, PySystemState systemState) { + } + + /** + * Compile and run some source in the interpreter, in the mode {@link CompileMode#single} which + * is used for incremental compilation at the interactive console, known as {@code }. + * + * @param source Python code + * @return true to indicate a partial statement was entered + */ + public boolean runsource(String source) { + return false; + } + + /** + * Compile and run some source in the interpreter, in the mode {@link CompileMode#single} which + * is used for incremental compilation at the interactive console. + * + * @param source Python code + * @param filename name with which to label this console input (e.g. in error messages). + * @return true to indicate a partial statement was entered + */ + public boolean runsource(String source, String filename) { + return false; + } + + /** + * Compile and run some source in the interpreter, according to the {@link CompileMode} given. + * This method supports incremental compilation and interpretation through the return value, + * where {@code true} signifies that more input is expected in order to complete the Python + * statement. An interpreter can use this to decide whether to use {@code sys.ps1} + * ("{@code >>> }") or {@code sys.ps2} ("{@code ... }") to prompt the next line. The arguments + * are the same as the mandatory ones in the Python {@code compile()} command. + *

    + * One the following can happen: + *

      + *
    1. The input is incorrect; compilation raised an exception (SyntaxError or OverflowError). A + * syntax traceback will be printed by calling {@link #showexception(PyException)}. Return is + * {@code false}.
    2. + * + *
    3. The input is incomplete, and more input is required; compilation returned no code. + * Nothing happens. Return is {@code true}.
    4. + * + *
    5. The input is complete; compilation returned a code object. The code is executed by + * calling {@link #runcode(PyObject)} (which also handles run-time exceptions, except for + * SystemExit). Return is {@code false}.
    6. + *
    + * + * @param source Python code + * @param filename name with which to label this console input (e.g. in error messages). + * @param kind of compilation required: {@link CompileMode#eval}, {@link CompileMode#exec} or + * {@link CompileMode#single} + * @return {@code true} to indicate a partial statement was provided + */ + public boolean runsource(String source, String filename, CompileMode kind) { + return false; + } + + /** + * Execute a code object. When an exception occurs, {@link #showexception(PyException)} is + * called to display a stack trace, except in the case of SystemExit, which is re-raised. + *

    + * A note about KeyboardInterrupt: this exception may occur elsewhere in this code, and may not + * always be caught. The caller should be prepared to deal with it. + **/ + + // Make this run in another thread somehow???? + public void runcode(PyObject code) { + } + + public void showexception(PyException exc) { + } + + public void write(String data) { + } + + public void resetbuffer() { + } +} diff --git a/java/ql/test/stubs/jpython-2.7.2/org/python/util/PythonInterpreter.java b/java/ql/test/stubs/jython-2.7.2/org/python/util/PythonInterpreter.java similarity index 100% rename from java/ql/test/stubs/jpython-2.7.2/org/python/util/PythonInterpreter.java rename to java/ql/test/stubs/jython-2.7.2/org/python/util/PythonInterpreter.java From b277082462848f3b102fac3c4f79e9babe8b4048 Mon Sep 17 00:00:00 2001 From: ihsinme Date: Wed, 5 May 2021 23:28:04 +0300 Subject: [PATCH 089/168] Update DeclarationOfVariableWithUnnecessarilyWideScope.qhelp --- .../DeclarationOfVariableWithUnnecessarilyWideScope.qhelp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cpp/ql/src/experimental/Security/CWE/CWE-1126/DeclarationOfVariableWithUnnecessarilyWideScope.qhelp b/cpp/ql/src/experimental/Security/CWE/CWE-1126/DeclarationOfVariableWithUnnecessarilyWideScope.qhelp index d84f47f5453..5234212f7ca 100644 --- a/cpp/ql/src/experimental/Security/CWE/CWE-1126/DeclarationOfVariableWithUnnecessarilyWideScope.qhelp +++ b/cpp/ql/src/experimental/Security/CWE/CWE-1126/DeclarationOfVariableWithUnnecessarilyWideScope.qhelp @@ -3,7 +3,7 @@ "qhelp.dtd"> -

    Using variables with the same name is dangerous. However, such a situation inside the while loop can lead to a violation of the accessibility of the program. Requires the attention of developers.

    +

    Using variables with the same name is dangerous. However, such a situation inside the while loop can create an infinite loop exhausting resources. Requires the attention of developers.

    From 976ccda135748f5fff12250eed60f02e14d9aab3 Mon Sep 17 00:00:00 2001 From: ihsinme Date: Wed, 5 May 2021 23:34:21 +0300 Subject: [PATCH 090/168] Update DeclarationOfVariableWithUnnecessarilyWideScope.ql --- .../DeclarationOfVariableWithUnnecessarilyWideScope.ql | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/cpp/ql/src/experimental/Security/CWE/CWE-1126/DeclarationOfVariableWithUnnecessarilyWideScope.ql b/cpp/ql/src/experimental/Security/CWE/CWE-1126/DeclarationOfVariableWithUnnecessarilyWideScope.ql index 9acc1d35d81..be712b1cb1d 100644 --- a/cpp/ql/src/experimental/Security/CWE/CWE-1126/DeclarationOfVariableWithUnnecessarilyWideScope.ql +++ b/cpp/ql/src/experimental/Security/CWE/CWE-1126/DeclarationOfVariableWithUnnecessarilyWideScope.ql @@ -1,7 +1,7 @@ /** * @name Errors When Using Variable Declaration Inside Loop * @description Using variables with the same name is dangerous. - * However, such a situation inside the while loop can lead to a violation of the accessibility of the program. + * However, such a situation inside the while loop can create an infinite loop exhausting resources. * Requires the attention of developers. * @kind problem * @id cpp/errors-when-using-variable-declaration-inside-loop @@ -37,7 +37,7 @@ class DangerousWhileLoop extends WhileStmt { /** Holds when there are changes to the variables involved in the condition. */ predicate isUseThisVariable() { exists(Variable v | - this.getCondition().getAChild*().(VariableAccess).getTarget() = v and + exp.(VariableAccess).getTarget() = v and ( exists(Assignment aexp | aexp = this.getStmt().getAChild*() and From 67e9f063047d5040f73f21f641270e07a8a3a144 Mon Sep 17 00:00:00 2001 From: Jonathan Leitschuh Date: Wed, 5 May 2021 17:27:49 -0400 Subject: [PATCH 091/168] [Java] Fix Kryo FP & Kryo 5 Support Closes #4992 --- .../2021-05-05-kryo-improvements.md | 3 + .../code/java/dataflow/ExternalFlow.qll | 1 + .../src/semmle/code/java/frameworks/Kryo.qll | 55 ++++++++++++++++- .../java/security/UnsafeDeserialization.qll | 59 +++++++++++++++++++ .../security/CWE-502/KryoTest.java | 34 +++++++++++ .../kryo/pool/KryoCallback.java | 7 +++ .../kryo/pool/KryoFactory.java | 7 +++ .../esotericsoftware/kryo/pool/KryoPool.java | 30 ++++++++++ 8 files changed, 194 insertions(+), 2 deletions(-) create mode 100644 java/change-notes/2021-05-05-kryo-improvements.md create mode 100644 java/ql/test/query-tests/security/CWE-502/KryoTest.java create mode 100644 java/ql/test/stubs/kryo-4.0.2/com/esotericsoftware/kryo/pool/KryoCallback.java create mode 100644 java/ql/test/stubs/kryo-4.0.2/com/esotericsoftware/kryo/pool/KryoFactory.java create mode 100644 java/ql/test/stubs/kryo-4.0.2/com/esotericsoftware/kryo/pool/KryoPool.java diff --git a/java/change-notes/2021-05-05-kryo-improvements.md b/java/change-notes/2021-05-05-kryo-improvements.md new file mode 100644 index 00000000000..f8ba79eb863 --- /dev/null +++ b/java/change-notes/2021-05-05-kryo-improvements.md @@ -0,0 +1,3 @@ +lgtm,codescanning +* Add support for version 5 of the Kryo serialzation/deserialization framework. +* Add support for detecting safe uses of Kryo utilizing `KryoPool.Builder. (#4992)[https://github.com/github/codeql/issues/4992] diff --git a/java/ql/src/semmle/code/java/dataflow/ExternalFlow.qll b/java/ql/src/semmle/code/java/dataflow/ExternalFlow.qll index 7073c57ff9c..b62018dc2c4 100644 --- a/java/ql/src/semmle/code/java/dataflow/ExternalFlow.qll +++ b/java/ql/src/semmle/code/java/dataflow/ExternalFlow.qll @@ -291,6 +291,7 @@ private predicate summaryModelCsv(string row) { "java.util;StringTokenizer;false;StringTokenizer;;;Argument[0];Argument[-1];taint", "java.beans;XMLDecoder;false;XMLDecoder;;;Argument[0];Argument[-1];taint", "com.esotericsoftware.kryo.io;Input;false;Input;;;Argument[0];Argument[-1];taint", + "com.esotericsoftware.kryo5.io;Input;false;Input;;;Argument[0];Argument[-1];taint", "java.io;BufferedInputStream;false;BufferedInputStream;;;Argument[0];Argument[-1];taint", "java.io;DataInputStream;false;DataInputStream;;;Argument[0];Argument[-1];taint", "java.io;ByteArrayInputStream;false;ByteArrayInputStream;;;Argument[0];Argument[-1];taint", diff --git a/java/ql/src/semmle/code/java/frameworks/Kryo.qll b/java/ql/src/semmle/code/java/frameworks/Kryo.qll index 049be97ea9c..16873fc751f 100644 --- a/java/ql/src/semmle/code/java/frameworks/Kryo.qll +++ b/java/ql/src/semmle/code/java/frameworks/Kryo.qll @@ -3,19 +3,60 @@ */ import java +private import semmle.code.java.dataflow.DataFlow +private import semmle.code.java.dataflow.FlowSteps /** * The type `com.esotericsoftware.kryo.Kryo`. */ class Kryo extends RefType { - Kryo() { this.hasQualifiedName("com.esotericsoftware.kryo", "Kryo") } + Kryo() { + hasQualifiedName("com.esotericsoftware.kryo", "Kryo") or + hasQualifiedName("com.esotericsoftware.kryo5", "Kryo") + } } /** * A Kryo input stream. */ class KryoInput extends RefType { - KryoInput() { this.hasQualifiedName("com.esotericsoftware.kryo.io", "Input") } + KryoInput() { + hasQualifiedName("com.esotericsoftware.kryo.io", "Input") or + hasQualifiedName("com.esotericsoftware.kryo5.io", "Input") + } +} + +/** + * A Kryo pool. + */ +class KryoPool extends RefType { + KryoPool() { + hasQualifiedName("com.esotericsoftware.kryo.pool", "KryoPool") or + hasQualifiedName("com.esotericsoftware.kryo5.pool", "KryoPool") + } +} + +/** + * A Kryo pool builder. + */ +class KryoPoolBuilder extends RefType { + KryoPoolBuilder() { + hasQualifiedName("com.esotericsoftware.kryo.pool", "KryoPool$Builder") or + hasQualifiedName("com.esotericsoftware.kryo5.pool", "KryoPool$Builder") + } +} + +/** + * A Kryo pool builder method used a fluent API call chain. + */ +class KryoPoolBuilderMethod extends Method { + KryoPoolBuilderMethod() { + getDeclaringType() instanceof KryoPoolBuilder and + ( + getReturnType() instanceof KryoPoolBuilder or + getReturnType() instanceof KryoPool + ) + } } /** @@ -45,3 +86,13 @@ class KryoEnableWhiteListing extends MethodAccess { ) } } + +/** + * A KryoPool method that uses a Kryo instance. + */ +class KryoPoolRunMethod extends Method { + KryoPoolRunMethod() { + getDeclaringType() instanceof KryoPool and + hasName("run") + } +} diff --git a/java/ql/src/semmle/code/java/security/UnsafeDeserialization.qll b/java/ql/src/semmle/code/java/security/UnsafeDeserialization.qll index ab809f07d6d..def37c0964e 100644 --- a/java/ql/src/semmle/code/java/security/UnsafeDeserialization.qll +++ b/java/ql/src/semmle/code/java/security/UnsafeDeserialization.qll @@ -48,6 +48,65 @@ class SafeKryo extends DataFlow2::Configuration { ma.getMethod() instanceof KryoReadObjectMethod ) } + + override predicate isAdditionalFlowStep(DataFlow::Node node1, DataFlow::Node node2) { + stepKryoPoolBuilderFactoryArgToConstructor(node1, node2) or + stepKryoPoolRunMethodAccessQualifierToFunctionalArgument(node1, node2) or + stepKryoPoolBuilderChainMethod(node1, node2) or + stepKryoPoolBorrowMethod(node1, node2) + } + + /** + * Holds when a funcitonal expression is used to create a `KryoPool.Builder`. + * Eg. `new KryoPool.Builder(() -> new Kryo())` + */ + private predicate stepKryoPoolBuilderFactoryArgToConstructor( + DataFlow::Node node1, DataFlow::Node node2 + ) { + exists(ConstructorCall cc, FunctionalExpr fe | + cc.getConstructedType() instanceof KryoPoolBuilder and + fe.asMethod().getBody().getAStmt().(ReturnStmt).getResult() = node1.asExpr() and + node2.asExpr() = cc and + cc.getArgument(0) = fe + ) + } + + /** + * Holds when a `KryoPool.run` is called to use a `Kryo` instance. + * Eg. `pool.run(kryo -> ...)` + */ + private predicate stepKryoPoolRunMethodAccessQualifierToFunctionalArgument( + DataFlow::Node node1, DataFlow::Node node2 + ) { + exists(MethodAccess ma | + ma.getMethod() instanceof KryoPoolRunMethod and + node1.asExpr() = ma.getQualifier() and + ma.getArgument(0).(FunctionalExpr).asMethod().getParameter(0) = node2.asParameter() + ) + } + + /** + * Holds when a `KryoPool.Builder` method is called fluently. + */ + private predicate stepKryoPoolBuilderChainMethod(DataFlow::Node node1, DataFlow::Node node2) { + exists(MethodAccess ma | + ma.getMethod() instanceof KryoPoolBuilderMethod and + ma = node2.asExpr() and + ma.getQualifier() = node1.asExpr() + ) + } + + /** + * Holds when a `KryoPool.borrow` method is called. + */ + private predicate stepKryoPoolBorrowMethod(DataFlow::Node node1, DataFlow::Node node2) { + exists(MethodAccess ma | + ma.getMethod() = + any(Method m | m.getDeclaringType() instanceof KryoPool and m.hasName("borrow")) and + node1.asExpr() = ma.getQualifier() and + node2.asExpr() = ma + ) + } } predicate unsafeDeserialization(MethodAccess ma, Expr sink) { diff --git a/java/ql/test/query-tests/security/CWE-502/KryoTest.java b/java/ql/test/query-tests/security/CWE-502/KryoTest.java new file mode 100644 index 00000000000..8890bad91b9 --- /dev/null +++ b/java/ql/test/query-tests/security/CWE-502/KryoTest.java @@ -0,0 +1,34 @@ + +import java.io.*; +import java.net.Socket; +import com.esotericsoftware.kryo.Kryo; +import com.esotericsoftware.kryo.pool.KryoPool; +import com.esotericsoftware.kryo.io.Input; + +public class KryoTest { + + private Kryo getSafeKryo() { + Kryo kryo = new Kryo(); + kryo.setRegistrationRequired(true); + // ... kryo.register(A.class) ... + return kryo; + } + + public void kryoDeserialize(Socket sock) throws java.io.IOException { + KryoPool kryoPool = new KryoPool.Builder(this::getSafeKryo).softReferences().build(); + Input input = new Input(sock.getInputStream()); + Object o = kryoPool.run(kryo -> kryo.readClassAndObject(input)); // OK + } + + public void kryoDeserialize2(Socket sock) throws java.io.IOException { + KryoPool kryoPool = new KryoPool.Builder(this::getSafeKryo).softReferences().build(); + Input input = new Input(sock.getInputStream()); + Kryo k = kryoPool.borrow(); + try { + Object o = k.readClassAndObject(input); // OK + } finally { + kryoPool.release(k); + } + } + +} diff --git a/java/ql/test/stubs/kryo-4.0.2/com/esotericsoftware/kryo/pool/KryoCallback.java b/java/ql/test/stubs/kryo-4.0.2/com/esotericsoftware/kryo/pool/KryoCallback.java new file mode 100644 index 00000000000..729426aba62 --- /dev/null +++ b/java/ql/test/stubs/kryo-4.0.2/com/esotericsoftware/kryo/pool/KryoCallback.java @@ -0,0 +1,7 @@ +package com.esotericsoftware.kryo.pool; + +import com.esotericsoftware.kryo.Kryo; + +public interface KryoCallback { + T execute (Kryo kryo); +} diff --git a/java/ql/test/stubs/kryo-4.0.2/com/esotericsoftware/kryo/pool/KryoFactory.java b/java/ql/test/stubs/kryo-4.0.2/com/esotericsoftware/kryo/pool/KryoFactory.java new file mode 100644 index 00000000000..4dcda1445df --- /dev/null +++ b/java/ql/test/stubs/kryo-4.0.2/com/esotericsoftware/kryo/pool/KryoFactory.java @@ -0,0 +1,7 @@ +package com.esotericsoftware.kryo.pool; + +import com.esotericsoftware.kryo.Kryo; + +public interface KryoFactory { + Kryo create (); +} diff --git a/java/ql/test/stubs/kryo-4.0.2/com/esotericsoftware/kryo/pool/KryoPool.java b/java/ql/test/stubs/kryo-4.0.2/com/esotericsoftware/kryo/pool/KryoPool.java new file mode 100644 index 00000000000..c005442c9e8 --- /dev/null +++ b/java/ql/test/stubs/kryo-4.0.2/com/esotericsoftware/kryo/pool/KryoPool.java @@ -0,0 +1,30 @@ +package com.esotericsoftware.kryo.pool; + +import com.esotericsoftware.kryo.Kryo; +import java.util.Queue; + +public interface KryoPool { + + Kryo borrow (); + + void release (Kryo kryo); + + T run (KryoCallback callback); + + static class Builder { + public Builder (KryoFactory factory) { + } + + public Builder queue (Queue queue) { + return null; + } + + public Builder softReferences () { + return null; + } + + public KryoPool build () { + return null; + } + } +} From df5eab33f946ed0b88da4287f24659e01ab6e399 Mon Sep 17 00:00:00 2001 From: Asger Feldthaus Date: Mon, 10 May 2021 09:43:33 +0100 Subject: [PATCH 092/168] JS: Update relevantTaintSource() --- javascript/ql/src/meta/internal/TaintMetrics.qll | 11 ++--------- 1 file changed, 2 insertions(+), 9 deletions(-) diff --git a/javascript/ql/src/meta/internal/TaintMetrics.qll b/javascript/ql/src/meta/internal/TaintMetrics.qll index 6d10b2c6ad6..f6eae2eaa6e 100644 --- a/javascript/ql/src/meta/internal/TaintMetrics.qll +++ b/javascript/ql/src/meta/internal/TaintMetrics.qll @@ -75,16 +75,9 @@ DataFlow::Node relevantTaintSink(string kind) { DataFlow::Node relevantTaintSink() { result = relevantTaintSink(_) } /** - * Gets a remote flow source or `document.location` source. + * Gets a relevant remote flow source. */ -DataFlow::Node relevantTaintSource() { - not result.getFile() instanceof IgnoredFile and - ( - result instanceof RemoteFlowSource - or - result = DOM::locationSource() - ) -} +RemoteFlowSource relevantTaintSource() { not result.getFile() instanceof IgnoredFile } /** * Gets the output of a call that shows intent to sanitize a value From c8f2937df9bd4d990f0cf654161319c668475e13 Mon Sep 17 00:00:00 2001 From: ihsinme Date: Mon, 10 May 2021 14:16:11 +0300 Subject: [PATCH 093/168] Update DeclarationOfVariableWithUnnecessarilyWideScope.ql --- .../DeclarationOfVariableWithUnnecessarilyWideScope.ql | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/cpp/ql/src/experimental/Security/CWE/CWE-1126/DeclarationOfVariableWithUnnecessarilyWideScope.ql b/cpp/ql/src/experimental/Security/CWE/CWE-1126/DeclarationOfVariableWithUnnecessarilyWideScope.ql index be712b1cb1d..e73f36145c6 100644 --- a/cpp/ql/src/experimental/Security/CWE/CWE-1126/DeclarationOfVariableWithUnnecessarilyWideScope.ql +++ b/cpp/ql/src/experimental/Security/CWE/CWE-1126/DeclarationOfVariableWithUnnecessarilyWideScope.ql @@ -27,8 +27,6 @@ class DangerousWhileLoop extends WhileStmt { not exp instanceof PointerFieldAccess and not exp instanceof ValueFieldAccess and exp.(VariableAccess).getTarget().getName() = dl.getName() and - not exp.getParent*() instanceof CrementOperation and - not exp.getParent*() instanceof Assignment and not exp.getParent*() instanceof FunctionCall } @@ -37,10 +35,10 @@ class DangerousWhileLoop extends WhileStmt { /** Holds when there are changes to the variables involved in the condition. */ predicate isUseThisVariable() { exists(Variable v | - exp.(VariableAccess).getTarget() = v and + this.getCondition().getAChild*().(VariableAccess).getTarget() = v and ( exists(Assignment aexp | - aexp = this.getStmt().getAChild*() and + this = aexp.getEnclosingStmt().getParentStmt*() and ( aexp.getLValue().(ArrayExpr).getArrayBase().(VariableAccess).getTarget() = v or @@ -49,7 +47,7 @@ class DangerousWhileLoop extends WhileStmt { ) or exists(CrementOperation crm | - crm = this.getStmt().getAChild*() and + this = crm.getEnclosingStmt().getParentStmt*() and crm.getOperand().(VariableAccess).getTarget() = v ) ) @@ -59,4 +57,4 @@ class DangerousWhileLoop extends WhileStmt { from DangerousWhileLoop lp where not lp.isUseThisVariable() -select lp.getDeclaration(), "A variable with this name is used in the loop condition." +select lp.getDeclaration(), "A variable with this name is used in the $@ condition.", lp, "loop" From d3c6093f37d14861a10cf1ef2134c8bc183340d7 Mon Sep 17 00:00:00 2001 From: ihsinme Date: Mon, 10 May 2021 14:16:38 +0300 Subject: [PATCH 094/168] Update test.c --- .../Security/CWE/CWE-1126/semmle/tests/test.c | 27 +++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/cpp/ql/test/experimental/query-tests/Security/CWE/CWE-1126/semmle/tests/test.c b/cpp/ql/test/experimental/query-tests/Security/CWE/CWE-1126/semmle/tests/test.c index 325c278f697..47d89188e6b 100644 --- a/cpp/ql/test/experimental/query-tests/Security/CWE/CWE-1126/semmle/tests/test.c +++ b/cpp/ql/test/experimental/query-tests/Security/CWE/CWE-1126/semmle/tests/test.c @@ -1,5 +1,6 @@ void workFunction_0(char *s) { int intIndex = 10; + int intGuard; char buf[80]; while(intIndex > 2) // GOOD { @@ -14,6 +15,32 @@ void workFunction_0(char *s) { intIndex--; } intIndex = 10; + intGuard = 20; + while(intIndex < intGuard--) // GOOD + { + buf[intIndex] = 1; + int intIndex; + intIndex--; + } + intIndex = 10; + intGuard = 20; + while(intIndex < intGuard) // GOOD + { + buf[intIndex] = 1; + int intIndex; + intIndex++; + intGuard--; + } + intIndex = 10; + intGuard = 20; + while(intIndex < intGuard) // GOOD + { + buf[intIndex] = 1; + int intIndex; + intIndex--; + intGuard -= 4; + } + intIndex = 10; while(intIndex > 2) // GOOD { buf[intIndex] = 1; From 3e5dc1efb7ea527e8b2d7b8149ce2f5dbc76a2ef Mon Sep 17 00:00:00 2001 From: Asger Feldthaus Date: Mon, 10 May 2021 13:17:25 +0200 Subject: [PATCH 095/168] JS: More robust hasUnderlyingType --- javascript/ql/src/semmle/javascript/TypeScript.qll | 2 +- javascript/ql/src/semmle/javascript/dataflow/DataFlow.qll | 1 - .../TypeScript/HasUnderlyingType/HasUnderlyingType.expected | 4 ++++ .../TypeScript/HasUnderlyingType/HasUnderlyingType.ql | 4 ++++ .../test/library-tests/TypeScript/HasUnderlyingType/foo.ts | 5 +++++ 5 files changed, 14 insertions(+), 2 deletions(-) create mode 100644 javascript/ql/test/library-tests/TypeScript/HasUnderlyingType/foo.ts diff --git a/javascript/ql/src/semmle/javascript/TypeScript.qll b/javascript/ql/src/semmle/javascript/TypeScript.qll index 4fe263c8389..0b611140449 100644 --- a/javascript/ql/src/semmle/javascript/TypeScript.qll +++ b/javascript/ql/src/semmle/javascript/TypeScript.qll @@ -725,7 +725,7 @@ class TypeAccess extends @typeaccess, TypeExpr, TypeRef { spec.getImportedName() = exportedName and this = spec.getLocal().(TypeDecl).getLocalTypeName().getAnAccess() or - spec instanceof ImportNamespaceSpecifier and + (spec instanceof ImportNamespaceSpecifier or spec instanceof ImportDefaultSpecifier) and this = spec.getLocal().(LocalNamespaceDecl).getLocalNamespaceName().getAMemberAccess(exportedName) ) diff --git a/javascript/ql/src/semmle/javascript/dataflow/DataFlow.qll b/javascript/ql/src/semmle/javascript/dataflow/DataFlow.qll index c73c894ca4b..f02e9b0f287 100644 --- a/javascript/ql/src/semmle/javascript/dataflow/DataFlow.qll +++ b/javascript/ql/src/semmle/javascript/dataflow/DataFlow.qll @@ -239,7 +239,6 @@ module DataFlow { private TypeAnnotation getFallbackTypeAnnotation() { exists(BindingPattern pattern | this = valueNode(pattern) and - not ast_node_type(pattern, _) and result = pattern.getTypeAnnotation() ) or diff --git a/javascript/ql/test/library-tests/TypeScript/HasUnderlyingType/HasUnderlyingType.expected b/javascript/ql/test/library-tests/TypeScript/HasUnderlyingType/HasUnderlyingType.expected index 95c6d7e03d9..47e57e0a242 100644 --- a/javascript/ql/test/library-tests/TypeScript/HasUnderlyingType/HasUnderlyingType.expected +++ b/javascript/ql/test/library-tests/TypeScript/HasUnderlyingType/HasUnderlyingType.expected @@ -1,2 +1,6 @@ +underlyingTypeNode +| foo | Bar | foo.ts:3:1:5:1 | use (instance (member Bar (member exports (module foo)))) | +| foo | Bar | foo.ts:3:12:3:12 | use (instance (member Bar (member exports (module foo)))) | +#select | tst.ts:8:14:8:16 | arg | Base in global scope | | tst.ts:8:14:8:16 | arg | Sub in global scope | diff --git a/javascript/ql/test/library-tests/TypeScript/HasUnderlyingType/HasUnderlyingType.ql b/javascript/ql/test/library-tests/TypeScript/HasUnderlyingType/HasUnderlyingType.ql index 77e311b3b76..72d4e6d0f3d 100644 --- a/javascript/ql/test/library-tests/TypeScript/HasUnderlyingType/HasUnderlyingType.ql +++ b/javascript/ql/test/library-tests/TypeScript/HasUnderlyingType/HasUnderlyingType.ql @@ -3,3 +3,7 @@ import javascript from Expr e, TypeName typeName where e.getType().hasUnderlyingTypeName(typeName) select e, typeName + +query API::Node underlyingTypeNode(string mod, string name) { + result = API::Node::ofType(mod, name) +} diff --git a/javascript/ql/test/library-tests/TypeScript/HasUnderlyingType/foo.ts b/javascript/ql/test/library-tests/TypeScript/HasUnderlyingType/foo.ts new file mode 100644 index 00000000000..1b5be79068a --- /dev/null +++ b/javascript/ql/test/library-tests/TypeScript/HasUnderlyingType/foo.ts @@ -0,0 +1,5 @@ +import foo from "foo"; + +function f(x: foo.Bar) { + return x; +} From 9e5a38debd315a8f7ca90c9bcc78d7798b334e5d Mon Sep 17 00:00:00 2001 From: ihsinme Date: Mon, 10 May 2021 14:17:40 +0300 Subject: [PATCH 096/168] Update DeclarationOfVariableWithUnnecessarilyWideScope.expected --- .../DeclarationOfVariableWithUnnecessarilyWideScope.expected | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cpp/ql/test/experimental/query-tests/Security/CWE/CWE-1126/semmle/tests/DeclarationOfVariableWithUnnecessarilyWideScope.expected b/cpp/ql/test/experimental/query-tests/Security/CWE/CWE-1126/semmle/tests/DeclarationOfVariableWithUnnecessarilyWideScope.expected index 4b29dad8779..244a28cf332 100644 --- a/cpp/ql/test/experimental/query-tests/Security/CWE/CWE-1126/semmle/tests/DeclarationOfVariableWithUnnecessarilyWideScope.expected +++ b/cpp/ql/test/experimental/query-tests/Security/CWE/CWE-1126/semmle/tests/DeclarationOfVariableWithUnnecessarilyWideScope.expected @@ -1 +1 @@ -| test.c:13:9:13:16 | intIndex | A variable with this name is used in the loop condition. | +| test.c:14:9:14:16 | intIndex | A variable with this name is used in the $@ condition. | test.c:11:3:16:3 | while (...) ... | loop | From d6f9e37e3943f689891df1360c651c61c0a403e6 Mon Sep 17 00:00:00 2001 From: Erik Krogh Kristensen Date: Mon, 10 May 2021 13:31:00 +0200 Subject: [PATCH 097/168] add printAst.ql support for regular expressions --- .../ql/src/semmle/javascript/PrintAst.qll | 36 +++- .../ql/src/semmle/javascript/Regexp.qll | 100 +++++++++-- .../library-tests/TypeScript/Types/dummy.ts | 2 + .../TypeScript/Types/printAst.expected | 163 +++++++++++------- .../TypeScript/Types/tests.expected | 2 + 5 files changed, 219 insertions(+), 84 deletions(-) diff --git a/javascript/ql/src/semmle/javascript/PrintAst.qll b/javascript/ql/src/semmle/javascript/PrintAst.qll index 0499e3bd547..99f06354d74 100644 --- a/javascript/ql/src/semmle/javascript/PrintAst.qll +++ b/javascript/ql/src/semmle/javascript/PrintAst.qll @@ -73,7 +73,8 @@ private newtype TPrintAstNode = THTMLAttributesNodes(HTML::Element e) { shouldPrint(e, _) and not isNotNeeded(e) } or THTMLAttributeNode(HTML::Attribute attr) { shouldPrint(attr, _) and not isNotNeeded(attr) } or THTMLScript(Script script) { shouldPrint(script, _) and not isNotNeeded(script) } or - THTMLCodeInAttr(CodeInAttribute attr) { shouldPrint(attr, _) and not isNotNeeded(attr) } + THTMLCodeInAttr(CodeInAttribute attr) { shouldPrint(attr, _) and not isNotNeeded(attr) } or + TRegExpTermNode(RegExpTerm term) { term.isUsedAsRegExp() } /** * A node in the output tree. @@ -282,6 +283,39 @@ private module PrintJavaScript { } } + /** + * A print node for regexp literals. + * + * The single child of this node is the root `RegExpTerm`. + */ + class RegexpNode extends ElementNode { + override RegExpLiteral element; + + override PrintAstNode getChild(int childIndex) { + childIndex = 0 and + result.(RegExpTermNode).getTerm() = element.getRoot() + } + } + + /** + * A print node for regexp terms. + */ + class RegExpTermNode extends PrintAstNode, TRegExpTermNode { + RegExpTerm term; + + RegExpTermNode() { this = TRegExpTermNode(term) } + + RegExpTerm getTerm() { result = term } + + override PrintAstNode getChild(int childIndex) { + result.(RegExpTermNode).getTerm() = term.getChild(childIndex) + } + + override string toString() { result = getQlClass(term) + term.toString() } + + override Location getLocation() { result = term.getLocation() } + } + /** * An aggregate node representing all the arguments for an function invocation. */ diff --git a/javascript/ql/src/semmle/javascript/Regexp.qll b/javascript/ql/src/semmle/javascript/Regexp.qll index a34b052005d..d3b7e9cac7e 100644 --- a/javascript/ql/src/semmle/javascript/Regexp.qll +++ b/javascript/ql/src/semmle/javascript/Regexp.qll @@ -215,7 +215,9 @@ class InfiniteRepetitionQuantifier extends RegExpQuantifier { * \w * ``` */ -class RegExpEscape extends RegExpTerm, @regexp_escape { } +class RegExpEscape extends RegExpTerm, @regexp_escape { + override string getAPrimaryQlClass() { result = "RegExpEscape" } +} /** * A constant regular expression term, that is, a regular expression @@ -240,6 +242,8 @@ class RegExpConstant extends RegExpTerm, @regexp_constant { override predicate isNullable() { none() } override string getConstantValue() { result = getValue() } + + override string getAPrimaryQlClass() { result = "RegExpConstant" } } /** @@ -264,6 +268,8 @@ class RegExpCharEscape extends RegExpEscape, RegExpConstant, @regexp_char_escape ) ) } + + override string getAPrimaryQlClass() { result = "RegExpCharEscape" } } /** @@ -285,6 +291,8 @@ class RegExpAlt extends RegExpTerm, @regexp_alt { override predicate isNullable() { getAlternative().isNullable() } override string getAMatchedString() { result = getAlternative().getAMatchedString() } + + override string getAPrimaryQlClass() { result = "RegExpAlt" } } /** @@ -332,6 +340,8 @@ class RegExpSequence extends RegExpTerm, @regexp_seq { result = this.getChild(i + 1) ) } + + override string getAPrimaryQlClass() { result = "RegExpSequence" } } /** @@ -346,6 +356,8 @@ class RegExpSequence extends RegExpTerm, @regexp_seq { */ class RegExpAnchor extends RegExpTerm, @regexp_anchor { override predicate isNullable() { any() } + + override string getAPrimaryQlClass() { result = "RegExpAnchor" } } /** @@ -357,7 +369,9 @@ class RegExpAnchor extends RegExpTerm, @regexp_anchor { * ^ * ``` */ -class RegExpCaret extends RegExpAnchor, @regexp_caret { } +class RegExpCaret extends RegExpAnchor, @regexp_caret { + override string getAPrimaryQlClass() { result = "RegExpCaret" } +} /** * A dollar assertion `$` matching the end of a line. @@ -368,7 +382,9 @@ class RegExpCaret extends RegExpAnchor, @regexp_caret { } * $ * ``` */ -class RegExpDollar extends RegExpAnchor, @regexp_dollar { } +class RegExpDollar extends RegExpAnchor, @regexp_dollar { + override string getAPrimaryQlClass() { result = "RegExpDollar" } +} /** * A word boundary assertion. @@ -381,6 +397,8 @@ class RegExpDollar extends RegExpAnchor, @regexp_dollar { } */ class RegExpWordBoundary extends RegExpTerm, @regexp_wordboundary { override predicate isNullable() { any() } + + override string getAPrimaryQlClass() { result = "RegExpWordBoundary" } } /** @@ -394,6 +412,8 @@ class RegExpWordBoundary extends RegExpTerm, @regexp_wordboundary { */ class RegExpNonWordBoundary extends RegExpTerm, @regexp_nonwordboundary { override predicate isNullable() { any() } + + override string getAPrimaryQlClass() { result = "RegExpNonWordBoundary" } } /** @@ -425,7 +445,9 @@ class RegExpSubPattern extends RegExpTerm, @regexp_subpattern { * (?!\n) * ``` */ -class RegExpLookahead extends RegExpSubPattern, @regexp_lookahead { } +class RegExpLookahead extends RegExpSubPattern, @regexp_lookahead { + override string getAPrimaryQlClass() { result = "RegExpLookahead" } +} /** * A zero-width lookbehind assertion. @@ -437,7 +459,9 @@ class RegExpLookahead extends RegExpSubPattern, @regexp_lookahead { } * (?` @@ -770,6 +832,8 @@ class RegExpBackRef extends RegExpTerm, @regexp_backref { } override predicate isNullable() { getGroup().isNullable() } + + override string getAPrimaryQlClass() { result = "RegExpBackRef" } } /** @@ -808,6 +872,8 @@ class RegExpCharacterClass extends RegExpTerm, @regexp_char_class { cce1 != cce2 and cce1.toLowerCase() = cce2.toLowerCase() ) } + + override string getAPrimaryQlClass() { result = "RegExpCharacterClass" } } /** @@ -827,6 +893,8 @@ class RegExpCharacterRange extends RegExpTerm, @regexp_char_range { lo = getChild(0).(RegExpConstant).getValue() and hi = getChild(1).(RegExpConstant).getValue() } + + override string getAPrimaryQlClass() { result = "RegExpCharacterRange" } } /** A parse error encountered while processing a regular expression literal. */ diff --git a/javascript/ql/test/library-tests/TypeScript/Types/dummy.ts b/javascript/ql/test/library-tests/TypeScript/Types/dummy.ts index 77f17538e18..d36be434986 100644 --- a/javascript/ql/test/library-tests/TypeScript/Types/dummy.ts +++ b/javascript/ql/test/library-tests/TypeScript/Types/dummy.ts @@ -1,2 +1,4 @@ // Dummy file to be imported so the other files are seen as modules. export let x = 5; + +export let reg = /ab+c/; \ No newline at end of file diff --git a/javascript/ql/test/library-tests/TypeScript/Types/printAst.expected b/javascript/ql/test/library-tests/TypeScript/Types/printAst.expected index bd50eaa28ec..1f39b308479 100644 --- a/javascript/ql/test/library-tests/TypeScript/Types/printAst.expected +++ b/javascript/ql/test/library-tests/TypeScript/Types/printAst.expected @@ -74,6 +74,17 @@ nodes | dummy.ts:2:12:2:12 | [VarDecl] x | semmle.label | [VarDecl] x | | dummy.ts:2:12:2:16 | [VariableDeclarator] x = 5 | semmle.label | [VariableDeclarator] x = 5 | | dummy.ts:2:16:2:16 | [Literal] 5 | semmle.label | [Literal] 5 | +| dummy.ts:4:1:4:24 | [ExportDeclaration] export ... /ab+c/; | semmle.label | [ExportDeclaration] export ... /ab+c/; | +| dummy.ts:4:1:4:24 | [ExportDeclaration] export ... /ab+c/; | semmle.order | 13 | +| dummy.ts:4:8:4:24 | [DeclStmt] let reg = ... | semmle.label | [DeclStmt] let reg = ... | +| dummy.ts:4:12:4:14 | [VarDecl] reg | semmle.label | [VarDecl] reg | +| dummy.ts:4:12:4:23 | [VariableDeclarator] reg = /ab+c/ | semmle.label | [VariableDeclarator] reg = /ab+c/ | +| dummy.ts:4:18:4:23 | [RegExpLiteral] /ab+c/ | semmle.label | [RegExpLiteral] /ab+c/ | +| dummy.ts:4:19:4:19 | [RegExpNormalConstant] a | semmle.label | [RegExpNormalConstant] a | +| dummy.ts:4:19:4:22 | [RegExpSequence] ab+c | semmle.label | [RegExpSequence] ab+c | +| dummy.ts:4:20:4:20 | [RegExpNormalConstant] b | semmle.label | [RegExpNormalConstant] b | +| dummy.ts:4:20:4:21 | [RegExpPlus] b+ | semmle.label | [RegExpPlus] b+ | +| dummy.ts:4:22:4:22 | [RegExpNormalConstant] c | semmle.label | [RegExpNormalConstant] c | | file://:0:0:0:0 | (Parameters) | semmle.label | (Parameters) | | file://:0:0:0:0 | (Parameters) | semmle.label | (Parameters) | | file://:0:0:0:0 | (Parameters) | semmle.label | (Parameters) | @@ -85,7 +96,7 @@ nodes | file://:0:0:0:0 | (TypeParameters) | semmle.label | (TypeParameters) | | file://:0:0:0:0 | (TypeParameters) | semmle.label | (TypeParameters) | | middle-rest.ts:1:1:1:40 | [DeclStmt] let foo = ... | semmle.label | [DeclStmt] let foo = ... | -| middle-rest.ts:1:1:1:40 | [DeclStmt] let foo = ... | semmle.order | 13 | +| middle-rest.ts:1:1:1:40 | [DeclStmt] let foo = ... | semmle.order | 14 | | middle-rest.ts:1:5:1:7 | [VarDecl] foo | semmle.label | [VarDecl] foo | | middle-rest.ts:1:5:1:39 | [VariableDeclarator] foo: [b ... number] | semmle.label | [VariableDeclarator] foo: [b ... number] | | middle-rest.ts:1:10:1:39 | [TupleTypeExpr] [boolea ... number] | semmle.label | [TupleTypeExpr] [boolea ... number] | @@ -97,55 +108,55 @@ nodes | middle-rest.ts:3:1:3:3 | [VarRef] foo | semmle.label | [VarRef] foo | | middle-rest.ts:3:1:3:26 | [AssignExpr] foo = [ ... ", 123] | semmle.label | [AssignExpr] foo = [ ... ", 123] | | middle-rest.ts:3:1:3:27 | [ExprStmt] foo = [ ... , 123]; | semmle.label | [ExprStmt] foo = [ ... , 123]; | -| middle-rest.ts:3:1:3:27 | [ExprStmt] foo = [ ... , 123]; | semmle.order | 14 | +| middle-rest.ts:3:1:3:27 | [ExprStmt] foo = [ ... , 123]; | semmle.order | 15 | | middle-rest.ts:3:7:3:26 | [ArrayExpr] [true, "hello", 123] | semmle.label | [ArrayExpr] [true, "hello", 123] | | middle-rest.ts:3:8:3:11 | [Literal] true | semmle.label | [Literal] true | | middle-rest.ts:3:14:3:20 | [Literal] "hello" | semmle.label | [Literal] "hello" | | middle-rest.ts:3:23:3:25 | [Literal] 123 | semmle.label | [Literal] 123 | | tst.ts:1:1:1:33 | [ImportDeclaration] import ... dummy"; | semmle.label | [ImportDeclaration] import ... dummy"; | -| tst.ts:1:1:1:33 | [ImportDeclaration] import ... dummy"; | semmle.order | 15 | +| tst.ts:1:1:1:33 | [ImportDeclaration] import ... dummy"; | semmle.order | 16 | | tst.ts:1:8:1:17 | [ImportSpecifier] * as dummy | semmle.label | [ImportSpecifier] * as dummy | | tst.ts:1:13:1:17 | [VarDecl] dummy | semmle.label | [VarDecl] dummy | | tst.ts:1:24:1:32 | [Literal] "./dummy" | semmle.label | [Literal] "./dummy" | | tst.ts:3:1:3:19 | [DeclStmt] var numVar = ... | semmle.label | [DeclStmt] var numVar = ... | -| tst.ts:3:1:3:19 | [DeclStmt] var numVar = ... | semmle.order | 16 | +| tst.ts:3:1:3:19 | [DeclStmt] var numVar = ... | semmle.order | 17 | | tst.ts:3:5:3:10 | [VarDecl] numVar | semmle.label | [VarDecl] numVar | | tst.ts:3:5:3:18 | [VariableDeclarator] numVar: number | semmle.label | [VariableDeclarator] numVar: number | | tst.ts:3:13:3:18 | [KeywordTypeExpr] number | semmle.label | [KeywordTypeExpr] number | | tst.ts:5:1:5:18 | [DeclStmt] var num1 = ... | semmle.label | [DeclStmt] var num1 = ... | -| tst.ts:5:1:5:18 | [DeclStmt] var num1 = ... | semmle.order | 17 | +| tst.ts:5:1:5:18 | [DeclStmt] var num1 = ... | semmle.order | 18 | | tst.ts:5:5:5:8 | [VarDecl] num1 | semmle.label | [VarDecl] num1 | | tst.ts:5:5:5:17 | [VariableDeclarator] num1 = numVar | semmle.label | [VariableDeclarator] num1 = numVar | | tst.ts:5:12:5:17 | [VarRef] numVar | semmle.label | [VarRef] numVar | | tst.ts:6:1:6:13 | [DeclStmt] var num2 = ... | semmle.label | [DeclStmt] var num2 = ... | -| tst.ts:6:1:6:13 | [DeclStmt] var num2 = ... | semmle.order | 18 | +| tst.ts:6:1:6:13 | [DeclStmt] var num2 = ... | semmle.order | 19 | | tst.ts:6:5:6:8 | [VarDecl] num2 | semmle.label | [VarDecl] num2 | | tst.ts:6:5:6:12 | [VariableDeclarator] num2 = 5 | semmle.label | [VariableDeclarator] num2 = 5 | | tst.ts:6:12:6:12 | [Literal] 5 | semmle.label | [Literal] 5 | | tst.ts:7:1:7:23 | [DeclStmt] var num3 = ... | semmle.label | [DeclStmt] var num3 = ... | -| tst.ts:7:1:7:23 | [DeclStmt] var num3 = ... | semmle.order | 19 | +| tst.ts:7:1:7:23 | [DeclStmt] var num3 = ... | semmle.order | 20 | | tst.ts:7:5:7:8 | [VarDecl] num3 | semmle.label | [VarDecl] num3 | | tst.ts:7:5:7:22 | [VariableDeclarator] num3 = num1 + num2 | semmle.label | [VariableDeclarator] num3 = num1 + num2 | | tst.ts:7:12:7:15 | [VarRef] num1 | semmle.label | [VarRef] num1 | | tst.ts:7:12:7:22 | [BinaryExpr] num1 + num2 | semmle.label | [BinaryExpr] num1 + num2 | | tst.ts:7:19:7:22 | [VarRef] num2 | semmle.label | [VarRef] num2 | | tst.ts:9:1:9:19 | [DeclStmt] var strVar = ... | semmle.label | [DeclStmt] var strVar = ... | -| tst.ts:9:1:9:19 | [DeclStmt] var strVar = ... | semmle.order | 20 | +| tst.ts:9:1:9:19 | [DeclStmt] var strVar = ... | semmle.order | 21 | | tst.ts:9:5:9:10 | [VarDecl] strVar | semmle.label | [VarDecl] strVar | | tst.ts:9:5:9:18 | [VariableDeclarator] strVar: string | semmle.label | [VariableDeclarator] strVar: string | | tst.ts:9:13:9:18 | [KeywordTypeExpr] string | semmle.label | [KeywordTypeExpr] string | | tst.ts:10:1:10:20 | [DeclStmt] var hello = ... | semmle.label | [DeclStmt] var hello = ... | -| tst.ts:10:1:10:20 | [DeclStmt] var hello = ... | semmle.order | 21 | +| tst.ts:10:1:10:20 | [DeclStmt] var hello = ... | semmle.order | 22 | | tst.ts:10:5:10:9 | [VarDecl] hello | semmle.label | [VarDecl] hello | | tst.ts:10:5:10:19 | [VariableDeclarator] hello = "hello" | semmle.label | [VariableDeclarator] hello = "hello" | | tst.ts:10:13:10:19 | [Literal] "hello" | semmle.label | [Literal] "hello" | | tst.ts:11:1:11:20 | [DeclStmt] var world = ... | semmle.label | [DeclStmt] var world = ... | -| tst.ts:11:1:11:20 | [DeclStmt] var world = ... | semmle.order | 22 | +| tst.ts:11:1:11:20 | [DeclStmt] var world = ... | semmle.order | 23 | | tst.ts:11:5:11:9 | [VarDecl] world | semmle.label | [VarDecl] world | | tst.ts:11:5:11:19 | [VariableDeclarator] world = "world" | semmle.label | [VariableDeclarator] world = "world" | | tst.ts:11:13:11:19 | [Literal] "world" | semmle.label | [Literal] "world" | | tst.ts:12:1:12:30 | [DeclStmt] var msg = ... | semmle.label | [DeclStmt] var msg = ... | -| tst.ts:12:1:12:30 | [DeclStmt] var msg = ... | semmle.order | 23 | +| tst.ts:12:1:12:30 | [DeclStmt] var msg = ... | semmle.order | 24 | | tst.ts:12:5:12:7 | [VarDecl] msg | semmle.label | [VarDecl] msg | | tst.ts:12:5:12:29 | [VariableDeclarator] msg = h ... + world | semmle.label | [VariableDeclarator] msg = h ... + world | | tst.ts:12:11:12:15 | [VarRef] hello | semmle.label | [VarRef] hello | @@ -154,7 +165,7 @@ nodes | tst.ts:12:19:12:21 | [Literal] " " | semmle.label | [Literal] " " | | tst.ts:12:25:12:29 | [VarRef] world | semmle.label | [VarRef] world | | tst.ts:14:1:14:63 | [FunctionDeclStmt] functio ... + y; } | semmle.label | [FunctionDeclStmt] functio ... + y; } | -| tst.ts:14:1:14:63 | [FunctionDeclStmt] functio ... + y; } | semmle.order | 24 | +| tst.ts:14:1:14:63 | [FunctionDeclStmt] functio ... + y; } | semmle.order | 25 | | tst.ts:14:10:14:15 | [VarDecl] concat | semmle.label | [VarDecl] concat | | tst.ts:14:17:14:17 | [SimpleParameter] x | semmle.label | [SimpleParameter] x | | tst.ts:14:20:14:25 | [KeywordTypeExpr] string | semmle.label | [KeywordTypeExpr] string | @@ -167,7 +178,7 @@ nodes | tst.ts:14:56:14:60 | [BinaryExpr] x + y | semmle.label | [BinaryExpr] x + y | | tst.ts:14:60:14:60 | [VarRef] y | semmle.label | [VarRef] y | | tst.ts:16:1:16:60 | [FunctionDeclStmt] functio ... + y; } | semmle.label | [FunctionDeclStmt] functio ... + y; } | -| tst.ts:16:1:16:60 | [FunctionDeclStmt] functio ... + y; } | semmle.order | 25 | +| tst.ts:16:1:16:60 | [FunctionDeclStmt] functio ... + y; } | semmle.order | 26 | | tst.ts:16:10:16:12 | [VarDecl] add | semmle.label | [VarDecl] add | | tst.ts:16:14:16:14 | [SimpleParameter] x | semmle.label | [SimpleParameter] x | | tst.ts:16:17:16:22 | [KeywordTypeExpr] number | semmle.label | [KeywordTypeExpr] number | @@ -180,7 +191,7 @@ nodes | tst.ts:16:53:16:57 | [BinaryExpr] x + y | semmle.label | [BinaryExpr] x + y | | tst.ts:16:57:16:57 | [VarRef] y | semmle.label | [VarRef] y | | tst.ts:18:1:18:40 | [FunctionDeclStmt] functio ... + y; } | semmle.label | [FunctionDeclStmt] functio ... + y; } | -| tst.ts:18:1:18:40 | [FunctionDeclStmt] functio ... + y; } | semmle.order | 26 | +| tst.ts:18:1:18:40 | [FunctionDeclStmt] functio ... + y; } | semmle.order | 27 | | tst.ts:18:10:18:16 | [VarDecl] untyped | semmle.label | [VarDecl] untyped | | tst.ts:18:18:18:18 | [SimpleParameter] x | semmle.label | [SimpleParameter] x | | tst.ts:18:21:18:21 | [SimpleParameter] y | semmle.label | [SimpleParameter] y | @@ -190,7 +201,7 @@ nodes | tst.ts:18:33:18:37 | [BinaryExpr] x + y | semmle.label | [BinaryExpr] x + y | | tst.ts:18:37:18:37 | [VarRef] y | semmle.label | [VarRef] y | | tst.ts:20:1:20:53 | [FunctionDeclStmt] functio ... + y; } | semmle.label | [FunctionDeclStmt] functio ... + y; } | -| tst.ts:20:1:20:53 | [FunctionDeclStmt] functio ... + y; } | semmle.order | 27 | +| tst.ts:20:1:20:53 | [FunctionDeclStmt] functio ... + y; } | semmle.order | 28 | | tst.ts:20:10:20:21 | [VarDecl] partialTyped | semmle.label | [VarDecl] partialTyped | | tst.ts:20:23:20:23 | [SimpleParameter] x | semmle.label | [SimpleParameter] x | | tst.ts:20:26:20:26 | [SimpleParameter] y | semmle.label | [SimpleParameter] y | @@ -201,7 +212,7 @@ nodes | tst.ts:20:46:20:50 | [BinaryExpr] x + y | semmle.label | [BinaryExpr] x + y | | tst.ts:20:50:20:50 | [VarRef] y | semmle.label | [VarRef] y | | tst.ts:22:1:22:34 | [ForOfStmt] for (le ... 2]) {} | semmle.label | [ForOfStmt] for (le ... 2]) {} | -| tst.ts:22:1:22:34 | [ForOfStmt] for (le ... 2]) {} | semmle.order | 28 | +| tst.ts:22:1:22:34 | [ForOfStmt] for (le ... 2]) {} | semmle.order | 29 | | tst.ts:22:6:22:20 | [DeclStmt] let numFromLoop = ... | semmle.label | [DeclStmt] let numFromLoop = ... | | tst.ts:22:10:22:20 | [VarDecl] numFromLoop | semmle.label | [VarDecl] numFromLoop | | tst.ts:22:10:22:20 | [VariableDeclarator] numFromLoop | semmle.label | [VariableDeclarator] numFromLoop | @@ -210,54 +221,54 @@ nodes | tst.ts:22:29:22:29 | [Literal] 2 | semmle.label | [Literal] 2 | | tst.ts:22:33:22:34 | [BlockStmt] {} | semmle.label | [BlockStmt] {} | | tst.ts:24:1:24:20 | [DeclStmt] let array = ... | semmle.label | [DeclStmt] let array = ... | -| tst.ts:24:1:24:20 | [DeclStmt] let array = ... | semmle.order | 29 | +| tst.ts:24:1:24:20 | [DeclStmt] let array = ... | semmle.order | 30 | | tst.ts:24:5:24:9 | [VarDecl] array | semmle.label | [VarDecl] array | | tst.ts:24:5:24:19 | [VariableDeclarator] array: number[] | semmle.label | [VariableDeclarator] array: number[] | | tst.ts:24:12:24:17 | [KeywordTypeExpr] number | semmle.label | [KeywordTypeExpr] number | | tst.ts:24:12:24:19 | [ArrayTypeExpr] number[] | semmle.label | [ArrayTypeExpr] number[] | | tst.ts:26:1:26:25 | [DeclStmt] let voidType = ... | semmle.label | [DeclStmt] let voidType = ... | -| tst.ts:26:1:26:25 | [DeclStmt] let voidType = ... | semmle.order | 30 | +| tst.ts:26:1:26:25 | [DeclStmt] let voidType = ... | semmle.order | 31 | | tst.ts:26:5:26:12 | [VarDecl] voidType | semmle.label | [VarDecl] voidType | | tst.ts:26:5:26:24 | [VariableDeclarator] voidType: () => void | semmle.label | [VariableDeclarator] voidType: () => void | | tst.ts:26:15:26:24 | [FunctionExpr] () => void | semmle.label | [FunctionExpr] () => void | | tst.ts:26:15:26:24 | [FunctionTypeExpr] () => void | semmle.label | [FunctionTypeExpr] () => void | | tst.ts:26:21:26:24 | [KeywordTypeExpr] void | semmle.label | [KeywordTypeExpr] void | | tst.ts:27:1:27:29 | [DeclStmt] let undefinedType = ... | semmle.label | [DeclStmt] let undefinedType = ... | -| tst.ts:27:1:27:29 | [DeclStmt] let undefinedType = ... | semmle.order | 31 | +| tst.ts:27:1:27:29 | [DeclStmt] let undefinedType = ... | semmle.order | 32 | | tst.ts:27:5:27:17 | [VarDecl] undefinedType | semmle.label | [VarDecl] undefinedType | | tst.ts:27:5:27:28 | [VariableDeclarator] undefin ... defined | semmle.label | [VariableDeclarator] undefin ... defined | | tst.ts:27:20:27:28 | [KeywordTypeExpr] undefined | semmle.label | [KeywordTypeExpr] undefined | | tst.ts:28:1:28:26 | [DeclStmt] let nullType = ... | semmle.label | [DeclStmt] let nullType = ... | -| tst.ts:28:1:28:26 | [DeclStmt] let nullType = ... | semmle.order | 32 | +| tst.ts:28:1:28:26 | [DeclStmt] let nullType = ... | semmle.order | 33 | | tst.ts:28:5:28:12 | [VarDecl] nullType | semmle.label | [VarDecl] nullType | | tst.ts:28:5:28:25 | [VariableDeclarator] nullTyp ... = null | semmle.label | [VariableDeclarator] nullTyp ... = null | | tst.ts:28:15:28:18 | [KeywordTypeExpr] null | semmle.label | [KeywordTypeExpr] null | | tst.ts:28:22:28:25 | [Literal] null | semmle.label | [Literal] null | | tst.ts:29:1:29:27 | [DeclStmt] let neverType = ... | semmle.label | [DeclStmt] let neverType = ... | -| tst.ts:29:1:29:27 | [DeclStmt] let neverType = ... | semmle.order | 33 | +| tst.ts:29:1:29:27 | [DeclStmt] let neverType = ... | semmle.order | 34 | | tst.ts:29:5:29:13 | [VarDecl] neverType | semmle.label | [VarDecl] neverType | | tst.ts:29:5:29:26 | [VariableDeclarator] neverTy ... > never | semmle.label | [VariableDeclarator] neverTy ... > never | | tst.ts:29:16:29:26 | [FunctionExpr] () => never | semmle.label | [FunctionExpr] () => never | | tst.ts:29:16:29:26 | [FunctionTypeExpr] () => never | semmle.label | [FunctionTypeExpr] () => never | | tst.ts:29:22:29:26 | [KeywordTypeExpr] never | semmle.label | [KeywordTypeExpr] never | | tst.ts:30:1:30:23 | [DeclStmt] let symbolType = ... | semmle.label | [DeclStmt] let symbolType = ... | -| tst.ts:30:1:30:23 | [DeclStmt] let symbolType = ... | semmle.order | 34 | +| tst.ts:30:1:30:23 | [DeclStmt] let symbolType = ... | semmle.order | 35 | | tst.ts:30:5:30:14 | [VarDecl] symbolType | semmle.label | [VarDecl] symbolType | | tst.ts:30:5:30:22 | [VariableDeclarator] symbolType: symbol | semmle.label | [VariableDeclarator] symbolType: symbol | | tst.ts:30:17:30:22 | [KeywordTypeExpr] symbol | semmle.label | [KeywordTypeExpr] symbol | | tst.ts:31:1:31:45 | [DeclStmt] const uniqueSymbolType = ... | semmle.label | [DeclStmt] const uniqueSymbolType = ... | -| tst.ts:31:1:31:45 | [DeclStmt] const uniqueSymbolType = ... | semmle.order | 35 | +| tst.ts:31:1:31:45 | [DeclStmt] const uniqueSymbolType = ... | semmle.order | 36 | | tst.ts:31:7:31:22 | [VarDecl] uniqueSymbolType | semmle.label | [VarDecl] uniqueSymbolType | | tst.ts:31:7:31:44 | [VariableDeclarator] uniqueS ... = null | semmle.label | [VariableDeclarator] uniqueS ... = null | | tst.ts:31:25:31:37 | [KeywordTypeExpr] unique symbol | semmle.label | [KeywordTypeExpr] unique symbol | | tst.ts:31:41:31:44 | [Literal] null | semmle.label | [Literal] null | | tst.ts:32:1:32:23 | [DeclStmt] let objectType = ... | semmle.label | [DeclStmt] let objectType = ... | -| tst.ts:32:1:32:23 | [DeclStmt] let objectType = ... | semmle.order | 36 | +| tst.ts:32:1:32:23 | [DeclStmt] let objectType = ... | semmle.order | 37 | | tst.ts:32:5:32:14 | [VarDecl] objectType | semmle.label | [VarDecl] objectType | | tst.ts:32:5:32:22 | [VariableDeclarator] objectType: object | semmle.label | [VariableDeclarator] objectType: object | | tst.ts:32:17:32:22 | [KeywordTypeExpr] object | semmle.label | [KeywordTypeExpr] object | | tst.ts:33:1:33:39 | [DeclStmt] let intersection = ... | semmle.label | [DeclStmt] let intersection = ... | -| tst.ts:33:1:33:39 | [DeclStmt] let intersection = ... | semmle.order | 37 | +| tst.ts:33:1:33:39 | [DeclStmt] let intersection = ... | semmle.order | 38 | | tst.ts:33:5:33:16 | [VarDecl] intersection | semmle.label | [VarDecl] intersection | | tst.ts:33:5:33:38 | [VariableDeclarator] interse ... string} | semmle.label | [VariableDeclarator] interse ... string} | | tst.ts:33:19:33:24 | [KeywordTypeExpr] string | semmle.label | [KeywordTypeExpr] string | @@ -267,14 +278,14 @@ nodes | tst.ts:33:29:33:37 | [FieldDeclaration] x: string | semmle.label | [FieldDeclaration] x: string | | tst.ts:33:32:33:37 | [KeywordTypeExpr] string | semmle.label | [KeywordTypeExpr] string | | tst.ts:34:1:34:28 | [DeclStmt] let tuple = ... | semmle.label | [DeclStmt] let tuple = ... | -| tst.ts:34:1:34:28 | [DeclStmt] let tuple = ... | semmle.order | 38 | +| tst.ts:34:1:34:28 | [DeclStmt] let tuple = ... | semmle.order | 39 | | tst.ts:34:5:34:9 | [VarDecl] tuple | semmle.label | [VarDecl] tuple | | tst.ts:34:5:34:27 | [VariableDeclarator] tuple: ... string] | semmle.label | [VariableDeclarator] tuple: ... string] | | tst.ts:34:12:34:27 | [TupleTypeExpr] [number, string] | semmle.label | [TupleTypeExpr] [number, string] | | tst.ts:34:13:34:18 | [KeywordTypeExpr] number | semmle.label | [KeywordTypeExpr] number | | tst.ts:34:21:34:26 | [KeywordTypeExpr] string | semmle.label | [KeywordTypeExpr] string | | tst.ts:36:1:36:56 | [DeclStmt] let tupleWithOptionalElement = ... | semmle.label | [DeclStmt] let tupleWithOptionalElement = ... | -| tst.ts:36:1:36:56 | [DeclStmt] let tupleWithOptionalElement = ... | semmle.order | 39 | +| tst.ts:36:1:36:56 | [DeclStmt] let tupleWithOptionalElement = ... | semmle.order | 40 | | tst.ts:36:5:36:28 | [VarDecl] tupleWithOptionalElement | semmle.label | [VarDecl] tupleWithOptionalElement | | tst.ts:36:5:36:55 | [VariableDeclarator] tupleWi ... umber?] | semmle.label | [VariableDeclarator] tupleWi ... umber?] | | tst.ts:36:31:36:55 | [TupleTypeExpr] [number ... umber?] | semmle.label | [TupleTypeExpr] [number ... umber?] | @@ -283,12 +294,12 @@ nodes | tst.ts:36:48:36:53 | [KeywordTypeExpr] number | semmle.label | [KeywordTypeExpr] number | | tst.ts:36:48:36:54 | [OptionalTypeExpr] number? | semmle.label | [OptionalTypeExpr] number? | | tst.ts:37:1:37:19 | [DeclStmt] let emptyTuple = ... | semmle.label | [DeclStmt] let emptyTuple = ... | -| tst.ts:37:1:37:19 | [DeclStmt] let emptyTuple = ... | semmle.order | 40 | +| tst.ts:37:1:37:19 | [DeclStmt] let emptyTuple = ... | semmle.order | 41 | | tst.ts:37:5:37:14 | [VarDecl] emptyTuple | semmle.label | [VarDecl] emptyTuple | | tst.ts:37:5:37:18 | [VariableDeclarator] emptyTuple: [] | semmle.label | [VariableDeclarator] emptyTuple: [] | | tst.ts:37:17:37:18 | [TupleTypeExpr] [] | semmle.label | [TupleTypeExpr] [] | | tst.ts:38:1:38:48 | [DeclStmt] let tupleWithRestElement = ... | semmle.label | [DeclStmt] let tupleWithRestElement = ... | -| tst.ts:38:1:38:48 | [DeclStmt] let tupleWithRestElement = ... | semmle.order | 41 | +| tst.ts:38:1:38:48 | [DeclStmt] let tupleWithRestElement = ... | semmle.order | 42 | | tst.ts:38:5:38:24 | [VarDecl] tupleWithRestElement | semmle.label | [VarDecl] tupleWithRestElement | | tst.ts:38:5:38:47 | [VariableDeclarator] tupleWi ... ring[]] | semmle.label | [VariableDeclarator] tupleWi ... ring[]] | | tst.ts:38:27:38:47 | [TupleTypeExpr] [number ... ring[]] | semmle.label | [TupleTypeExpr] [number ... ring[]] | @@ -297,7 +308,7 @@ nodes | tst.ts:38:39:38:44 | [KeywordTypeExpr] string | semmle.label | [KeywordTypeExpr] string | | tst.ts:38:39:38:46 | [ArrayTypeExpr] string[] | semmle.label | [ArrayTypeExpr] string[] | | tst.ts:39:1:39:69 | [DeclStmt] let tupleWithOptionalAndRestElements = ... | semmle.label | [DeclStmt] let tupleWithOptionalAndRestElements = ... | -| tst.ts:39:1:39:69 | [DeclStmt] let tupleWithOptionalAndRestElements = ... | semmle.order | 42 | +| tst.ts:39:1:39:69 | [DeclStmt] let tupleWithOptionalAndRestElements = ... | semmle.order | 43 | | tst.ts:39:5:39:36 | [VarDecl] tupleWithOptionalAndRestElements | semmle.label | [VarDecl] tupleWithOptionalAndRestElements | | tst.ts:39:5:39:68 | [VariableDeclarator] tupleWi ... mber[]] | semmle.label | [VariableDeclarator] tupleWi ... mber[]] | | tst.ts:39:39:39:68 | [TupleTypeExpr] [number ... mber[]] | semmle.label | [TupleTypeExpr] [number ... mber[]] | @@ -308,12 +319,12 @@ nodes | tst.ts:39:60:39:65 | [KeywordTypeExpr] number | semmle.label | [KeywordTypeExpr] number | | tst.ts:39:60:39:67 | [ArrayTypeExpr] number[] | semmle.label | [ArrayTypeExpr] number[] | | tst.ts:40:1:40:25 | [DeclStmt] let unknownType = ... | semmle.label | [DeclStmt] let unknownType = ... | -| tst.ts:40:1:40:25 | [DeclStmt] let unknownType = ... | semmle.order | 43 | +| tst.ts:40:1:40:25 | [DeclStmt] let unknownType = ... | semmle.order | 44 | | tst.ts:40:5:40:15 | [VarDecl] unknownType | semmle.label | [VarDecl] unknownType | | tst.ts:40:5:40:24 | [VariableDeclarator] unknownType: unknown | semmle.label | [VariableDeclarator] unknownType: unknown | | tst.ts:40:18:40:24 | [KeywordTypeExpr] unknown | semmle.label | [KeywordTypeExpr] unknown | | tst.ts:42:1:42:40 | [DeclStmt] let constArrayLiteral = ... | semmle.label | [DeclStmt] let constArrayLiteral = ... | -| tst.ts:42:1:42:40 | [DeclStmt] let constArrayLiteral = ... | semmle.order | 44 | +| tst.ts:42:1:42:40 | [DeclStmt] let constArrayLiteral = ... | semmle.order | 45 | | tst.ts:42:5:42:21 | [VarDecl] constArrayLiteral | semmle.label | [VarDecl] constArrayLiteral | | tst.ts:42:5:42:39 | [VariableDeclarator] constAr ... s const | semmle.label | [VariableDeclarator] constAr ... s const | | tst.ts:42:25:42:30 | [ArrayExpr] [1, 2] | semmle.label | [ArrayExpr] [1, 2] | @@ -322,7 +333,7 @@ nodes | tst.ts:42:29:42:29 | [Literal] 2 | semmle.label | [Literal] 2 | | tst.ts:42:35:42:39 | [KeywordTypeExpr] const | semmle.label | [KeywordTypeExpr] const | | tst.ts:43:1:43:49 | [DeclStmt] let constObjectLiteral = ... | semmle.label | [DeclStmt] let constObjectLiteral = ... | -| tst.ts:43:1:43:49 | [DeclStmt] let constObjectLiteral = ... | semmle.order | 45 | +| tst.ts:43:1:43:49 | [DeclStmt] let constObjectLiteral = ... | semmle.order | 46 | | tst.ts:43:5:43:22 | [VarDecl] constObjectLiteral | semmle.label | [VarDecl] constObjectLiteral | | tst.ts:43:5:43:48 | [VariableDeclarator] constOb ... s const | semmle.label | [VariableDeclarator] constOb ... s const | | tst.ts:43:26:43:39 | [ObjectExpr] {foo: ...} | semmle.label | [ObjectExpr] {foo: ...} | @@ -332,7 +343,7 @@ nodes | tst.ts:43:33:43:37 | [Literal] "foo" | semmle.label | [Literal] "foo" | | tst.ts:43:44:43:48 | [KeywordTypeExpr] const | semmle.label | [KeywordTypeExpr] const | | tst.ts:46:1:51:1 | [TryStmt] try { } ... ; } } | semmle.label | [TryStmt] try { } ... ; } } | -| tst.ts:46:1:51:1 | [TryStmt] try { } ... ; } } | semmle.order | 46 | +| tst.ts:46:1:51:1 | [TryStmt] try { } ... ; } } | semmle.order | 47 | | tst.ts:46:5:46:7 | [BlockStmt] { } | semmle.label | [BlockStmt] { } | | tst.ts:47:1:51:1 | [CatchClause] catch ( ... ; } } | semmle.label | [CatchClause] catch ( ... ; } } | | tst.ts:47:8:47:8 | [SimpleParameter] e | semmle.label | [SimpleParameter] e | @@ -349,21 +360,21 @@ nodes | tst.ts:49:15:49:20 | [KeywordTypeExpr] string | semmle.label | [KeywordTypeExpr] string | | tst.ts:49:24:49:24 | [VarRef] e | semmle.label | [VarRef] e | | tst.ts:54:1:56:1 | [InterfaceDeclaration,TypeDefinition] interfa ... mber; } | semmle.label | [InterfaceDeclaration,TypeDefinition] interfa ... mber; } | -| tst.ts:54:1:56:1 | [InterfaceDeclaration,TypeDefinition] interfa ... mber; } | semmle.order | 47 | +| tst.ts:54:1:56:1 | [InterfaceDeclaration,TypeDefinition] interfa ... mber; } | semmle.order | 48 | | tst.ts:54:11:54:26 | [Identifier] NonAbstractDummy | semmle.label | [Identifier] NonAbstractDummy | | tst.ts:55:3:55:9 | [Label] getArea | semmle.label | [Label] getArea | | tst.ts:55:3:55:20 | [FunctionExpr] getArea(): number; | semmle.label | [FunctionExpr] getArea(): number; | | tst.ts:55:3:55:20 | [MethodSignature] getArea(): number; | semmle.label | [MethodSignature] getArea(): number; | | tst.ts:55:14:55:19 | [KeywordTypeExpr] number | semmle.label | [KeywordTypeExpr] number | | tst.ts:58:1:60:1 | [InterfaceDeclaration,TypeDefinition] interfa ... mber; } | semmle.label | [InterfaceDeclaration,TypeDefinition] interfa ... mber; } | -| tst.ts:58:1:60:1 | [InterfaceDeclaration,TypeDefinition] interfa ... mber; } | semmle.order | 48 | +| tst.ts:58:1:60:1 | [InterfaceDeclaration,TypeDefinition] interfa ... mber; } | semmle.order | 49 | | tst.ts:58:11:58:17 | [Identifier] HasArea | semmle.label | [Identifier] HasArea | | tst.ts:59:3:59:9 | [Label] getArea | semmle.label | [Label] getArea | | tst.ts:59:3:59:20 | [FunctionExpr] getArea(): number; | semmle.label | [FunctionExpr] getArea(): number; | | tst.ts:59:3:59:20 | [MethodSignature] getArea(): number; | semmle.label | [MethodSignature] getArea(): number; | | tst.ts:59:14:59:19 | [KeywordTypeExpr] number | semmle.label | [KeywordTypeExpr] number | | tst.ts:63:1:63:45 | [DeclStmt] let Ctor = ... | semmle.label | [DeclStmt] let Ctor = ... | -| tst.ts:63:1:63:45 | [DeclStmt] let Ctor = ... | semmle.order | 49 | +| tst.ts:63:1:63:45 | [DeclStmt] let Ctor = ... | semmle.order | 50 | | tst.ts:63:5:63:8 | [VarDecl] Ctor | semmle.label | [VarDecl] Ctor | | tst.ts:63:5:63:44 | [VariableDeclarator] Ctor: a ... = Shape | semmle.label | [VariableDeclarator] Ctor: a ... = Shape | | tst.ts:63:11:63:36 | [FunctionExpr] abstrac ... HasArea | semmle.label | [FunctionExpr] abstrac ... HasArea | @@ -371,7 +382,7 @@ nodes | tst.ts:63:30:63:36 | [LocalTypeAccess] HasArea | semmle.label | [LocalTypeAccess] HasArea | | tst.ts:63:40:63:44 | [VarRef] Shape | semmle.label | [VarRef] Shape | | tst.ts:65:1:65:54 | [TypeAliasDeclaration,TypeDefinition] type My ... true}; | semmle.label | [TypeAliasDeclaration,TypeDefinition] type My ... true}; | -| tst.ts:65:1:65:54 | [TypeAliasDeclaration,TypeDefinition] type My ... true}; | semmle.order | 50 | +| tst.ts:65:1:65:54 | [TypeAliasDeclaration,TypeDefinition] type My ... true}; | semmle.order | 51 | | tst.ts:65:6:65:12 | [Identifier] MyUnion | semmle.label | [Identifier] MyUnion | | tst.ts:65:16:65:30 | [InterfaceTypeExpr] {myUnion: true} | semmle.label | [InterfaceTypeExpr] {myUnion: true} | | tst.ts:65:16:65:53 | [UnionTypeExpr] {myUnio ... : true} | semmle.label | [UnionTypeExpr] {myUnio ... : true} | @@ -383,7 +394,7 @@ nodes | tst.ts:65:35:65:52 | [FieldDeclaration] stillMyUnion: true | semmle.label | [FieldDeclaration] stillMyUnion: true | | tst.ts:65:49:65:52 | [LiteralTypeExpr] true | semmle.label | [LiteralTypeExpr] true | | tst.ts:66:1:66:38 | [DeclStmt] let union1 = ... | semmle.label | [DeclStmt] let union1 = ... | -| tst.ts:66:1:66:38 | [DeclStmt] let union1 = ... | semmle.order | 51 | +| tst.ts:66:1:66:38 | [DeclStmt] let union1 = ... | semmle.order | 52 | | tst.ts:66:5:66:10 | [VarDecl] union1 | semmle.label | [VarDecl] union1 | | tst.ts:66:5:66:37 | [VariableDeclarator] union1: ... : true} | semmle.label | [VariableDeclarator] union1: ... : true} | | tst.ts:66:13:66:19 | [LocalTypeAccess] MyUnion | semmle.label | [LocalTypeAccess] MyUnion | @@ -392,7 +403,7 @@ nodes | tst.ts:66:24:66:36 | [Property] myUnion: true | semmle.label | [Property] myUnion: true | | tst.ts:66:33:66:36 | [Literal] true | semmle.label | [Literal] true | | tst.ts:68:1:68:49 | [TypeAliasDeclaration,TypeDefinition] type My ... true}; | semmle.label | [TypeAliasDeclaration,TypeDefinition] type My ... true}; | -| tst.ts:68:1:68:49 | [TypeAliasDeclaration,TypeDefinition] type My ... true}; | semmle.order | 52 | +| tst.ts:68:1:68:49 | [TypeAliasDeclaration,TypeDefinition] type My ... true}; | semmle.order | 53 | | tst.ts:68:6:68:13 | [Identifier] MyUnion2 | semmle.label | [Identifier] MyUnion2 | | tst.ts:68:17:68:23 | [LocalTypeAccess] MyUnion | semmle.label | [LocalTypeAccess] MyUnion | | tst.ts:68:17:68:48 | [UnionTypeExpr] MyUnion ... : true} | semmle.label | [UnionTypeExpr] MyUnion ... : true} | @@ -401,7 +412,7 @@ nodes | tst.ts:68:28:68:47 | [FieldDeclaration] yetAnotherType: true | semmle.label | [FieldDeclaration] yetAnotherType: true | | tst.ts:68:44:68:47 | [LiteralTypeExpr] true | semmle.label | [LiteralTypeExpr] true | | tst.ts:69:1:69:46 | [DeclStmt] let union2 = ... | semmle.label | [DeclStmt] let union2 = ... | -| tst.ts:69:1:69:46 | [DeclStmt] let union2 = ... | semmle.order | 53 | +| tst.ts:69:1:69:46 | [DeclStmt] let union2 = ... | semmle.order | 54 | | tst.ts:69:5:69:10 | [VarDecl] union2 | semmle.label | [VarDecl] union2 | | tst.ts:69:5:69:45 | [VariableDeclarator] union2: ... : true} | semmle.label | [VariableDeclarator] union2: ... : true} | | tst.ts:69:13:69:20 | [LocalTypeAccess] MyUnion2 | semmle.label | [LocalTypeAccess] MyUnion2 | @@ -410,16 +421,16 @@ nodes | tst.ts:69:25:69:44 | [Property] yetAnotherType: true | semmle.label | [Property] yetAnotherType: true | | tst.ts:69:41:69:44 | [Literal] true | semmle.label | [Literal] true | | type_alias.ts:1:1:1:17 | [TypeAliasDeclaration,TypeDefinition] type B = boolean; | semmle.label | [TypeAliasDeclaration,TypeDefinition] type B = boolean; | -| type_alias.ts:1:1:1:17 | [TypeAliasDeclaration,TypeDefinition] type B = boolean; | semmle.order | 54 | +| type_alias.ts:1:1:1:17 | [TypeAliasDeclaration,TypeDefinition] type B = boolean; | semmle.order | 55 | | type_alias.ts:1:6:1:6 | [Identifier] B | semmle.label | [Identifier] B | | type_alias.ts:1:10:1:16 | [KeywordTypeExpr] boolean | semmle.label | [KeywordTypeExpr] boolean | | type_alias.ts:3:1:3:9 | [DeclStmt] var b = ... | semmle.label | [DeclStmt] var b = ... | -| type_alias.ts:3:1:3:9 | [DeclStmt] var b = ... | semmle.order | 55 | +| type_alias.ts:3:1:3:9 | [DeclStmt] var b = ... | semmle.order | 56 | | type_alias.ts:3:5:3:5 | [VarDecl] b | semmle.label | [VarDecl] b | | type_alias.ts:3:5:3:8 | [VariableDeclarator] b: B | semmle.label | [VariableDeclarator] b: B | | type_alias.ts:3:8:3:8 | [LocalTypeAccess] B | semmle.label | [LocalTypeAccess] B | | type_alias.ts:5:1:5:50 | [TypeAliasDeclaration,TypeDefinition] type Va ... ay>; | semmle.label | [TypeAliasDeclaration,TypeDefinition] type Va ... ay>; | -| type_alias.ts:5:1:5:50 | [TypeAliasDeclaration,TypeDefinition] type Va ... ay>; | semmle.order | 56 | +| type_alias.ts:5:1:5:50 | [TypeAliasDeclaration,TypeDefinition] type Va ... ay>; | semmle.order | 57 | | type_alias.ts:5:6:5:17 | [Identifier] ValueOrArray | semmle.label | [Identifier] ValueOrArray | | type_alias.ts:5:19:5:19 | [Identifier] T | semmle.label | [Identifier] T | | type_alias.ts:5:19:5:19 | [TypeParameter] T | semmle.label | [TypeParameter] T | @@ -431,14 +442,14 @@ nodes | type_alias.ts:5:34:5:48 | [GenericTypeExpr] ValueOrArray | semmle.label | [GenericTypeExpr] ValueOrArray | | type_alias.ts:5:47:5:47 | [LocalTypeAccess] T | semmle.label | [LocalTypeAccess] T | | type_alias.ts:7:1:7:28 | [DeclStmt] var c = ... | semmle.label | [DeclStmt] var c = ... | -| type_alias.ts:7:1:7:28 | [DeclStmt] var c = ... | semmle.order | 57 | +| type_alias.ts:7:1:7:28 | [DeclStmt] var c = ... | semmle.order | 58 | | type_alias.ts:7:5:7:5 | [VarDecl] c | semmle.label | [VarDecl] c | | type_alias.ts:7:5:7:27 | [VariableDeclarator] c: Valu ... number> | semmle.label | [VariableDeclarator] c: Valu ... number> | | type_alias.ts:7:8:7:19 | [LocalTypeAccess] ValueOrArray | semmle.label | [LocalTypeAccess] ValueOrArray | | type_alias.ts:7:8:7:27 | [GenericTypeExpr] ValueOrArray | semmle.label | [GenericTypeExpr] ValueOrArray | | type_alias.ts:7:21:7:26 | [KeywordTypeExpr] number | semmle.label | [KeywordTypeExpr] number | | type_alias.ts:9:1:15:13 | [TypeAliasDeclaration,TypeDefinition] type Js ... Json[]; | semmle.label | [TypeAliasDeclaration,TypeDefinition] type Js ... Json[]; | -| type_alias.ts:9:1:15:13 | [TypeAliasDeclaration,TypeDefinition] type Js ... Json[]; | semmle.order | 58 | +| type_alias.ts:9:1:15:13 | [TypeAliasDeclaration,TypeDefinition] type Js ... Json[]; | semmle.order | 59 | | type_alias.ts:9:6:9:9 | [Identifier] Json | semmle.label | [Identifier] Json | | type_alias.ts:10:5:15:12 | [UnionTypeExpr] \| strin ... Json[] | semmle.label | [UnionTypeExpr] \| strin ... Json[] | | type_alias.ts:10:7:10:12 | [KeywordTypeExpr] string | semmle.label | [KeywordTypeExpr] string | @@ -454,12 +465,12 @@ nodes | type_alias.ts:15:7:15:10 | [LocalTypeAccess] Json | semmle.label | [LocalTypeAccess] Json | | type_alias.ts:15:7:15:12 | [ArrayTypeExpr] Json[] | semmle.label | [ArrayTypeExpr] Json[] | | type_alias.ts:17:1:17:15 | [DeclStmt] var json = ... | semmle.label | [DeclStmt] var json = ... | -| type_alias.ts:17:1:17:15 | [DeclStmt] var json = ... | semmle.order | 59 | +| type_alias.ts:17:1:17:15 | [DeclStmt] var json = ... | semmle.order | 60 | | type_alias.ts:17:5:17:8 | [VarDecl] json | semmle.label | [VarDecl] json | | type_alias.ts:17:5:17:14 | [VariableDeclarator] json: Json | semmle.label | [VariableDeclarator] json: Json | | type_alias.ts:17:11:17:14 | [LocalTypeAccess] Json | semmle.label | [LocalTypeAccess] Json | | type_alias.ts:19:1:21:57 | [TypeAliasDeclaration,TypeDefinition] type Vi ... ode[]]; | semmle.label | [TypeAliasDeclaration,TypeDefinition] type Vi ... ode[]]; | -| type_alias.ts:19:1:21:57 | [TypeAliasDeclaration,TypeDefinition] type Vi ... ode[]]; | semmle.order | 60 | +| type_alias.ts:19:1:21:57 | [TypeAliasDeclaration,TypeDefinition] type Vi ... ode[]]; | semmle.order | 61 | | type_alias.ts:19:6:19:16 | [Identifier] VirtualNode | semmle.label | [Identifier] VirtualNode | | type_alias.ts:20:5:21:56 | [UnionTypeExpr] \| strin ... Node[]] | semmle.label | [UnionTypeExpr] \| strin ... Node[]] | | type_alias.ts:20:7:20:12 | [KeywordTypeExpr] string | semmle.label | [KeywordTypeExpr] string | @@ -475,7 +486,7 @@ nodes | type_alias.ts:21:43:21:53 | [LocalTypeAccess] VirtualNode | semmle.label | [LocalTypeAccess] VirtualNode | | type_alias.ts:21:43:21:55 | [ArrayTypeExpr] VirtualNode[] | semmle.label | [ArrayTypeExpr] VirtualNode[] | | type_alias.ts:23:1:27:6 | [DeclStmt] const myNode = ... | semmle.label | [DeclStmt] const myNode = ... | -| type_alias.ts:23:1:27:6 | [DeclStmt] const myNode = ... | semmle.order | 61 | +| type_alias.ts:23:1:27:6 | [DeclStmt] const myNode = ... | semmle.order | 62 | | type_alias.ts:23:7:23:12 | [VarDecl] myNode | semmle.label | [VarDecl] myNode | | type_alias.ts:23:7:27:5 | [VariableDeclarator] myNode: ... ] ] | semmle.label | [VariableDeclarator] myNode: ... ] ] | | type_alias.ts:23:15:23:25 | [LocalTypeAccess] VirtualNode | semmle.label | [LocalTypeAccess] VirtualNode | @@ -500,12 +511,12 @@ nodes | type_alias.ts:26:23:26:36 | [Literal] "second-child" | semmle.label | [Literal] "second-child" | | type_alias.ts:26:41:26:62 | [Literal] "I'm the second child" | semmle.label | [Literal] "I'm the second child" | | type_definition_objects.ts:1:1:1:33 | [ImportDeclaration] import ... dummy"; | semmle.label | [ImportDeclaration] import ... dummy"; | -| type_definition_objects.ts:1:1:1:33 | [ImportDeclaration] import ... dummy"; | semmle.order | 62 | +| type_definition_objects.ts:1:1:1:33 | [ImportDeclaration] import ... dummy"; | semmle.order | 63 | | type_definition_objects.ts:1:8:1:17 | [ImportSpecifier] * as dummy | semmle.label | [ImportSpecifier] * as dummy | | type_definition_objects.ts:1:13:1:17 | [VarDecl] dummy | semmle.label | [VarDecl] dummy | | type_definition_objects.ts:1:24:1:32 | [Literal] "./dummy" | semmle.label | [Literal] "./dummy" | | type_definition_objects.ts:3:1:3:17 | [ExportDeclaration] export class C {} | semmle.label | [ExportDeclaration] export class C {} | -| type_definition_objects.ts:3:1:3:17 | [ExportDeclaration] export class C {} | semmle.order | 63 | +| type_definition_objects.ts:3:1:3:17 | [ExportDeclaration] export class C {} | semmle.order | 64 | | type_definition_objects.ts:3:8:3:17 | [ClassDefinition,TypeDefinition] class C {} | semmle.label | [ClassDefinition,TypeDefinition] class C {} | | type_definition_objects.ts:3:14:3:14 | [VarDecl] C | semmle.label | [VarDecl] C | | type_definition_objects.ts:3:16:3:15 | [BlockStmt] {} | semmle.label | [BlockStmt] {} | @@ -513,36 +524,36 @@ nodes | type_definition_objects.ts:3:16:3:15 | [FunctionExpr] () {} | semmle.label | [FunctionExpr] () {} | | type_definition_objects.ts:3:16:3:15 | [Label] constructor | semmle.label | [Label] constructor | | type_definition_objects.ts:4:1:4:17 | [DeclStmt] let classObj = ... | semmle.label | [DeclStmt] let classObj = ... | -| type_definition_objects.ts:4:1:4:17 | [DeclStmt] let classObj = ... | semmle.order | 64 | +| type_definition_objects.ts:4:1:4:17 | [DeclStmt] let classObj = ... | semmle.order | 65 | | type_definition_objects.ts:4:5:4:12 | [VarDecl] classObj | semmle.label | [VarDecl] classObj | | type_definition_objects.ts:4:5:4:16 | [VariableDeclarator] classObj = C | semmle.label | [VariableDeclarator] classObj = C | | type_definition_objects.ts:4:16:4:16 | [VarRef] C | semmle.label | [VarRef] C | | type_definition_objects.ts:6:1:6:16 | [ExportDeclaration] export enum E {} | semmle.label | [ExportDeclaration] export enum E {} | -| type_definition_objects.ts:6:1:6:16 | [ExportDeclaration] export enum E {} | semmle.order | 65 | +| type_definition_objects.ts:6:1:6:16 | [ExportDeclaration] export enum E {} | semmle.order | 66 | | type_definition_objects.ts:6:8:6:16 | [EnumDeclaration,TypeDefinition] enum E {} | semmle.label | [EnumDeclaration,TypeDefinition] enum E {} | | type_definition_objects.ts:6:13:6:13 | [VarDecl] E | semmle.label | [VarDecl] E | | type_definition_objects.ts:7:1:7:16 | [DeclStmt] let enumObj = ... | semmle.label | [DeclStmt] let enumObj = ... | -| type_definition_objects.ts:7:1:7:16 | [DeclStmt] let enumObj = ... | semmle.order | 66 | +| type_definition_objects.ts:7:1:7:16 | [DeclStmt] let enumObj = ... | semmle.order | 67 | | type_definition_objects.ts:7:5:7:11 | [VarDecl] enumObj | semmle.label | [VarDecl] enumObj | | type_definition_objects.ts:7:5:7:15 | [VariableDeclarator] enumObj = E | semmle.label | [VariableDeclarator] enumObj = E | | type_definition_objects.ts:7:15:7:15 | [VarRef] E | semmle.label | [VarRef] E | | type_definition_objects.ts:9:1:9:22 | [ExportDeclaration] export ... e N {;} | semmle.label | [ExportDeclaration] export ... e N {;} | -| type_definition_objects.ts:9:1:9:22 | [ExportDeclaration] export ... e N {;} | semmle.order | 67 | +| type_definition_objects.ts:9:1:9:22 | [ExportDeclaration] export ... e N {;} | semmle.order | 68 | | type_definition_objects.ts:9:8:9:22 | [NamespaceDeclaration] namespace N {;} | semmle.label | [NamespaceDeclaration] namespace N {;} | | type_definition_objects.ts:9:18:9:18 | [VarDecl] N | semmle.label | [VarDecl] N | | type_definition_objects.ts:9:21:9:21 | [EmptyStmt] ; | semmle.label | [EmptyStmt] ; | | type_definition_objects.ts:10:1:10:21 | [DeclStmt] let namespaceObj = ... | semmle.label | [DeclStmt] let namespaceObj = ... | -| type_definition_objects.ts:10:1:10:21 | [DeclStmt] let namespaceObj = ... | semmle.order | 68 | +| type_definition_objects.ts:10:1:10:21 | [DeclStmt] let namespaceObj = ... | semmle.order | 69 | | type_definition_objects.ts:10:5:10:16 | [VarDecl] namespaceObj | semmle.label | [VarDecl] namespaceObj | | type_definition_objects.ts:10:5:10:20 | [VariableDeclarator] namespaceObj = N | semmle.label | [VariableDeclarator] namespaceObj = N | | type_definition_objects.ts:10:20:10:20 | [VarRef] N | semmle.label | [VarRef] N | | type_definitions.ts:1:1:1:33 | [ImportDeclaration] import ... dummy"; | semmle.label | [ImportDeclaration] import ... dummy"; | -| type_definitions.ts:1:1:1:33 | [ImportDeclaration] import ... dummy"; | semmle.order | 69 | +| type_definitions.ts:1:1:1:33 | [ImportDeclaration] import ... dummy"; | semmle.order | 70 | | type_definitions.ts:1:8:1:17 | [ImportSpecifier] * as dummy | semmle.label | [ImportSpecifier] * as dummy | | type_definitions.ts:1:13:1:17 | [VarDecl] dummy | semmle.label | [VarDecl] dummy | | type_definitions.ts:1:24:1:32 | [Literal] "./dummy" | semmle.label | [Literal] "./dummy" | | type_definitions.ts:3:1:5:1 | [InterfaceDeclaration,TypeDefinition] interfa ... x: S; } | semmle.label | [InterfaceDeclaration,TypeDefinition] interfa ... x: S; } | -| type_definitions.ts:3:1:5:1 | [InterfaceDeclaration,TypeDefinition] interfa ... x: S; } | semmle.order | 70 | +| type_definitions.ts:3:1:5:1 | [InterfaceDeclaration,TypeDefinition] interfa ... x: S; } | semmle.order | 71 | | type_definitions.ts:3:11:3:11 | [Identifier] I | semmle.label | [Identifier] I | | type_definitions.ts:3:13:3:13 | [Identifier] S | semmle.label | [Identifier] S | | type_definitions.ts:3:13:3:13 | [TypeParameter] S | semmle.label | [TypeParameter] S | @@ -550,14 +561,14 @@ nodes | type_definitions.ts:4:3:4:7 | [FieldDeclaration] x: S; | semmle.label | [FieldDeclaration] x: S; | | type_definitions.ts:4:6:4:6 | [LocalTypeAccess] S | semmle.label | [LocalTypeAccess] S | | type_definitions.ts:6:1:6:16 | [DeclStmt] let i = ... | semmle.label | [DeclStmt] let i = ... | -| type_definitions.ts:6:1:6:16 | [DeclStmt] let i = ... | semmle.order | 71 | +| type_definitions.ts:6:1:6:16 | [DeclStmt] let i = ... | semmle.order | 72 | | type_definitions.ts:6:5:6:5 | [VarDecl] i | semmle.label | [VarDecl] i | | type_definitions.ts:6:5:6:16 | [VariableDeclarator] i: I | semmle.label | [VariableDeclarator] i: I | | type_definitions.ts:6:8:6:8 | [LocalTypeAccess] I | semmle.label | [LocalTypeAccess] I | | type_definitions.ts:6:8:6:16 | [GenericTypeExpr] I | semmle.label | [GenericTypeExpr] I | | type_definitions.ts:6:10:6:15 | [KeywordTypeExpr] number | semmle.label | [KeywordTypeExpr] number | | type_definitions.ts:8:1:10:1 | [ClassDefinition,TypeDefinition] class C ... x: T } | semmle.label | [ClassDefinition,TypeDefinition] class C ... x: T } | -| type_definitions.ts:8:1:10:1 | [ClassDefinition,TypeDefinition] class C ... x: T } | semmle.order | 72 | +| type_definitions.ts:8:1:10:1 | [ClassDefinition,TypeDefinition] class C ... x: T } | semmle.order | 73 | | type_definitions.ts:8:7:8:7 | [VarDecl] C | semmle.label | [VarDecl] C | | type_definitions.ts:8:8:8:7 | [BlockStmt] {} | semmle.label | [BlockStmt] {} | | type_definitions.ts:8:8:8:7 | [ClassInitializedMember,ConstructorDefinition] constructor() {} | semmle.label | [ClassInitializedMember,ConstructorDefinition] constructor() {} | @@ -569,14 +580,14 @@ nodes | type_definitions.ts:9:3:9:6 | [FieldDeclaration] x: T | semmle.label | [FieldDeclaration] x: T | | type_definitions.ts:9:6:9:6 | [LocalTypeAccess] T | semmle.label | [LocalTypeAccess] T | | type_definitions.ts:11:1:11:17 | [DeclStmt] let c = ... | semmle.label | [DeclStmt] let c = ... | -| type_definitions.ts:11:1:11:17 | [DeclStmt] let c = ... | semmle.order | 73 | +| type_definitions.ts:11:1:11:17 | [DeclStmt] let c = ... | semmle.order | 74 | | type_definitions.ts:11:5:11:5 | [VarDecl] c | semmle.label | [VarDecl] c | | type_definitions.ts:11:5:11:16 | [VariableDeclarator] c: C | semmle.label | [VariableDeclarator] c: C | | type_definitions.ts:11:8:11:8 | [LocalTypeAccess] C | semmle.label | [LocalTypeAccess] C | | type_definitions.ts:11:8:11:16 | [GenericTypeExpr] C | semmle.label | [GenericTypeExpr] C | | type_definitions.ts:11:10:11:15 | [KeywordTypeExpr] number | semmle.label | [KeywordTypeExpr] number | | type_definitions.ts:13:1:15:1 | [EnumDeclaration,TypeDefinition] enum Co ... blue } | semmle.label | [EnumDeclaration,TypeDefinition] enum Co ... blue } | -| type_definitions.ts:13:1:15:1 | [EnumDeclaration,TypeDefinition] enum Co ... blue } | semmle.order | 74 | +| type_definitions.ts:13:1:15:1 | [EnumDeclaration,TypeDefinition] enum Co ... blue } | semmle.order | 75 | | type_definitions.ts:13:6:13:10 | [VarDecl] Color | semmle.label | [VarDecl] Color | | type_definitions.ts:14:3:14:5 | [EnumMember,TypeDefinition] red | semmle.label | [EnumMember,TypeDefinition] red | | type_definitions.ts:14:3:14:5 | [VarDecl] red | semmle.label | [VarDecl] red | @@ -585,29 +596,29 @@ nodes | type_definitions.ts:14:15:14:18 | [EnumMember,TypeDefinition] blue | semmle.label | [EnumMember,TypeDefinition] blue | | type_definitions.ts:14:15:14:18 | [VarDecl] blue | semmle.label | [VarDecl] blue | | type_definitions.ts:16:1:16:17 | [DeclStmt] let color = ... | semmle.label | [DeclStmt] let color = ... | -| type_definitions.ts:16:1:16:17 | [DeclStmt] let color = ... | semmle.order | 75 | +| type_definitions.ts:16:1:16:17 | [DeclStmt] let color = ... | semmle.order | 76 | | type_definitions.ts:16:5:16:9 | [VarDecl] color | semmle.label | [VarDecl] color | | type_definitions.ts:16:5:16:16 | [VariableDeclarator] color: Color | semmle.label | [VariableDeclarator] color: Color | | type_definitions.ts:16:12:16:16 | [LocalTypeAccess] Color | semmle.label | [LocalTypeAccess] Color | | type_definitions.ts:18:1:18:33 | [EnumDeclaration,TypeDefinition] enum En ... ember } | semmle.label | [EnumDeclaration,TypeDefinition] enum En ... ember } | -| type_definitions.ts:18:1:18:33 | [EnumDeclaration,TypeDefinition] enum En ... ember } | semmle.order | 76 | +| type_definitions.ts:18:1:18:33 | [EnumDeclaration,TypeDefinition] enum En ... ember } | semmle.order | 77 | | type_definitions.ts:18:6:18:22 | [VarDecl] EnumWithOneMember | semmle.label | [VarDecl] EnumWithOneMember | | type_definitions.ts:18:26:18:31 | [EnumMember,TypeDefinition] member | semmle.label | [EnumMember,TypeDefinition] member | | type_definitions.ts:18:26:18:31 | [VarDecl] member | semmle.label | [VarDecl] member | | type_definitions.ts:19:1:19:25 | [DeclStmt] let e = ... | semmle.label | [DeclStmt] let e = ... | -| type_definitions.ts:19:1:19:25 | [DeclStmt] let e = ... | semmle.order | 77 | +| type_definitions.ts:19:1:19:25 | [DeclStmt] let e = ... | semmle.order | 78 | | type_definitions.ts:19:5:19:5 | [VarDecl] e | semmle.label | [VarDecl] e | | type_definitions.ts:19:5:19:24 | [VariableDeclarator] e: EnumWithOneMember | semmle.label | [VariableDeclarator] e: EnumWithOneMember | | type_definitions.ts:19:8:19:24 | [LocalTypeAccess] EnumWithOneMember | semmle.label | [LocalTypeAccess] EnumWithOneMember | | type_definitions.ts:21:1:21:20 | [TypeAliasDeclaration,TypeDefinition] type Alias = T[]; | semmle.label | [TypeAliasDeclaration,TypeDefinition] type Alias = T[]; | -| type_definitions.ts:21:1:21:20 | [TypeAliasDeclaration,TypeDefinition] type Alias = T[]; | semmle.order | 78 | +| type_definitions.ts:21:1:21:20 | [TypeAliasDeclaration,TypeDefinition] type Alias = T[]; | semmle.order | 79 | | type_definitions.ts:21:6:21:10 | [Identifier] Alias | semmle.label | [Identifier] Alias | | type_definitions.ts:21:12:21:12 | [Identifier] T | semmle.label | [Identifier] T | | type_definitions.ts:21:12:21:12 | [TypeParameter] T | semmle.label | [TypeParameter] T | | type_definitions.ts:21:17:21:17 | [LocalTypeAccess] T | semmle.label | [LocalTypeAccess] T | | type_definitions.ts:21:17:21:19 | [ArrayTypeExpr] T[] | semmle.label | [ArrayTypeExpr] T[] | | type_definitions.ts:22:1:22:39 | [DeclStmt] let aliasForNumberArray = ... | semmle.label | [DeclStmt] let aliasForNumberArray = ... | -| type_definitions.ts:22:1:22:39 | [DeclStmt] let aliasForNumberArray = ... | semmle.order | 79 | +| type_definitions.ts:22:1:22:39 | [DeclStmt] let aliasForNumberArray = ... | semmle.order | 80 | | type_definitions.ts:22:5:22:23 | [VarDecl] aliasForNumberArray | semmle.label | [VarDecl] aliasForNumberArray | | type_definitions.ts:22:5:22:38 | [VariableDeclarator] aliasFo ... number> | semmle.label | [VariableDeclarator] aliasFo ... number> | | type_definitions.ts:22:26:22:30 | [LocalTypeAccess] Alias | semmle.label | [LocalTypeAccess] Alias | @@ -716,6 +727,24 @@ edges | dummy.ts:2:12:2:16 | [VariableDeclarator] x = 5 | dummy.ts:2:12:2:12 | [VarDecl] x | semmle.order | 1 | | dummy.ts:2:12:2:16 | [VariableDeclarator] x = 5 | dummy.ts:2:16:2:16 | [Literal] 5 | semmle.label | 2 | | dummy.ts:2:12:2:16 | [VariableDeclarator] x = 5 | dummy.ts:2:16:2:16 | [Literal] 5 | semmle.order | 2 | +| dummy.ts:4:1:4:24 | [ExportDeclaration] export ... /ab+c/; | dummy.ts:4:8:4:24 | [DeclStmt] let reg = ... | semmle.label | 1 | +| dummy.ts:4:1:4:24 | [ExportDeclaration] export ... /ab+c/; | dummy.ts:4:8:4:24 | [DeclStmt] let reg = ... | semmle.order | 1 | +| dummy.ts:4:8:4:24 | [DeclStmt] let reg = ... | dummy.ts:4:12:4:23 | [VariableDeclarator] reg = /ab+c/ | semmle.label | 1 | +| dummy.ts:4:8:4:24 | [DeclStmt] let reg = ... | dummy.ts:4:12:4:23 | [VariableDeclarator] reg = /ab+c/ | semmle.order | 1 | +| dummy.ts:4:12:4:23 | [VariableDeclarator] reg = /ab+c/ | dummy.ts:4:12:4:14 | [VarDecl] reg | semmle.label | 1 | +| dummy.ts:4:12:4:23 | [VariableDeclarator] reg = /ab+c/ | dummy.ts:4:12:4:14 | [VarDecl] reg | semmle.order | 1 | +| dummy.ts:4:12:4:23 | [VariableDeclarator] reg = /ab+c/ | dummy.ts:4:18:4:23 | [RegExpLiteral] /ab+c/ | semmle.label | 2 | +| dummy.ts:4:12:4:23 | [VariableDeclarator] reg = /ab+c/ | dummy.ts:4:18:4:23 | [RegExpLiteral] /ab+c/ | semmle.order | 2 | +| dummy.ts:4:18:4:23 | [RegExpLiteral] /ab+c/ | dummy.ts:4:19:4:22 | [RegExpSequence] ab+c | semmle.label | 0 | +| dummy.ts:4:18:4:23 | [RegExpLiteral] /ab+c/ | dummy.ts:4:19:4:22 | [RegExpSequence] ab+c | semmle.order | 0 | +| dummy.ts:4:19:4:22 | [RegExpSequence] ab+c | dummy.ts:4:19:4:19 | [RegExpNormalConstant] a | semmle.label | 0 | +| dummy.ts:4:19:4:22 | [RegExpSequence] ab+c | dummy.ts:4:19:4:19 | [RegExpNormalConstant] a | semmle.order | 0 | +| dummy.ts:4:19:4:22 | [RegExpSequence] ab+c | dummy.ts:4:20:4:21 | [RegExpPlus] b+ | semmle.label | 1 | +| dummy.ts:4:19:4:22 | [RegExpSequence] ab+c | dummy.ts:4:20:4:21 | [RegExpPlus] b+ | semmle.order | 1 | +| dummy.ts:4:19:4:22 | [RegExpSequence] ab+c | dummy.ts:4:22:4:22 | [RegExpNormalConstant] c | semmle.label | 2 | +| dummy.ts:4:19:4:22 | [RegExpSequence] ab+c | dummy.ts:4:22:4:22 | [RegExpNormalConstant] c | semmle.order | 2 | +| dummy.ts:4:20:4:21 | [RegExpPlus] b+ | dummy.ts:4:20:4:20 | [RegExpNormalConstant] b | semmle.label | 0 | +| dummy.ts:4:20:4:21 | [RegExpPlus] b+ | dummy.ts:4:20:4:20 | [RegExpNormalConstant] b | semmle.order | 0 | | file://:0:0:0:0 | (Parameters) | tst.ts:14:17:14:17 | [SimpleParameter] x | semmle.label | 0 | | file://:0:0:0:0 | (Parameters) | tst.ts:14:17:14:17 | [SimpleParameter] x | semmle.order | 0 | | file://:0:0:0:0 | (Parameters) | tst.ts:14:28:14:28 | [SimpleParameter] y | semmle.label | 1 | diff --git a/javascript/ql/test/library-tests/TypeScript/Types/tests.expected b/javascript/ql/test/library-tests/TypeScript/Types/tests.expected index c9cad8a1443..7c8159a9b10 100644 --- a/javascript/ql/test/library-tests/TypeScript/Types/tests.expected +++ b/javascript/ql/test/library-tests/TypeScript/Types/tests.expected @@ -15,6 +15,8 @@ getExprType | boolean-type.ts:15:5:15:12 | boolean6 | boolean | | dummy.ts:2:12:2:12 | x | number | | dummy.ts:2:16:2:16 | 5 | 5 | +| dummy.ts:4:12:4:14 | reg | RegExp | +| dummy.ts:4:18:4:23 | /ab+c/ | RegExp | | middle-rest.ts:1:5:1:7 | foo | [boolean, ...string[], number] | | middle-rest.ts:3:1:3:3 | foo | [boolean, ...string[], number] | | middle-rest.ts:3:1:3:26 | foo = [ ... ", 123] | [true, string, number] | From 504c34ed2c1493b0f7b413e710e8545d8ebfd21f Mon Sep 17 00:00:00 2001 From: Erik Krogh Kristensen Date: Mon, 10 May 2021 14:51:13 +0200 Subject: [PATCH 098/168] use shouldPrint to filter out regular expressions from other files --- javascript/ql/src/semmle/javascript/PrintAst.qll | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/javascript/ql/src/semmle/javascript/PrintAst.qll b/javascript/ql/src/semmle/javascript/PrintAst.qll index 99f06354d74..029cfd536fc 100644 --- a/javascript/ql/src/semmle/javascript/PrintAst.qll +++ b/javascript/ql/src/semmle/javascript/PrintAst.qll @@ -74,7 +74,11 @@ private newtype TPrintAstNode = THTMLAttributeNode(HTML::Attribute attr) { shouldPrint(attr, _) and not isNotNeeded(attr) } or THTMLScript(Script script) { shouldPrint(script, _) and not isNotNeeded(script) } or THTMLCodeInAttr(CodeInAttribute attr) { shouldPrint(attr, _) and not isNotNeeded(attr) } or - TRegExpTermNode(RegExpTerm term) { term.isUsedAsRegExp() } + TRegExpTermNode(RegExpTerm term) { + shouldPrint(term, _) and + term.isUsedAsRegExp() and + any(RegExpLiteral lit).getRoot() = term.getRootTerm() + } /** * A node in the output tree. From 78370cf63fa0bf528c3f566654c73bdf25547f1f Mon Sep 17 00:00:00 2001 From: yoff Date: Mon, 10 May 2021 14:53:40 +0200 Subject: [PATCH 099/168] Update python/ql/src/experimental/semmle/python/frameworks/Stdlib.qll --- python/ql/src/experimental/semmle/python/frameworks/Stdlib.qll | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/python/ql/src/experimental/semmle/python/frameworks/Stdlib.qll b/python/ql/src/experimental/semmle/python/frameworks/Stdlib.qll index 05ea4f62630..4f3457e0a99 100644 --- a/python/ql/src/experimental/semmle/python/frameworks/Stdlib.qll +++ b/python/ql/src/experimental/semmle/python/frameworks/Stdlib.qll @@ -58,7 +58,7 @@ private module Re { * This class will identify that `re.compile` compiles `input` and afterwards * executes `re`'s `match`. As a result, `this` will refer to `pattern.match(s)` * and `this.getRegexNode()` will return the node for `input` (`re.compile`'s first argument) - * + * * * See `RegexExecutionMethods` * From 31ac6442e8c3161405938f8621d6c15b34439df8 Mon Sep 17 00:00:00 2001 From: Tamas Vajk Date: Mon, 10 May 2021 15:49:31 +0200 Subject: [PATCH 100/168] C#: Fix default parameter value generation in case of error symbols --- .../extractor/Semmle.Extraction.CSharp/Entities/Expression.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/csharp/extractor/Semmle.Extraction.CSharp/Entities/Expression.cs b/csharp/extractor/Semmle.Extraction.CSharp/Entities/Expression.cs index 103ea8cacda..a478047ac7b 100644 --- a/csharp/extractor/Semmle.Extraction.CSharp/Entities/Expression.cs +++ b/csharp/extractor/Semmle.Extraction.CSharp/Entities/Expression.cs @@ -170,7 +170,8 @@ namespace Semmle.Extraction.CSharp.Entities public static Expression? CreateGenerated(Context cx, IParameterSymbol parameter, IExpressionParentEntity parent, int childIndex, Extraction.Entities.Location location) { - if (!parameter.HasExplicitDefaultValue) + if (!parameter.HasExplicitDefaultValue || + parameter.Type is IErrorTypeSymbol) { return null; } From dd86da3f242ea1186f85be6da8e424940b91fe02 Mon Sep 17 00:00:00 2001 From: Tamas Vajk Date: Mon, 10 May 2021 15:48:33 +0200 Subject: [PATCH 101/168] C#: Remove base class from type IDs in trap files --- .../Entities/Types/Type.cs | 74 +- .../SymbolExtensions.cs | 47 +- csharp/ql/src/semmlecode.csharp.dbscheme | 2 +- .../old.dbscheme | 2083 +++++++++++++++++ .../semmlecode.csharp.dbscheme | 2083 +++++++++++++++++ .../upgrade.properties | 2 + 6 files changed, 4233 insertions(+), 58 deletions(-) create mode 100644 csharp/upgrades/9258e9b38d85f92cee9559f2ed21e241f0c7a29e/old.dbscheme create mode 100644 csharp/upgrades/9258e9b38d85f92cee9559f2ed21e241f0c7a29e/semmlecode.csharp.dbscheme create mode 100644 csharp/upgrades/9258e9b38d85f92cee9559f2ed21e241f0c7a29e/upgrade.properties diff --git a/csharp/extractor/Semmle.Extraction.CSharp/Entities/Types/Type.cs b/csharp/extractor/Semmle.Extraction.CSharp/Entities/Types/Type.cs index e2522648678..67936f9a913 100644 --- a/csharp/extractor/Semmle.Extraction.CSharp/Entities/Types/Type.cs +++ b/csharp/extractor/Semmle.Extraction.CSharp/Entities/Types/Type.cs @@ -81,22 +81,45 @@ namespace Semmle.Extraction.CSharp.Entities Symbol.BuildDisplayName(Context, trapFile, constructUnderlyingTupleType); trapFile.WriteLine("\")"); + var baseTypes = GetBaseTypeDeclarations(); + // Visit base types - var baseTypes = new List(); if (Symbol.GetNonObjectBaseType(Context) is INamedTypeSymbol @base) { - var baseKey = Create(Context, @base); - trapFile.extend(this, baseKey.TypeRef); - if (Symbol.TypeKind != TypeKind.Struct) - baseTypes.Add(baseKey); + var bts = GetBaseTypeDeclarations(baseTypes, @base); + + Context.PopulateLater(() => + { + var baseKey = Create(Context, @base); + trapFile.extend(this, baseKey.TypeRef); + + if (Symbol.TypeKind != TypeKind.Struct) + { + foreach (var bt in bts) + { + TypeMention.Create(Context, bt.Type, this, baseKey); + } + } + }); } + // Visit implemented interfaces if (!(base.Symbol is IArrayTypeSymbol)) { - foreach (var t in base.Symbol.Interfaces.Select(i => Create(Context, i))) + foreach (var i in base.Symbol.Interfaces) { - trapFile.implement(this, t.TypeRef); - baseTypes.Add(t); + var bts = GetBaseTypeDeclarations(baseTypes, i); + + Context.PopulateLater(() => + { + var interfaceKey = Create(Context, i); + trapFile.implement(this, interfaceKey.TypeRef); + + foreach (var bt in bts) + { + TypeMention.Create(Context, bt.Type, this, interfaceKey); + } + }); } } @@ -145,23 +168,30 @@ namespace Semmle.Extraction.CSharp.Entities } Modifier.ExtractModifiers(Context, trapFile, this, Symbol); + } - if (IsSourceDeclaration && Symbol.FromSource()) + private IEnumerable GetBaseTypeDeclarations() + { + if (!IsSourceDeclaration || !Symbol.FromSource()) { - var declSyntaxReferences = Symbol.DeclaringSyntaxReferences.Select(d => d.GetSyntax()).ToArray(); - - var baseLists = declSyntaxReferences.OfType().Select(c => c.BaseList); - baseLists = baseLists.Concat(declSyntaxReferences.OfType().Select(c => c.BaseList)); - baseLists = baseLists.Concat(declSyntaxReferences.OfType().Select(c => c.BaseList)); - - baseLists - .Where(bl => bl is not null) - .SelectMany(bl => bl!.Types) - .Zip( - baseTypes.Where(bt => bt.Symbol.SpecialType != SpecialType.System_Object), - (s, t) => TypeMention.Create(Context, s.Type, this, t)) - .Enumerate(); + return Enumerable.Empty(); } + + var declSyntaxReferences = Symbol.DeclaringSyntaxReferences.Select(d => d.GetSyntax()).ToArray(); + + var baseLists = declSyntaxReferences.OfType().Select(c => c.BaseList); + baseLists = baseLists.Concat(declSyntaxReferences.OfType().Select(c => c.BaseList)); + baseLists = baseLists.Concat(declSyntaxReferences.OfType().Select(c => c.BaseList)); + + return baseLists + .Where(bl => bl is not null) + .SelectMany(bl => bl!.Types) + .ToList(); + } + + private IEnumerable GetBaseTypeDeclarations(IEnumerable baseTypes, INamedTypeSymbol type) + { + return baseTypes.Where(bt => SymbolEqualityComparer.Default.Equals(Context.GetModel(bt).GetTypeInfo(bt.Type).Type, type)); } private void ExtractParametersForDelegateLikeType(TextWriter trapFile, IMethodSymbol invokeMethod, Action storeReturnType) diff --git a/csharp/extractor/Semmle.Extraction.CSharp/SymbolExtensions.cs b/csharp/extractor/Semmle.Extraction.CSharp/SymbolExtensions.cs index 32b90e37068..91f0d0edf20 100644 --- a/csharp/extractor/Semmle.Extraction.CSharp/SymbolExtensions.cs +++ b/csharp/extractor/Semmle.Extraction.CSharp/SymbolExtensions.cs @@ -121,8 +121,6 @@ namespace Semmle.Extraction.CSharp named = named.TupleUnderlyingType; if (IdDependsOnImpl(named.ContainingType)) return true; - if (IdDependsOnImpl(named.GetNonObjectBaseType(cx))) - return true; if (IdDependsOnImpl(named.ConstructedFrom)) return true; return named.TypeArguments.Any(IdDependsOnImpl); @@ -160,10 +158,7 @@ namespace Semmle.Extraction.CSharp /// The trap builder used to store the result. /// The outer symbol being defined (to avoid recursive ids). /// Whether to build a type ID for the underlying `System.ValueTuple` struct in the case of tuple types. - public static void BuildTypeId(this ITypeSymbol type, Context cx, EscapingTextWriter trapFile, ISymbol symbolBeingDefined, bool constructUnderlyingTupleType = false) => - type.BuildTypeId(cx, trapFile, symbolBeingDefined, true, constructUnderlyingTupleType); - - private static void BuildTypeId(this ITypeSymbol type, Context cx, EscapingTextWriter trapFile, ISymbol symbolBeingDefined, bool addBaseClass, bool constructUnderlyingTupleType) + public static void BuildTypeId(this ITypeSymbol type, Context cx, EscapingTextWriter trapFile, ISymbol symbolBeingDefined, bool constructUnderlyingTupleType = false) { using (cx.StackGuard) { @@ -171,7 +166,7 @@ namespace Semmle.Extraction.CSharp { case TypeKind.Array: var array = (IArrayTypeSymbol)type; - array.ElementType.BuildOrWriteId(cx, trapFile, symbolBeingDefined, addBaseClass); + array.ElementType.BuildOrWriteId(cx, trapFile, symbolBeingDefined); array.BuildArraySuffix(trapFile); return; case TypeKind.Class: @@ -181,16 +176,16 @@ namespace Semmle.Extraction.CSharp case TypeKind.Delegate: case TypeKind.Error: var named = (INamedTypeSymbol)type; - named.BuildNamedTypeId(cx, trapFile, symbolBeingDefined, addBaseClass, constructUnderlyingTupleType); + named.BuildNamedTypeId(cx, trapFile, symbolBeingDefined, constructUnderlyingTupleType); return; case TypeKind.Pointer: var ptr = (IPointerTypeSymbol)type; - ptr.PointedAtType.BuildOrWriteId(cx, trapFile, symbolBeingDefined, addBaseClass); + ptr.PointedAtType.BuildOrWriteId(cx, trapFile, symbolBeingDefined); trapFile.Write('*'); return; case TypeKind.TypeParameter: var tp = (ITypeParameterSymbol)type; - tp.ContainingSymbol.BuildOrWriteId(cx, trapFile, symbolBeingDefined, addBaseClass); + tp.ContainingSymbol.BuildOrWriteId(cx, trapFile, symbolBeingDefined); trapFile.Write('_'); trapFile.Write(tp.Name); return; @@ -207,7 +202,7 @@ namespace Semmle.Extraction.CSharp } } - private static void BuildOrWriteId(this ISymbol? symbol, Context cx, EscapingTextWriter trapFile, ISymbol symbolBeingDefined, bool addBaseClass, bool constructUnderlyingTupleType = false) + private static void BuildOrWriteId(this ISymbol? symbol, Context cx, EscapingTextWriter trapFile, ISymbol symbolBeingDefined, bool constructUnderlyingTupleType = false) { if (symbol is null) { @@ -232,7 +227,7 @@ namespace Semmle.Extraction.CSharp if (SymbolEqualityComparer.Default.Equals(symbol, symbolBeingDefined)) trapFile.Write("__self__"); else if (symbol is ITypeSymbol type && type.IdDependsOn(cx, symbolBeingDefined)) - type.BuildTypeId(cx, trapFile, symbolBeingDefined, addBaseClass, constructUnderlyingTupleType); + type.BuildTypeId(cx, trapFile, symbolBeingDefined, constructUnderlyingTupleType); else if (symbol is INamedTypeSymbol namedType && namedType.IsTupleType && constructUnderlyingTupleType) trapFile.WriteSubId(NamedType.CreateNamedTypeFromTupleType(cx, namedType)); else @@ -287,7 +282,7 @@ namespace Semmle.Extraction.CSharp BuildFunctionPointerSignature(funptr, trapFile, s => s.BuildOrWriteId(cx, trapFile, symbolBeingDefined)); } - private static void BuildNamedTypeId(this INamedTypeSymbol named, Context cx, EscapingTextWriter trapFile, ISymbol symbolBeingDefined, bool addBaseClass, bool constructUnderlyingTupleType) + private static void BuildNamedTypeId(this INamedTypeSymbol named, Context cx, EscapingTextWriter trapFile, ISymbol symbolBeingDefined, bool constructUnderlyingTupleType) { if (!constructUnderlyingTupleType && named.IsTupleType) { @@ -297,7 +292,7 @@ namespace Semmle.Extraction.CSharp { trapFile.Write((f.CorrespondingTupleField ?? f).Name); trapFile.Write(":"); - f.Type.BuildOrWriteId(cx, trapFile, symbolBeingDefined, addBaseClass); + f.Type.BuildOrWriteId(cx, trapFile, symbolBeingDefined); } ); trapFile.Write(")"); @@ -308,7 +303,7 @@ namespace Semmle.Extraction.CSharp { if (named.ContainingType is not null) { - named.ContainingType.BuildOrWriteId(cx, trapFile, symbolBeingDefined, addBaseClass); + named.ContainingType.BuildOrWriteId(cx, trapFile, symbolBeingDefined); trapFile.Write('.'); } else if (named.ContainingNamespace is not null) @@ -333,35 +328,17 @@ namespace Semmle.Extraction.CSharp } else { - named.ConstructedFrom.BuildOrWriteId(cx, trapFile, symbolBeingDefined, addBaseClass, constructUnderlyingTupleType); + named.ConstructedFrom.BuildOrWriteId(cx, trapFile, symbolBeingDefined, constructUnderlyingTupleType); trapFile.Write('<'); // Encode the nullability of the type arguments in the label. // Type arguments with different nullability can result in // a constructed type with different nullability of its members and methods, // so we need to create a distinct database entity for it. trapFile.BuildList(",", named.GetAnnotatedTypeArguments(), - ta => ta.Symbol.BuildOrWriteId(cx, trapFile, symbolBeingDefined, addBaseClass) + ta => ta.Symbol.BuildOrWriteId(cx, trapFile, symbolBeingDefined) ); trapFile.Write('>'); } - - if (addBaseClass && named.GetNonObjectBaseType(cx) is INamedTypeSymbol @base) - { - // We need to limit unfolding of base classes. For example, in - // - // ```csharp - // class C1 { } - // class C2 : C1> { } - // class C3 : C1> { } - // class C4 : C3 { } - // ``` - // - // when we generate the label for `C4`, the base class `C3` has itself `C1>` as - // a base class, which in turn has `C1>` as a base class. The latter has the original - // base class `C3` as a type argument, which would lead to infinite unfolding. - trapFile.Write(" : "); - @base.BuildOrWriteId(cx, trapFile, symbolBeingDefined, addBaseClass: false); - } } private static void BuildNamespace(this INamespaceSymbol ns, Context cx, EscapingTextWriter trapFile) diff --git a/csharp/ql/src/semmlecode.csharp.dbscheme b/csharp/ql/src/semmlecode.csharp.dbscheme index 9258e9b38d8..770f844243d 100644 --- a/csharp/ql/src/semmlecode.csharp.dbscheme +++ b/csharp/ql/src/semmlecode.csharp.dbscheme @@ -529,7 +529,7 @@ function_pointer_return_type( int return_type_id: @type_or_ref ref); extend( - unique int sub: @type ref, + int sub: @type ref, int super: @type_or_ref ref); anonymous_types( diff --git a/csharp/upgrades/9258e9b38d85f92cee9559f2ed21e241f0c7a29e/old.dbscheme b/csharp/upgrades/9258e9b38d85f92cee9559f2ed21e241f0c7a29e/old.dbscheme new file mode 100644 index 00000000000..9258e9b38d8 --- /dev/null +++ b/csharp/upgrades/9258e9b38d85f92cee9559f2ed21e241f0c7a29e/old.dbscheme @@ -0,0 +1,2083 @@ + +/** + * An invocation of the compiler. Note that more than one file may be + * compiled per invocation. For example, this command compiles three + * source files: + * + * csc f1.cs f2.cs f3.cs + * + * The `id` simply identifies the invocation, while `cwd` is the working + * directory from which the compiler was invoked. + */ +compilations( + unique int id : @compilation, + string cwd : string ref +); + +/** + * The arguments that were passed to the extractor for a compiler + * invocation. If `id` is for the compiler invocation + * + * csc f1.cs f2.cs f3.cs + * + * then typically there will be rows for + * + * num | arg + * --- | --- + * 0 | --compiler + * 1 | *path to compiler* + * 2 | --cil + * 3 | f1.cs + * 4 | f2.cs + * 5 | f3.cs + */ +#keyset[id, num] +compilation_args( + int id : @compilation ref, + int num : int ref, + string arg : string ref +); + +/** + * The source files that are compiled by a compiler invocation. + * If `id` is for the compiler invocation + * + * csc f1.cs f2.cs f3.cs + * + * then there will be rows for + * + * num | arg + * --- | --- + * 0 | f1.cs + * 1 | f2.cs + * 2 | f3.cs + */ +#keyset[id, num] +compilation_compiling_files( + int id : @compilation ref, + int num : int ref, + int file : @file ref +); + +/** + * The references used by a compiler invocation. + * If `id` is for the compiler invocation + * + * csc f1.cs f2.cs f3.cs /r:ref1.dll /r:ref2.dll /r:ref3.dll + * + * then there will be rows for + * + * num | arg + * --- | --- + * 0 | ref1.dll + * 1 | ref2.dll + * 2 | ref3.dll + */ +#keyset[id, num] +compilation_referencing_files( + int id : @compilation ref, + int num : int ref, + int file : @file ref +); + +/** + * The time taken by the extractor for a compiler invocation. + * + * For each file `num`, there will be rows for + * + * kind | seconds + * ---- | --- + * 1 | CPU seconds used by the extractor frontend + * 2 | Elapsed seconds during the extractor frontend + * 3 | CPU seconds used by the extractor backend + * 4 | Elapsed seconds during the extractor backend + */ +#keyset[id, num, kind] +compilation_time( + int id : @compilation ref, + int num : int ref, + /* kind: + 1 = frontend_cpu_seconds + 2 = frontend_elapsed_seconds + 3 = extractor_cpu_seconds + 4 = extractor_elapsed_seconds + */ + int kind : int ref, + float seconds : float ref +); + +/** + * An error or warning generated by the extractor. + * The diagnostic message `diagnostic` was generated during compiler + * invocation `compilation`, and is the `file_number_diagnostic_number`th + * message generated while extracting the `file_number`th file of that + * invocation. + */ +#keyset[compilation, file_number, file_number_diagnostic_number] +diagnostic_for( + unique int diagnostic : @diagnostic ref, + int compilation : @compilation ref, + int file_number : int ref, + int file_number_diagnostic_number : int ref +); + +diagnostics( + unique int id: @diagnostic, + int severity: int ref, + string error_tag: string ref, + string error_message: string ref, + string full_error_message: string ref, + int location: @location_default ref +); + +extractor_messages( + unique int id: @extractor_message, + int severity: int ref, + string origin : string ref, + string text : string ref, + string entity : string ref, + int location: @location_default ref, + string stack_trace : string ref +); + +/** + * If extraction was successful, then `cpu_seconds` and + * `elapsed_seconds` are the CPU time and elapsed time (respectively) + * that extraction took for compiler invocation `id`. + */ +compilation_finished( + unique int id : @compilation ref, + float cpu_seconds : float ref, + float elapsed_seconds : float ref +); + +compilation_assembly( + unique int id : @compilation ref, + int assembly: @assembly ref +) + +/* + * External artifacts + */ + +externalDefects( + unique int id: @externalDefect, + string queryPath: string ref, + int location: @location ref, + string message: string ref, + float severity: float ref); + +externalMetrics( + unique int id: @externalMetric, + string queryPath: string ref, + int location: @location ref, + float value: float ref); + +externalData( + int id: @externalDataElement, + string path: string ref, + int column: int ref, + string value: string ref); + +snapshotDate( + unique date snapshotDate: date ref); + +sourceLocationPrefix( + string prefix: string ref); + +/* + * Duplicate code + */ + +duplicateCode( + unique int id: @duplication, + string relativePath: string ref, + int equivClass: int ref); + +similarCode( + unique int id: @similarity, + string relativePath: string ref, + int equivClass: int ref); + +@duplication_or_similarity = @duplication | @similarity + +tokens( + int id: @duplication_or_similarity ref, + int offset: int ref, + int beginLine: int ref, + int beginColumn: int ref, + int endLine: int ref, + int endColumn: int ref); + +/* + * C# dbscheme + */ + +/** ELEMENTS **/ + +@element = @declaration | @stmt | @expr | @modifier | @attribute | @namespace_declaration + | @using_directive | @type_parameter_constraints | @external_element + | @xmllocatable | @asp_element | @namespace | @preprocessor_directive; + +@declaration = @callable | @generic | @assignable | @namespace; + +@named_element = @namespace | @declaration; + +@declaration_with_accessors = @property | @indexer | @event; + +@assignable = @variable | @assignable_with_accessors | @event; + +@assignable_with_accessors = @property | @indexer; + +@external_element = @externalMetric | @externalDefect | @externalDataElement; + +@attributable = @assembly | @field | @parameter | @operator | @method | @constructor + | @destructor | @callable_accessor | @value_or_ref_type | @declaration_with_accessors + | @local_function; + +/** LOCATIONS, ASEMMBLIES, MODULES, FILES and FOLDERS **/ + +@location = @location_default | @assembly; + +locations_default( + unique int id: @location_default, + int file: @file ref, + int beginLine: int ref, + int beginColumn: int ref, + int endLine: int ref, + int endColumn: int ref); + +locations_mapped( + unique int id: @location_default ref, + int mapped_to: @location_default ref); + +@sourceline = @file | @callable | @xmllocatable; + +numlines( + int element_id: @sourceline ref, + int num_lines: int ref, + int num_code: int ref, + int num_comment: int ref); + +assemblies( + unique int id: @assembly, + int file: @file ref, + string fullname: string ref, + string name: string ref, + string version: string ref); + +/* + fromSource(0) = unknown, + fromSource(1) = from source, + fromSource(2) = from library +*/ +files( + unique int id: @file, + string name: string ref, + string simple: string ref, + string ext: string ref, + int fromSource: int ref); + +folders( + unique int id: @folder, + string name: string ref, + string simple: string ref); + +@container = @folder | @file ; + +containerparent( + int parent: @container ref, + unique int child: @container ref); + +file_extraction_mode( + unique int file: @file ref, + int mode: int ref + /* 0 = normal, 1 = standalone extractor */ + ); + +/** NAMESPACES **/ + +@type_container = @namespace | @type; + +namespaces( + unique int id: @namespace, + string name: string ref); + +namespace_declarations( + unique int id: @namespace_declaration, + int namespace_id: @namespace ref); + +namespace_declaration_location( + unique int id: @namespace_declaration ref, + int loc: @location ref); + +parent_namespace( + unique int child_id: @type_container ref, + int namespace_id: @namespace ref); + +@declaration_or_directive = @namespace_declaration | @type | @using_directive; + +parent_namespace_declaration( + int child_id: @declaration_or_directive ref, // cannot be unique because of partial classes + int namespace_id: @namespace_declaration ref); + +@using_directive = @using_namespace_directive | @using_static_directive; + +using_namespace_directives( + unique int id: @using_namespace_directive, + int namespace_id: @namespace ref); + +using_static_directives( + unique int id: @using_static_directive, + int type_id: @type_or_ref ref); + +using_directive_location( + unique int id: @using_directive ref, + int loc: @location ref); + +@preprocessor_directive = @pragma_warning | @pragma_checksum | @directive_define | @directive_undefine | @directive_warning + | @directive_error | @directive_nullable | @directive_line | @directive_region | @directive_endregion | @directive_if + | @directive_elif | @directive_else | @directive_endif; + +@conditional_directive = @directive_if | @directive_elif; +@branch_directive = @directive_if | @directive_elif | @directive_else; + +directive_ifs( + unique int id: @directive_if, + int branchTaken: int ref, /* 0: false, 1: true */ + int conditionValue: int ref); /* 0: false, 1: true */ + +directive_elifs( + unique int id: @directive_elif, + int branchTaken: int ref, /* 0: false, 1: true */ + int conditionValue: int ref, /* 0: false, 1: true */ + int parent: @directive_if ref, + int index: int ref); + +directive_elses( + unique int id: @directive_else, + int branchTaken: int ref, /* 0: false, 1: true */ + int parent: @directive_if ref, + int index: int ref); + +#keyset[id, start] +directive_endifs( + unique int id: @directive_endif, + unique int start: @directive_if ref); + +directive_define_symbols( + unique int id: @define_symbol_expr ref, + string name: string ref); + +directive_regions( + unique int id: @directive_region, + string name: string ref); + +#keyset[id, start] +directive_endregions( + unique int id: @directive_endregion, + unique int start: @directive_region ref); + +directive_lines( + unique int id: @directive_line, + int kind: int ref); /* 0: default, 1: hidden, 2: numeric */ + +directive_line_value( + unique int id: @directive_line ref, + int line: int ref); + +directive_line_file( + unique int id: @directive_line ref, + int file: @file ref +) + +directive_nullables( + unique int id: @directive_nullable, + int setting: int ref, /* 0: disable, 1: enable, 2: restore */ + int target: int ref); /* 0: none, 1: annotations, 2: warnings */ + +directive_warnings( + unique int id: @directive_warning, + string message: string ref); + +directive_errors( + unique int id: @directive_error, + string message: string ref); + +directive_undefines( + unique int id: @directive_undefine, + string name: string ref); + +directive_defines( + unique int id: @directive_define, + string name: string ref); + +pragma_checksums( + unique int id: @pragma_checksum, + int file: @file ref, + string guid: string ref, + string bytes: string ref); + +pragma_warnings( + unique int id: @pragma_warning, + int kind: int ref /* 0 = disable, 1 = restore */); + +#keyset[id, index] +pragma_warning_error_codes( + int id: @pragma_warning ref, + string errorCode: string ref, + int index: int ref); + +preprocessor_directive_location( + unique int id: @preprocessor_directive ref, + int loc: @location ref); + +preprocessor_directive_compilation( + unique int id: @preprocessor_directive ref, + int compilation: @compilation ref); + +preprocessor_directive_active( + unique int id: @preprocessor_directive ref, + int active: int ref); /* 0: false, 1: true */ + +/** TYPES **/ + +types( + unique int id: @type, + int kind: int ref, + string name: string ref); + +case @type.kind of + 1 = @bool_type +| 2 = @char_type +| 3 = @decimal_type +| 4 = @sbyte_type +| 5 = @short_type +| 6 = @int_type +| 7 = @long_type +| 8 = @byte_type +| 9 = @ushort_type +| 10 = @uint_type +| 11 = @ulong_type +| 12 = @float_type +| 13 = @double_type +| 14 = @enum_type +| 15 = @struct_type +| 17 = @class_type +| 19 = @interface_type +| 20 = @delegate_type +| 21 = @null_type +| 22 = @type_parameter +| 23 = @pointer_type +| 24 = @nullable_type +| 25 = @array_type +| 26 = @void_type +| 27 = @int_ptr_type +| 28 = @uint_ptr_type +| 29 = @dynamic_type +| 30 = @arglist_type +| 31 = @unknown_type +| 32 = @tuple_type +| 33 = @function_pointer_type + ; + +@simple_type = @bool_type | @char_type | @integral_type | @floating_point_type | @decimal_type; +@integral_type = @signed_integral_type | @unsigned_integral_type; +@signed_integral_type = @sbyte_type | @short_type | @int_type | @long_type; +@unsigned_integral_type = @byte_type | @ushort_type | @uint_type | @ulong_type; +@floating_point_type = @float_type | @double_type; +@value_type = @simple_type | @enum_type | @struct_type | @nullable_type | @int_ptr_type + | @uint_ptr_type | @tuple_type; +@ref_type = @class_type | @interface_type | @array_type | @delegate_type | @null_type + | @dynamic_type; +@value_or_ref_type = @value_type | @ref_type; + +typerefs( + unique int id: @typeref, + string name: string ref); + +typeref_type( + int id: @typeref ref, + unique int typeId: @type ref); + +@type_or_ref = @type | @typeref; + +array_element_type( + unique int array: @array_type ref, + int dimension: int ref, + int rank: int ref, + int element: @type_or_ref ref); + +nullable_underlying_type( + unique int nullable: @nullable_type ref, + int underlying: @type_or_ref ref); + +pointer_referent_type( + unique int pointer: @pointer_type ref, + int referent: @type_or_ref ref); + +enum_underlying_type( + unique int enum_id: @enum_type ref, + int underlying_type_id: @type_or_ref ref); + +delegate_return_type( + unique int delegate_id: @delegate_type ref, + int return_type_id: @type_or_ref ref); + +function_pointer_return_type( + unique int function_pointer_id: @function_pointer_type ref, + int return_type_id: @type_or_ref ref); + +extend( + unique int sub: @type ref, + int super: @type_or_ref ref); + +anonymous_types( + unique int id: @type ref); + +@interface_or_ref = @interface_type | @typeref; + +implement( + int sub: @type ref, + int super: @type_or_ref ref); + +type_location( + int id: @type ref, + int loc: @location ref); + +tuple_underlying_type( + unique int tuple: @tuple_type ref, + int struct: @type_or_ref ref); + +#keyset[tuple, index] +tuple_element( + int tuple: @tuple_type ref, + int index: int ref, + unique int field: @field ref); + +attributes( + unique int id: @attribute, + int type_id: @type_or_ref ref, + int target: @attributable ref); + +attribute_location( + int id: @attribute ref, + int loc: @location ref); + +@type_mention_parent = @element | @type_mention; + +type_mention( + unique int id: @type_mention, + int type_id: @type_or_ref ref, + int parent: @type_mention_parent ref); + +type_mention_location( + unique int id: @type_mention ref, + int loc: @location ref); + +@has_type_annotation = @assignable | @type_parameter | @callable | @expr | @delegate_type | @generic | @function_pointer_type; + +/** + * A direct annotation on an entity, for example `string? x;`. + * + * Annotations: + * 2 = reftype is not annotated "!" + * 3 = reftype is annotated "?" + * 4 = readonly ref type / in parameter + * 5 = ref type parameter, return or local variable + * 6 = out parameter + * + * Note that the annotation depends on the element it annotates. + * @assignable: The annotation is on the type of the assignable, for example the variable type. + * @type_parameter: The annotation is on the reftype constraint + * @callable: The annotation is on the return type + * @array_type: The annotation is on the element type + */ +type_annotation(int id: @has_type_annotation ref, int annotation: int ref); + +nullability(unique int nullability: @nullability, int kind: int ref); + +case @nullability.kind of + 0 = @oblivious +| 1 = @not_annotated +| 2 = @annotated +; + +#keyset[parent, index] +nullability_parent(int nullability: @nullability ref, int index: int ref, int parent: @nullability ref) + +type_nullability(int id: @has_type_annotation ref, int nullability: @nullability ref); + +/** + * The nullable flow state of an expression, as determined by Roslyn. + * 0 = none (default, not populated) + * 1 = not null + * 2 = maybe null + */ +expr_flowstate(unique int id: @expr ref, int state: int ref); + +/** GENERICS **/ + +@generic = @type | @method | @local_function; + +type_parameters( + unique int id: @type_parameter ref, + int index: int ref, + int generic_id: @generic ref, + int variance: int ref /* none = 0, out = 1, in = 2 */); + +#keyset[constructed_id, index] +type_arguments( + int id: @type_or_ref ref, + int index: int ref, + int constructed_id: @generic_or_ref ref); + +@generic_or_ref = @generic | @typeref; + +constructed_generic( + unique int constructed: @generic ref, + int generic: @generic_or_ref ref); + +type_parameter_constraints( + unique int id: @type_parameter_constraints, + int param_id: @type_parameter ref); + +type_parameter_constraints_location( + int id: @type_parameter_constraints ref, + int loc: @location ref); + +general_type_parameter_constraints( + int id: @type_parameter_constraints ref, + int kind: int ref /* class = 1, struct = 2, new = 3 */); + +specific_type_parameter_constraints( + int id: @type_parameter_constraints ref, + int base_id: @type_or_ref ref); + +specific_type_parameter_nullability( + int id: @type_parameter_constraints ref, + int base_id: @type_or_ref ref, + int nullability: @nullability ref); + +/** FUNCTION POINTERS */ + +function_pointer_calling_conventions( + int id: @function_pointer_type ref, + int kind: int ref); + +#keyset[id, index] +has_unmanaged_calling_conventions( + int id: @function_pointer_type ref, + int index: int ref, + int conv_id: @type_or_ref ref); + +/** MODIFIERS */ + +@modifiable = @modifiable_direct | @event_accessor; + +@modifiable_direct = @member | @accessor | @local_function | @anonymous_function_expr; + +modifiers( + unique int id: @modifier, + string name: string ref); + +has_modifiers( + int id: @modifiable_direct ref, + int mod_id: @modifier ref); + +compiler_generated(unique int id: @modifiable_direct ref); + +/** MEMBERS **/ + +@member = @method | @constructor | @destructor | @field | @property | @event | @operator | @indexer | @type; + +@named_exprorstmt = @goto_stmt | @labeled_stmt | @expr; + +@virtualizable = @method | @property | @indexer | @event; + +exprorstmt_name( + unique int parent_id: @named_exprorstmt ref, + string name: string ref); + +nested_types( + unique int id: @type ref, + int declaring_type_id: @type ref, + int unbound_id: @type ref); + +properties( + unique int id: @property, + string name: string ref, + int declaring_type_id: @type ref, + int type_id: @type_or_ref ref, + int unbound_id: @property ref); + +property_location( + int id: @property ref, + int loc: @location ref); + +indexers( + unique int id: @indexer, + string name: string ref, + int declaring_type_id: @type ref, + int type_id: @type_or_ref ref, + int unbound_id: @indexer ref); + +indexer_location( + int id: @indexer ref, + int loc: @location ref); + +accessors( + unique int id: @accessor, + int kind: int ref, + string name: string ref, + int declaring_member_id: @member ref, + int unbound_id: @accessor ref); + +case @accessor.kind of + 1 = @getter +| 2 = @setter + ; + +init_only_accessors( + unique int id: @accessor ref); + +accessor_location( + int id: @accessor ref, + int loc: @location ref); + +events( + unique int id: @event, + string name: string ref, + int declaring_type_id: @type ref, + int type_id: @type_or_ref ref, + int unbound_id: @event ref); + +event_location( + int id: @event ref, + int loc: @location ref); + +event_accessors( + unique int id: @event_accessor, + int kind: int ref, + string name: string ref, + int declaring_event_id: @event ref, + int unbound_id: @event_accessor ref); + +case @event_accessor.kind of + 1 = @add_event_accessor +| 2 = @remove_event_accessor + ; + +event_accessor_location( + int id: @event_accessor ref, + int loc: @location ref); + +operators( + unique int id: @operator, + string name: string ref, + string symbol: string ref, + int declaring_type_id: @type ref, + int type_id: @type_or_ref ref, + int unbound_id: @operator ref); + +operator_location( + int id: @operator ref, + int loc: @location ref); + +constant_value( + int id: @variable ref, + string value: string ref); + +/** CALLABLES **/ + +@callable = @method | @constructor | @destructor | @operator | @callable_accessor | @anonymous_function_expr | @local_function; + +@callable_accessor = @accessor | @event_accessor; + +methods( + unique int id: @method, + string name: string ref, + int declaring_type_id: @type ref, + int type_id: @type_or_ref ref, + int unbound_id: @method ref); + +method_location( + int id: @method ref, + int loc: @location ref); + +constructors( + unique int id: @constructor, + string name: string ref, + int declaring_type_id: @type ref, + int unbound_id: @constructor ref); + +constructor_location( + int id: @constructor ref, + int loc: @location ref); + +destructors( + unique int id: @destructor, + string name: string ref, + int declaring_type_id: @type ref, + int unbound_id: @destructor ref); + +destructor_location( + int id: @destructor ref, + int loc: @location ref); + +overrides( + int id: @callable ref, + int base_id: @callable ref); + +explicitly_implements( + int id: @member ref, + int interface_id: @interface_or_ref ref); + +local_functions( + unique int id: @local_function, + string name: string ref, + int return_type: @type ref, + int unbound_id: @local_function ref); + +local_function_stmts( + unique int fn: @local_function_stmt ref, + int stmt: @local_function ref); + +/** VARIABLES **/ + +@variable = @local_scope_variable | @field; + +@local_scope_variable = @local_variable | @parameter; + +fields( + unique int id: @field, + int kind: int ref, + string name: string ref, + int declaring_type_id: @type ref, + int type_id: @type_or_ref ref, + int unbound_id: @field ref); + +case @field.kind of + 1 = @addressable_field +| 2 = @constant + ; + +field_location( + int id: @field ref, + int loc: @location ref); + +localvars( + unique int id: @local_variable, + int kind: int ref, + string name: string ref, + int implicitly_typed: int ref /* 0 = no, 1 = yes */, + int type_id: @type_or_ref ref, + int parent_id: @local_var_decl_expr ref); + +case @local_variable.kind of + 1 = @addressable_local_variable +| 2 = @local_constant +| 3 = @local_variable_ref + ; + +localvar_location( + unique int id: @local_variable ref, + int loc: @location ref); + +@parameterizable = @callable | @delegate_type | @indexer | @function_pointer_type; + +#keyset[name, parent_id] +#keyset[index, parent_id] +params( + unique int id: @parameter, + string name: string ref, + int type_id: @type_or_ref ref, + int index: int ref, + int mode: int ref, /* value = 0, ref = 1, out = 2, array = 3, this = 4 */ + int parent_id: @parameterizable ref, + int unbound_id: @parameter ref); + +param_location( + int id: @parameter ref, + int loc: @location ref); + +/** STATEMENTS **/ + +@exprorstmt_parent = @control_flow_element | @top_level_exprorstmt_parent; + +statements( + unique int id: @stmt, + int kind: int ref); + +#keyset[index, parent] +stmt_parent( + unique int stmt: @stmt ref, + int index: int ref, + int parent: @control_flow_element ref); + +@top_level_stmt_parent = @callable; + +// [index, parent] is not a keyset because the same parent may be compiled multiple times +stmt_parent_top_level( + unique int stmt: @stmt ref, + int index: int ref, + int parent: @top_level_stmt_parent ref); + +case @stmt.kind of + 1 = @block_stmt +| 2 = @expr_stmt +| 3 = @if_stmt +| 4 = @switch_stmt +| 5 = @while_stmt +| 6 = @do_stmt +| 7 = @for_stmt +| 8 = @foreach_stmt +| 9 = @break_stmt +| 10 = @continue_stmt +| 11 = @goto_stmt +| 12 = @goto_case_stmt +| 13 = @goto_default_stmt +| 14 = @throw_stmt +| 15 = @return_stmt +| 16 = @yield_stmt +| 17 = @try_stmt +| 18 = @checked_stmt +| 19 = @unchecked_stmt +| 20 = @lock_stmt +| 21 = @using_block_stmt +| 22 = @var_decl_stmt +| 23 = @const_decl_stmt +| 24 = @empty_stmt +| 25 = @unsafe_stmt +| 26 = @fixed_stmt +| 27 = @label_stmt +| 28 = @catch +| 29 = @case_stmt +| 30 = @local_function_stmt +| 31 = @using_decl_stmt + ; + +@using_stmt = @using_block_stmt | @using_decl_stmt; + +@labeled_stmt = @label_stmt | @case; + +@decl_stmt = @var_decl_stmt | @const_decl_stmt | @using_decl_stmt; + +@cond_stmt = @if_stmt | @switch_stmt; + +@loop_stmt = @while_stmt | @do_stmt | @for_stmt | @foreach_stmt; + +@jump_stmt = @break_stmt | @goto_any_stmt | @continue_stmt | @throw_stmt | @return_stmt + | @yield_stmt; + +@goto_any_stmt = @goto_default_stmt | @goto_case_stmt | @goto_stmt; + + +stmt_location( + unique int id: @stmt ref, + int loc: @location ref); + +catch_type( + unique int catch_id: @catch ref, + int type_id: @type_or_ref ref, + int kind: int ref /* explicit = 1, implicit = 2 */); + +foreach_stmt_info( + unique int id: @foreach_stmt ref, + int kind: int ref /* non-async = 1, async = 2 */); + +@foreach_symbol = @method | @property | @type_or_ref; + +#keyset[id, kind] +foreach_stmt_desugar( + int id: @foreach_stmt ref, + int symbol: @foreach_symbol ref, + int kind: int ref /* GetEnumeratorMethod = 1, CurrentProperty = 2, MoveNextMethod = 3, DisposeMethod = 4, ElementType = 5 */); + +/** EXPRESSIONS **/ + +expressions( + unique int id: @expr, + int kind: int ref, + int type_id: @type_or_ref ref); + +#keyset[index, parent] +expr_parent( + unique int expr: @expr ref, + int index: int ref, + int parent: @control_flow_element ref); + +@top_level_expr_parent = @attribute | @field | @property | @indexer | @parameter | @directive_if | @directive_elif; + +@top_level_exprorstmt_parent = @top_level_expr_parent | @top_level_stmt_parent; + +// [index, parent] is not a keyset because the same parent may be compiled multiple times +expr_parent_top_level( + unique int expr: @expr ref, + int index: int ref, + int parent: @top_level_exprorstmt_parent ref); + +case @expr.kind of +/* literal */ + 1 = @bool_literal_expr +| 2 = @char_literal_expr +| 3 = @decimal_literal_expr +| 4 = @int_literal_expr +| 5 = @long_literal_expr +| 6 = @uint_literal_expr +| 7 = @ulong_literal_expr +| 8 = @float_literal_expr +| 9 = @double_literal_expr +| 10 = @string_literal_expr +| 11 = @null_literal_expr +/* primary & unary */ +| 12 = @this_access_expr +| 13 = @base_access_expr +| 14 = @local_variable_access_expr +| 15 = @parameter_access_expr +| 16 = @field_access_expr +| 17 = @property_access_expr +| 18 = @method_access_expr +| 19 = @event_access_expr +| 20 = @indexer_access_expr +| 21 = @array_access_expr +| 22 = @type_access_expr +| 23 = @typeof_expr +| 24 = @method_invocation_expr +| 25 = @delegate_invocation_expr +| 26 = @operator_invocation_expr +| 27 = @cast_expr +| 28 = @object_creation_expr +| 29 = @explicit_delegate_creation_expr +| 30 = @implicit_delegate_creation_expr +| 31 = @array_creation_expr +| 32 = @default_expr +| 33 = @plus_expr +| 34 = @minus_expr +| 35 = @bit_not_expr +| 36 = @log_not_expr +| 37 = @post_incr_expr +| 38 = @post_decr_expr +| 39 = @pre_incr_expr +| 40 = @pre_decr_expr +/* multiplicative */ +| 41 = @mul_expr +| 42 = @div_expr +| 43 = @rem_expr +/* additive */ +| 44 = @add_expr +| 45 = @sub_expr +/* shift */ +| 46 = @lshift_expr +| 47 = @rshift_expr +/* relational */ +| 48 = @lt_expr +| 49 = @gt_expr +| 50 = @le_expr +| 51 = @ge_expr +/* equality */ +| 52 = @eq_expr +| 53 = @ne_expr +/* logical */ +| 54 = @bit_and_expr +| 55 = @bit_xor_expr +| 56 = @bit_or_expr +| 57 = @log_and_expr +| 58 = @log_or_expr +/* type testing */ +| 59 = @is_expr +| 60 = @as_expr +/* null coalescing */ +| 61 = @null_coalescing_expr +/* conditional */ +| 62 = @conditional_expr +/* assignment */ +| 63 = @simple_assign_expr +| 64 = @assign_add_expr +| 65 = @assign_sub_expr +| 66 = @assign_mul_expr +| 67 = @assign_div_expr +| 68 = @assign_rem_expr +| 69 = @assign_and_expr +| 70 = @assign_xor_expr +| 71 = @assign_or_expr +| 72 = @assign_lshift_expr +| 73 = @assign_rshift_expr +/* more */ +| 74 = @object_init_expr +| 75 = @collection_init_expr +| 76 = @array_init_expr +| 77 = @checked_expr +| 78 = @unchecked_expr +| 79 = @constructor_init_expr +| 80 = @add_event_expr +| 81 = @remove_event_expr +| 82 = @par_expr +| 83 = @local_var_decl_expr +| 84 = @lambda_expr +| 85 = @anonymous_method_expr +| 86 = @namespace_expr +/* dynamic */ +| 92 = @dynamic_element_access_expr +| 93 = @dynamic_member_access_expr +/* unsafe */ +| 100 = @pointer_indirection_expr +| 101 = @address_of_expr +| 102 = @sizeof_expr +/* async */ +| 103 = @await_expr +/* C# 6.0 */ +| 104 = @nameof_expr +| 105 = @interpolated_string_expr +| 106 = @unknown_expr +/* C# 7.0 */ +| 107 = @throw_expr +| 108 = @tuple_expr +| 109 = @local_function_invocation_expr +| 110 = @ref_expr +| 111 = @discard_expr +/* C# 8.0 */ +| 112 = @range_expr +| 113 = @index_expr +| 114 = @switch_expr +| 115 = @recursive_pattern_expr +| 116 = @property_pattern_expr +| 117 = @positional_pattern_expr +| 118 = @switch_case_expr +| 119 = @assign_coalesce_expr +| 120 = @suppress_nullable_warning_expr +| 121 = @namespace_access_expr +/* C# 9.0 */ +| 122 = @lt_pattern_expr +| 123 = @gt_pattern_expr +| 124 = @le_pattern_expr +| 125 = @ge_pattern_expr +| 126 = @not_pattern_expr +| 127 = @and_pattern_expr +| 128 = @or_pattern_expr +| 129 = @function_pointer_invocation_expr +| 130 = @with_expr +/* Preprocessor */ +| 999 = @define_symbol_expr +; + +@switch = @switch_stmt | @switch_expr; +@case = @case_stmt | @switch_case_expr; +@pattern_match = @case | @is_expr; +@unary_pattern_expr = @not_pattern_expr; +@relational_pattern_expr = @gt_pattern_expr | @lt_pattern_expr | @ge_pattern_expr | @le_pattern_expr; +@binary_pattern_expr = @and_pattern_expr | @or_pattern_expr; + +@integer_literal_expr = @int_literal_expr | @long_literal_expr | @uint_literal_expr | @ulong_literal_expr; +@real_literal_expr = @float_literal_expr | @double_literal_expr | @decimal_literal_expr; +@literal_expr = @bool_literal_expr | @char_literal_expr | @integer_literal_expr | @real_literal_expr + | @string_literal_expr | @null_literal_expr; + +@assign_expr = @simple_assign_expr | @assign_op_expr | @local_var_decl_expr; +@assign_op_expr = @assign_arith_expr | @assign_bitwise_expr | @assign_event_expr | @assign_coalesce_expr; +@assign_event_expr = @add_event_expr | @remove_event_expr; + +@assign_arith_expr = @assign_add_expr | @assign_sub_expr | @assign_mul_expr | @assign_div_expr + | @assign_rem_expr +@assign_bitwise_expr = @assign_and_expr | @assign_or_expr | @assign_xor_expr + | @assign_lshift_expr | @assign_rshift_expr; + +@member_access_expr = @field_access_expr | @property_access_expr | @indexer_access_expr | @event_access_expr + | @method_access_expr | @type_access_expr | @dynamic_member_access_expr; +@access_expr = @member_access_expr | @this_access_expr | @base_access_expr | @assignable_access_expr | @namespace_access_expr; +@element_access_expr = @indexer_access_expr | @array_access_expr | @dynamic_element_access_expr; + +@local_variable_access = @local_variable_access_expr | @local_var_decl_expr; +@local_scope_variable_access_expr = @parameter_access_expr | @local_variable_access; +@variable_access_expr = @local_scope_variable_access_expr | @field_access_expr; + +@assignable_access_expr = @variable_access_expr | @property_access_expr | @element_access_expr + | @event_access_expr | @dynamic_member_access_expr; + +@objectorcollection_init_expr = @object_init_expr | @collection_init_expr; + +@delegate_creation_expr = @explicit_delegate_creation_expr | @implicit_delegate_creation_expr; + +@bin_arith_op_expr = @mul_expr | @div_expr | @rem_expr | @add_expr | @sub_expr; +@incr_op_expr = @pre_incr_expr | @post_incr_expr; +@decr_op_expr = @pre_decr_expr | @post_decr_expr; +@mut_op_expr = @incr_op_expr | @decr_op_expr; +@un_arith_op_expr = @plus_expr | @minus_expr | @mut_op_expr; +@arith_op_expr = @bin_arith_op_expr | @un_arith_op_expr; + +@ternary_log_op_expr = @conditional_expr; +@bin_log_op_expr = @log_and_expr | @log_or_expr | @null_coalescing_expr; +@un_log_op_expr = @log_not_expr; +@log_expr = @un_log_op_expr | @bin_log_op_expr | @ternary_log_op_expr; + +@bin_bit_op_expr = @bit_and_expr | @bit_or_expr | @bit_xor_expr | @lshift_expr + | @rshift_expr; +@un_bit_op_expr = @bit_not_expr; +@bit_expr = @un_bit_op_expr | @bin_bit_op_expr; + +@equality_op_expr = @eq_expr | @ne_expr; +@rel_op_expr = @gt_expr | @lt_expr| @ge_expr | @le_expr; +@comp_expr = @equality_op_expr | @rel_op_expr; + +@op_expr = @assign_expr | @un_op | @bin_op | @ternary_op; + +@ternary_op = @ternary_log_op_expr; +@bin_op = @bin_arith_op_expr | @bin_log_op_expr | @bin_bit_op_expr | @comp_expr; +@un_op = @un_arith_op_expr | @un_log_op_expr | @un_bit_op_expr | @sizeof_expr + | @pointer_indirection_expr | @address_of_expr; + +@anonymous_function_expr = @lambda_expr | @anonymous_method_expr; + +@call = @method_invocation_expr | @constructor_init_expr | @operator_invocation_expr + | @delegate_invocation_expr | @object_creation_expr | @call_access_expr + | @local_function_invocation_expr | @function_pointer_invocation_expr; + +@call_access_expr = @property_access_expr | @event_access_expr | @indexer_access_expr; + +@late_bindable_expr = @dynamic_element_access_expr | @dynamic_member_access_expr + | @object_creation_expr | @method_invocation_expr | @operator_invocation_expr; + +@throw_element = @throw_expr | @throw_stmt; + +@implicitly_typeable_object_creation_expr = @object_creation_expr | @explicit_delegate_creation_expr; + +implicitly_typed_array_creation( + unique int id: @array_creation_expr ref); + +explicitly_sized_array_creation( + unique int id: @array_creation_expr ref); + +stackalloc_array_creation( + unique int id: @array_creation_expr ref); + +implicitly_typed_object_creation( + unique int id: @implicitly_typeable_object_creation_expr ref); + +mutator_invocation_mode( + unique int id: @operator_invocation_expr ref, + int mode: int ref /* prefix = 1, postfix = 2*/); + +expr_compiler_generated( + unique int id: @expr ref); + +expr_value( + unique int id: @expr ref, + string value: string ref); + +expr_call( + unique int caller_id: @expr ref, + int target_id: @callable ref); + +expr_access( + unique int accesser_id: @access_expr ref, + int target_id: @accessible ref); + +@accessible = @method | @assignable | @local_function | @namespace; + +expr_location( + unique int id: @expr ref, + int loc: @location ref); + +dynamic_member_name( + unique int id: @late_bindable_expr ref, + string name: string ref); + +@qualifiable_expr = @member_access_expr + | @method_invocation_expr + | @element_access_expr; + +conditional_access( + unique int id: @qualifiable_expr ref); + +expr_argument( + unique int id: @expr ref, + int mode: int ref); + /* mode is the same as params: value = 0, ref = 1, out = 2 */ + +expr_argument_name( + unique int id: @expr ref, + string name: string ref); + +/** CONTROL/DATA FLOW **/ + +@control_flow_element = @stmt | @expr; + +/* XML Files */ + +xmlEncoding ( + unique int id: @file ref, + string encoding: string ref); + +xmlDTDs( + unique int id: @xmldtd, + string root: string ref, + string publicId: string ref, + string systemId: string ref, + int fileid: @file ref); + +xmlElements( + unique int id: @xmlelement, + string name: string ref, + int parentid: @xmlparent ref, + int idx: int ref, + int fileid: @file ref); + +xmlAttrs( + unique int id: @xmlattribute, + int elementid: @xmlelement ref, + string name: string ref, + string value: string ref, + int idx: int ref, + int fileid: @file ref); + +xmlNs( + int id: @xmlnamespace, + string prefixName: string ref, + string URI: string ref, + int fileid: @file ref); + +xmlHasNs( + int elementId: @xmlnamespaceable ref, + int nsId: @xmlnamespace ref, + int fileid: @file ref); + +xmlComments( + unique int id: @xmlcomment, + string text: string ref, + int parentid: @xmlparent ref, + int fileid: @file ref); + +xmlChars( + unique int id: @xmlcharacters, + string text: string ref, + int parentid: @xmlparent ref, + int idx: int ref, + int isCDATA: int ref, + int fileid: @file ref); + +@xmlparent = @file | @xmlelement; +@xmlnamespaceable = @xmlelement | @xmlattribute; + +xmllocations( + int xmlElement: @xmllocatable ref, + int location: @location_default ref); + +@xmllocatable = @xmlcharacters | @xmlelement | @xmlcomment | @xmlattribute | @xmldtd | @file | @xmlnamespace; + +/* Comments */ + +commentline( + unique int id: @commentline, + int kind: int ref, + string text: string ref, + string rawtext: string ref); + +case @commentline.kind of + 0 = @singlelinecomment +| 1 = @xmldoccomment +| 2 = @multilinecomment; + +commentline_location( + unique int id: @commentline ref, + int loc: @location ref); + +commentblock( + unique int id : @commentblock); + +commentblock_location( + unique int id: @commentblock ref, + int loc: @location ref); + +commentblock_binding( + int id: @commentblock ref, + int entity: @element ref, + int bindtype: int ref); /* 0: Parent, 1: Best, 2: Before, 3: After */ + +commentblock_child( + int id: @commentblock ref, + int commentline: @commentline ref, + int index: int ref); + +/* ASP.NET */ + +case @asp_element.kind of + 0=@asp_close_tag +| 1=@asp_code +| 2=@asp_comment +| 3=@asp_data_binding +| 4=@asp_directive +| 5=@asp_open_tag +| 6=@asp_quoted_string +| 7=@asp_text +| 8=@asp_xml_directive; + +@asp_attribute = @asp_code | @asp_data_binding | @asp_quoted_string; + +asp_elements( + unique int id: @asp_element, + int kind: int ref, + int loc: @location ref); + +asp_comment_server(unique int comment: @asp_comment ref); +asp_code_inline(unique int code: @asp_code ref); +asp_directive_attribute( + int directive: @asp_directive ref, + int index: int ref, + string name: string ref, + int value: @asp_quoted_string ref); +asp_directive_name( + unique int directive: @asp_directive ref, + string name: string ref); +asp_element_body( + unique int element: @asp_element ref, + string body: string ref); +asp_tag_attribute( + int tag: @asp_open_tag ref, + int index: int ref, + string name: string ref, + int attribute: @asp_attribute ref); +asp_tag_name( + unique int tag: @asp_open_tag ref, + string name: string ref); +asp_tag_isempty(int tag: @asp_open_tag ref); + +/* Common Intermediate Language - CIL */ + +case @cil_instruction.opcode of + 0 = @cil_nop +| 1 = @cil_break +| 2 = @cil_ldarg_0 +| 3 = @cil_ldarg_1 +| 4 = @cil_ldarg_2 +| 5 = @cil_ldarg_3 +| 6 = @cil_ldloc_0 +| 7 = @cil_ldloc_1 +| 8 = @cil_ldloc_2 +| 9 = @cil_ldloc_3 +| 10 = @cil_stloc_0 +| 11 = @cil_stloc_1 +| 12 = @cil_stloc_2 +| 13 = @cil_stloc_3 +| 14 = @cil_ldarg_s +| 15 = @cil_ldarga_s +| 16 = @cil_starg_s +| 17 = @cil_ldloc_s +| 18 = @cil_ldloca_s +| 19 = @cil_stloc_s +| 20 = @cil_ldnull +| 21 = @cil_ldc_i4_m1 +| 22 = @cil_ldc_i4_0 +| 23 = @cil_ldc_i4_1 +| 24 = @cil_ldc_i4_2 +| 25 = @cil_ldc_i4_3 +| 26 = @cil_ldc_i4_4 +| 27 = @cil_ldc_i4_5 +| 28 = @cil_ldc_i4_6 +| 29 = @cil_ldc_i4_7 +| 30 = @cil_ldc_i4_8 +| 31 = @cil_ldc_i4_s +| 32 = @cil_ldc_i4 +| 33 = @cil_ldc_i8 +| 34 = @cil_ldc_r4 +| 35 = @cil_ldc_r8 +| 37 = @cil_dup +| 38 = @cil_pop +| 39 = @cil_jmp +| 40 = @cil_call +| 41 = @cil_calli +| 42 = @cil_ret +| 43 = @cil_br_s +| 44 = @cil_brfalse_s +| 45 = @cil_brtrue_s +| 46 = @cil_beq_s +| 47 = @cil_bge_s +| 48 = @cil_bgt_s +| 49 = @cil_ble_s +| 50 = @cil_blt_s +| 51 = @cil_bne_un_s +| 52 = @cil_bge_un_s +| 53 = @cil_bgt_un_s +| 54 = @cil_ble_un_s +| 55 = @cil_blt_un_s +| 56 = @cil_br +| 57 = @cil_brfalse +| 58 = @cil_brtrue +| 59 = @cil_beq +| 60 = @cil_bge +| 61 = @cil_bgt +| 62 = @cil_ble +| 63 = @cil_blt +| 64 = @cil_bne_un +| 65 = @cil_bge_un +| 66 = @cil_bgt_un +| 67 = @cil_ble_un +| 68 = @cil_blt_un +| 69 = @cil_switch +| 70 = @cil_ldind_i1 +| 71 = @cil_ldind_u1 +| 72 = @cil_ldind_i2 +| 73 = @cil_ldind_u2 +| 74 = @cil_ldind_i4 +| 75 = @cil_ldind_u4 +| 76 = @cil_ldind_i8 +| 77 = @cil_ldind_i +| 78 = @cil_ldind_r4 +| 79 = @cil_ldind_r8 +| 80 = @cil_ldind_ref +| 81 = @cil_stind_ref +| 82 = @cil_stind_i1 +| 83 = @cil_stind_i2 +| 84 = @cil_stind_i4 +| 85 = @cil_stind_i8 +| 86 = @cil_stind_r4 +| 87 = @cil_stind_r8 +| 88 = @cil_add +| 89 = @cil_sub +| 90 = @cil_mul +| 91 = @cil_div +| 92 = @cil_div_un +| 93 = @cil_rem +| 94 = @cil_rem_un +| 95 = @cil_and +| 96 = @cil_or +| 97 = @cil_xor +| 98 = @cil_shl +| 99 = @cil_shr +| 100 = @cil_shr_un +| 101 = @cil_neg +| 102 = @cil_not +| 103 = @cil_conv_i1 +| 104 = @cil_conv_i2 +| 105 = @cil_conv_i4 +| 106 = @cil_conv_i8 +| 107 = @cil_conv_r4 +| 108 = @cil_conv_r8 +| 109 = @cil_conv_u4 +| 110 = @cil_conv_u8 +| 111 = @cil_callvirt +| 112 = @cil_cpobj +| 113 = @cil_ldobj +| 114 = @cil_ldstr +| 115 = @cil_newobj +| 116 = @cil_castclass +| 117 = @cil_isinst +| 118 = @cil_conv_r_un +| 121 = @cil_unbox +| 122 = @cil_throw +| 123 = @cil_ldfld +| 124 = @cil_ldflda +| 125 = @cil_stfld +| 126 = @cil_ldsfld +| 127 = @cil_ldsflda +| 128 = @cil_stsfld +| 129 = @cil_stobj +| 130 = @cil_conv_ovf_i1_un +| 131 = @cil_conv_ovf_i2_un +| 132 = @cil_conv_ovf_i4_un +| 133 = @cil_conv_ovf_i8_un +| 134 = @cil_conv_ovf_u1_un +| 135 = @cil_conv_ovf_u2_un +| 136 = @cil_conv_ovf_u4_un +| 137 = @cil_conv_ovf_u8_un +| 138 = @cil_conv_ovf_i_un +| 139 = @cil_conv_ovf_u_un +| 140 = @cil_box +| 141 = @cil_newarr +| 142 = @cil_ldlen +| 143 = @cil_ldelema +| 144 = @cil_ldelem_i1 +| 145 = @cil_ldelem_u1 +| 146 = @cil_ldelem_i2 +| 147 = @cil_ldelem_u2 +| 148 = @cil_ldelem_i4 +| 149 = @cil_ldelem_u4 +| 150 = @cil_ldelem_i8 +| 151 = @cil_ldelem_i +| 152 = @cil_ldelem_r4 +| 153 = @cil_ldelem_r8 +| 154 = @cil_ldelem_ref +| 155 = @cil_stelem_i +| 156 = @cil_stelem_i1 +| 157 = @cil_stelem_i2 +| 158 = @cil_stelem_i4 +| 159 = @cil_stelem_i8 +| 160 = @cil_stelem_r4 +| 161 = @cil_stelem_r8 +| 162 = @cil_stelem_ref +| 163 = @cil_ldelem +| 164 = @cil_stelem +| 165 = @cil_unbox_any +| 179 = @cil_conv_ovf_i1 +| 180 = @cil_conv_ovf_u1 +| 181 = @cil_conv_ovf_i2 +| 182 = @cil_conv_ovf_u2 +| 183 = @cil_conv_ovf_i4 +| 184 = @cil_conv_ovf_u4 +| 185 = @cil_conv_ovf_i8 +| 186 = @cil_conv_ovf_u8 +| 194 = @cil_refanyval +| 195 = @cil_ckinfinite +| 198 = @cil_mkrefany +| 208 = @cil_ldtoken +| 209 = @cil_conv_u2 +| 210 = @cil_conv_u1 +| 211 = @cil_conv_i +| 212 = @cil_conv_ovf_i +| 213 = @cil_conv_ovf_u +| 214 = @cil_add_ovf +| 215 = @cil_add_ovf_un +| 216 = @cil_mul_ovf +| 217 = @cil_mul_ovf_un +| 218 = @cil_sub_ovf +| 219 = @cil_sub_ovf_un +| 220 = @cil_endfinally +| 221 = @cil_leave +| 222 = @cil_leave_s +| 223 = @cil_stind_i +| 224 = @cil_conv_u +| 65024 = @cil_arglist +| 65025 = @cil_ceq +| 65026 = @cil_cgt +| 65027 = @cil_cgt_un +| 65028 = @cil_clt +| 65029 = @cil_clt_un +| 65030 = @cil_ldftn +| 65031 = @cil_ldvirtftn +| 65033 = @cil_ldarg +| 65034 = @cil_ldarga +| 65035 = @cil_starg +| 65036 = @cil_ldloc +| 65037 = @cil_ldloca +| 65038 = @cil_stloc +| 65039 = @cil_localloc +| 65041 = @cil_endfilter +| 65042 = @cil_unaligned +| 65043 = @cil_volatile +| 65044 = @cil_tail +| 65045 = @cil_initobj +| 65046 = @cil_constrained +| 65047 = @cil_cpblk +| 65048 = @cil_initblk +| 65050 = @cil_rethrow +| 65052 = @cil_sizeof +| 65053 = @cil_refanytype +| 65054 = @cil_readonly +; + +// CIL ignored instructions + +@cil_ignore = @cil_nop | @cil_break | @cil_volatile | @cil_unaligned; + +// CIL local/parameter/field access + +@cil_ldarg_any = @cil_ldarg_0 | @cil_ldarg_1 | @cil_ldarg_2 | @cil_ldarg_3 | @cil_ldarg_s | @cil_ldarga_s | @cil_ldarg | @cil_ldarga; +@cil_starg_any = @cil_starg | @cil_starg_s; + +@cil_ldloc_any = @cil_ldloc_0 | @cil_ldloc_1 | @cil_ldloc_2 | @cil_ldloc_3 | @cil_ldloc_s | @cil_ldloca_s | @cil_ldloc | @cil_ldloca; +@cil_stloc_any = @cil_stloc_0 | @cil_stloc_1 | @cil_stloc_2 | @cil_stloc_3 | @cil_stloc_s | @cil_stloc; + +@cil_ldfld_any = @cil_ldfld | @cil_ldsfld | @cil_ldsflda | @cil_ldflda; +@cil_stfld_any = @cil_stfld | @cil_stsfld; + +@cil_local_access = @cil_stloc_any | @cil_ldloc_any; +@cil_arg_access = @cil_starg_any | @cil_ldarg_any; +@cil_read_access = @cil_ldloc_any | @cil_ldarg_any | @cil_ldfld_any; +@cil_write_access = @cil_stloc_any | @cil_starg_any | @cil_stfld_any; + +@cil_stack_access = @cil_local_access | @cil_arg_access; +@cil_field_access = @cil_ldfld_any | @cil_stfld_any; + +@cil_access = @cil_read_access | @cil_write_access; + +// CIL constant/literal instructions + +@cil_ldc_i = @cil_ldc_i4_any | @cil_ldc_i8; + +@cil_ldc_i4_any = @cil_ldc_i4_m1 | @cil_ldc_i4_0 | @cil_ldc_i4_1 | @cil_ldc_i4_2 | @cil_ldc_i4_3 | + @cil_ldc_i4_4 | @cil_ldc_i4_5 | @cil_ldc_i4_6 | @cil_ldc_i4_7 | @cil_ldc_i4_8 | @cil_ldc_i4_s | @cil_ldc_i4; + +@cil_ldc_r = @cil_ldc_r4 | @cil_ldc_r8; + +@cil_literal = @cil_ldnull | @cil_ldc_i | @cil_ldc_r | @cil_ldstr; + +// Control flow + +@cil_conditional_jump = @cil_binary_jump | @cil_unary_jump; +@cil_binary_jump = @cil_beq_s | @cil_bge_s | @cil_bgt_s | @cil_ble_s | @cil_blt_s | + @cil_bne_un_s | @cil_bge_un_s | @cil_bgt_un_s | @cil_ble_un_s | @cil_blt_un_s | + @cil_beq | @cil_bge | @cil_bgt | @cil_ble | @cil_blt | + @cil_bne_un | @cil_bge_un | @cil_bgt_un | @cil_ble_un | @cil_blt_un; +@cil_unary_jump = @cil_brfalse_s | @cil_brtrue_s | @cil_brfalse | @cil_brtrue | @cil_switch; +@cil_unconditional_jump = @cil_br | @cil_br_s | @cil_leave_any; +@cil_leave_any = @cil_leave | @cil_leave_s; +@cil_jump = @cil_unconditional_jump | @cil_conditional_jump; + +// CIL call instructions + +@cil_call_any = @cil_jmp | @cil_call | @cil_calli | @cil_tail | @cil_callvirt | @cil_newobj; + +// CIL expression instructions + +@cil_expr = @cil_literal | @cil_binary_expr | @cil_unary_expr | @cil_call_any | @cil_read_access | + @cil_newarr | @cil_ldtoken | @cil_sizeof | + @cil_ldftn | @cil_ldvirtftn | @cil_localloc | @cil_mkrefany | @cil_refanytype | @cil_arglist | @cil_dup; + +@cil_unary_expr = + @cil_conversion_operation | @cil_unary_arithmetic_operation | @cil_unary_bitwise_operation| + @cil_ldlen | @cil_isinst | @cil_box | @cil_ldobj | @cil_castclass | @cil_unbox_any | + @cil_ldind | @cil_unbox; + +@cil_conversion_operation = + @cil_conv_i1 | @cil_conv_i2 | @cil_conv_i4 | @cil_conv_i8 | + @cil_conv_u1 | @cil_conv_u2 | @cil_conv_u4 | @cil_conv_u8 | + @cil_conv_ovf_i | @cil_conv_ovf_i_un | @cil_conv_ovf_i1 | @cil_conv_ovf_i1_un | + @cil_conv_ovf_i2 | @cil_conv_ovf_i2_un | @cil_conv_ovf_i4 | @cil_conv_ovf_i4_un | + @cil_conv_ovf_i8 | @cil_conv_ovf_i8_un | @cil_conv_ovf_u | @cil_conv_ovf_u_un | + @cil_conv_ovf_u1 | @cil_conv_ovf_u1_un | @cil_conv_ovf_u2 | @cil_conv_ovf_u2_un | + @cil_conv_ovf_u4 | @cil_conv_ovf_u4_un | @cil_conv_ovf_u8 | @cil_conv_ovf_u8_un | + @cil_conv_r4 | @cil_conv_r8 | @cil_conv_ovf_u2 | @cil_conv_ovf_u2_un | + @cil_conv_i | @cil_conv_u | @cil_conv_r_un; + +@cil_ldind = @cil_ldind_i | @cil_ldind_i1 | @cil_ldind_i2 | @cil_ldind_i4 | @cil_ldind_i8 | + @cil_ldind_r4 | @cil_ldind_r8 | @cil_ldind_ref | @cil_ldind_u1 | @cil_ldind_u2 | @cil_ldind_u4; + +@cil_stind = @cil_stind_i | @cil_stind_i1 | @cil_stind_i2 | @cil_stind_i4 | @cil_stind_i8 | + @cil_stind_r4 | @cil_stind_r8 | @cil_stind_ref; + +@cil_bitwise_operation = @cil_binary_bitwise_operation | @cil_unary_bitwise_operation; + +@cil_binary_bitwise_operation = @cil_and | @cil_or | @cil_xor | @cil_shr | @cil_shr | @cil_shr_un | @cil_shl; + +@cil_binary_arithmetic_operation = @cil_add | @cil_sub | @cil_mul | @cil_div | @cil_div_un | + @cil_rem | @cil_rem_un | @cil_add_ovf | @cil_add_ovf_un | @cil_mul_ovf | @cil_mul_ovf_un | + @cil_sub_ovf | @cil_sub_ovf_un; + +@cil_unary_bitwise_operation = @cil_not; + +@cil_binary_expr = @cil_binary_arithmetic_operation | @cil_binary_bitwise_operation | @cil_read_array | @cil_comparison_operation; + +@cil_unary_arithmetic_operation = @cil_neg; + +@cil_comparison_operation = @cil_cgt_un | @cil_ceq | @cil_cgt | @cil_clt | @cil_clt_un; + +// Elements that retrieve an address of something +@cil_read_ref = @cil_ldloca_s | @cil_ldarga_s | @cil_ldflda | @cil_ldsflda | @cil_ldelema; + +// CIL array instructions + +@cil_read_array = + @cil_ldelem | @cil_ldelema | @cil_ldelem_i1 | @cil_ldelem_ref | @cil_ldelem_i | + @cil_ldelem_i1 | @cil_ldelem_i2 | @cil_ldelem_i4 | @cil_ldelem_i8 | @cil_ldelem_r4 | + @cil_ldelem_r8 | @cil_ldelem_u1 | @cil_ldelem_u2 | @cil_ldelem_u4; + +@cil_write_array = @cil_stelem | @cil_stelem_ref | + @cil_stelem_i | @cil_stelem_i1 | @cil_stelem_i2 | @cil_stelem_i4 | @cil_stelem_i8 | + @cil_stelem_r4 | @cil_stelem_r8; + +@cil_throw_any = @cil_throw | @cil_rethrow; + +#keyset[impl, index] +cil_instruction( + unique int id: @cil_instruction, + int opcode: int ref, + int index: int ref, + int impl: @cil_method_implementation ref); + +cil_jump( + unique int instruction: @cil_jump ref, + int target: @cil_instruction ref); + +cil_access( + unique int instruction: @cil_instruction ref, + int target: @cil_accessible ref); + +cil_value( + unique int instruction: @cil_literal ref, + string value: string ref); + +#keyset[instruction, index] +cil_switch( + int instruction: @cil_switch ref, + int index: int ref, + int target: @cil_instruction ref); + +cil_instruction_location( + unique int id: @cil_instruction ref, + int loc: @location ref); + +cil_type_location( + int id: @cil_type ref, + int loc: @location ref); + +cil_method_location( + int id: @cil_method ref, + int loc: @location ref); + +@cil_namespace = @namespace; + +@cil_type_container = @cil_type | @cil_namespace | @cil_method; + +case @cil_type.kind of + 0 = @cil_valueorreftype +| 1 = @cil_typeparameter +| 2 = @cil_array_type +| 3 = @cil_pointer_type +| 4 = @cil_function_pointer_type +; + +cil_type( + unique int id: @cil_type, + string name: string ref, + int kind: int ref, + int parent: @cil_type_container ref, + int sourceDecl: @cil_type ref); + +cil_pointer_type( + unique int id: @cil_pointer_type ref, + int pointee: @cil_type ref); + +cil_array_type( + unique int id: @cil_array_type ref, + int element_type: @cil_type ref, + int rank: int ref); + +cil_function_pointer_return_type( + unique int id: @cil_function_pointer_type ref, + int return_type: @cil_type ref); + +cil_method( + unique int id: @cil_method, + string name: string ref, + int parent: @cil_type ref, + int return_type: @cil_type ref); + +cil_method_source_declaration( + unique int method: @cil_method ref, + int source: @cil_method ref); + +cil_method_implementation( + unique int id: @cil_method_implementation, + int method: @cil_method ref, + int location: @assembly ref); + +cil_implements( + int id: @cil_method ref, + int decl: @cil_method ref); + +#keyset[parent, name] +cil_field( + unique int id: @cil_field, + int parent: @cil_type ref, + string name: string ref, + int field_type: @cil_type ref); + +@cil_element = @cil_instruction | @cil_declaration | @cil_handler | @cil_attribute | @cil_namespace; +@cil_named_element = @cil_declaration | @cil_namespace; +@cil_declaration = @cil_variable | @cil_method | @cil_type | @cil_member; +@cil_accessible = @cil_declaration; +@cil_variable = @cil_field | @cil_stack_variable; +@cil_stack_variable = @cil_local_variable | @cil_parameter; +@cil_member = @cil_method | @cil_type | @cil_field | @cil_property | @cil_event; +@cil_custom_modifier_receiver = @cil_method | @cil_property | @cil_parameter | @cil_field | @cil_function_pointer_type; +@cil_parameterizable = @cil_method | @cil_function_pointer_type; +@cil_has_type_annotation = @cil_stack_variable | @cil_property | @cil_method | @cil_function_pointer_type; + +#keyset[parameterizable, index] +cil_parameter( + unique int id: @cil_parameter, + int parameterizable: @cil_parameterizable ref, + int index: int ref, + int param_type: @cil_type ref); + +cil_parameter_in(unique int id: @cil_parameter ref); +cil_parameter_out(unique int id: @cil_parameter ref); + +cil_setter(unique int prop: @cil_property ref, + int method: @cil_method ref); + +#keyset[id, modifier] +cil_custom_modifiers( + int id: @cil_custom_modifier_receiver ref, + int modifier: @cil_type ref, + int kind: int ref); // modreq: 1, modopt: 0 + +cil_type_annotation( + int id: @cil_has_type_annotation ref, + int annotation: int ref); + +cil_getter(unique int prop: @cil_property ref, + int method: @cil_method ref); + +cil_adder(unique int event: @cil_event ref, + int method: @cil_method ref); + +cil_remover(unique int event: @cil_event ref, int method: @cil_method ref); + +cil_raiser(unique int event: @cil_event ref, int method: @cil_method ref); + +cil_property( + unique int id: @cil_property, + int parent: @cil_type ref, + string name: string ref, + int property_type: @cil_type ref); + +#keyset[parent, name] +cil_event(unique int id: @cil_event, + int parent: @cil_type ref, + string name: string ref, + int event_type: @cil_type ref); + +#keyset[impl, index] +cil_local_variable( + unique int id: @cil_local_variable, + int impl: @cil_method_implementation ref, + int index: int ref, + int var_type: @cil_type ref); + +cil_function_pointer_calling_conventions( + int id: @cil_function_pointer_type ref, + int kind: int ref); + +// CIL handlers (exception handlers etc). + +case @cil_handler.kind of + 0 = @cil_catch_handler +| 1 = @cil_filter_handler +| 2 = @cil_finally_handler +| 4 = @cil_fault_handler +; + +#keyset[impl, index] +cil_handler( + unique int id: @cil_handler, + int impl: @cil_method_implementation ref, + int index: int ref, + int kind: int ref, + int try_start: @cil_instruction ref, + int try_end: @cil_instruction ref, + int handler_start: @cil_instruction ref); + +cil_handler_filter( + unique int id: @cil_handler ref, + int filter_start: @cil_instruction ref); + +cil_handler_type( + unique int id: @cil_handler ref, + int catch_type: @cil_type ref); + +@cil_controlflow_node = @cil_entry_point | @cil_instruction; + +@cil_entry_point = @cil_method_implementation | @cil_handler; + +@cil_dataflow_node = @cil_instruction | @cil_variable | @cil_method; + +cil_method_stack_size( + unique int method: @cil_method_implementation ref, + int size: int ref); + +// CIL modifiers + +cil_public(int id: @cil_member ref); +cil_private(int id: @cil_member ref); +cil_protected(int id: @cil_member ref); +cil_internal(int id: @cil_member ref); +cil_static(int id: @cil_member ref); +cil_sealed(int id: @cil_member ref); +cil_virtual(int id: @cil_method ref); +cil_abstract(int id: @cil_member ref); +cil_class(int id: @cil_type ref); +cil_interface(int id: @cil_type ref); +cil_security(int id: @cil_member ref); +cil_requiresecobject(int id: @cil_method ref); +cil_specialname(int id: @cil_method ref); +cil_newslot(int id: @cil_method ref); + +cil_base_class(unique int id: @cil_type ref, int base: @cil_type ref); +cil_base_interface(int id: @cil_type ref, int base: @cil_type ref); +cil_enum_underlying_type(unique int id: @cil_type ref, int underlying: @cil_type ref); + +#keyset[unbound, index] +cil_type_parameter( + int unbound: @cil_member ref, + int index: int ref, + int param: @cil_typeparameter ref); + +#keyset[bound, index] +cil_type_argument( + int bound: @cil_member ref, + int index: int ref, + int t: @cil_type ref); + +// CIL type parameter constraints + +cil_typeparam_covariant(int tp: @cil_typeparameter ref); +cil_typeparam_contravariant(int tp: @cil_typeparameter ref); +cil_typeparam_class(int tp: @cil_typeparameter ref); +cil_typeparam_struct(int tp: @cil_typeparameter ref); +cil_typeparam_new(int tp: @cil_typeparameter ref); +cil_typeparam_constraint(int tp: @cil_typeparameter ref, int supertype: @cil_type ref); + +// CIL attributes + +cil_attribute( + unique int attributeid: @cil_attribute, + int element: @cil_declaration ref, + int constructor: @cil_method ref); + +#keyset[attribute_id, param] +cil_attribute_named_argument( + int attribute_id: @cil_attribute ref, + string param: string ref, + string value: string ref); + +#keyset[attribute_id, index] +cil_attribute_positional_argument( + int attribute_id: @cil_attribute ref, + int index: int ref, + string value: string ref); + + +// Common .Net data model covering both C# and CIL + +// Common elements +@dotnet_element = @element | @cil_element; +@dotnet_named_element = @named_element | @cil_named_element; +@dotnet_callable = @callable | @cil_method; +@dotnet_variable = @variable | @cil_variable; +@dotnet_field = @field | @cil_field; +@dotnet_parameter = @parameter | @cil_parameter; +@dotnet_declaration = @declaration | @cil_declaration; +@dotnet_member = @member | @cil_member; +@dotnet_event = @event | @cil_event; +@dotnet_property = @property | @cil_property | @indexer; +@dotnet_parameterizable = @parameterizable | @cil_parameterizable; + +// Common types +@dotnet_type = @type | @cil_type; +@dotnet_call = @call | @cil_call_any; +@dotnet_throw = @throw_element | @cil_throw_any; +@dotnet_valueorreftype = @cil_valueorreftype | @value_or_ref_type | @cil_array_type | @void_type; +@dotnet_typeparameter = @type_parameter | @cil_typeparameter; +@dotnet_array_type = @array_type | @cil_array_type; +@dotnet_pointer_type = @pointer_type | @cil_pointer_type; +@dotnet_type_parameter = @type_parameter | @cil_typeparameter; +@dotnet_generic = @dotnet_valueorreftype | @dotnet_callable; + +// Attributes +@dotnet_attribute = @attribute | @cil_attribute; + +// Expressions +@dotnet_expr = @expr | @cil_expr; + +// Literals +@dotnet_literal = @literal_expr | @cil_literal; +@dotnet_string_literal = @string_literal_expr | @cil_ldstr; +@dotnet_int_literal = @integer_literal_expr | @cil_ldc_i; +@dotnet_float_literal = @float_literal_expr | @cil_ldc_r; +@dotnet_null_literal = @null_literal_expr | @cil_ldnull; + +@metadata_entity = @cil_method | @cil_type | @cil_field | @cil_property | @field | @property | + @callable | @value_or_ref_type | @void_type; + +#keyset[entity, location] +metadata_handle(int entity : @metadata_entity ref, int location: @assembly ref, int handle: int ref) diff --git a/csharp/upgrades/9258e9b38d85f92cee9559f2ed21e241f0c7a29e/semmlecode.csharp.dbscheme b/csharp/upgrades/9258e9b38d85f92cee9559f2ed21e241f0c7a29e/semmlecode.csharp.dbscheme new file mode 100644 index 00000000000..770f844243d --- /dev/null +++ b/csharp/upgrades/9258e9b38d85f92cee9559f2ed21e241f0c7a29e/semmlecode.csharp.dbscheme @@ -0,0 +1,2083 @@ + +/** + * An invocation of the compiler. Note that more than one file may be + * compiled per invocation. For example, this command compiles three + * source files: + * + * csc f1.cs f2.cs f3.cs + * + * The `id` simply identifies the invocation, while `cwd` is the working + * directory from which the compiler was invoked. + */ +compilations( + unique int id : @compilation, + string cwd : string ref +); + +/** + * The arguments that were passed to the extractor for a compiler + * invocation. If `id` is for the compiler invocation + * + * csc f1.cs f2.cs f3.cs + * + * then typically there will be rows for + * + * num | arg + * --- | --- + * 0 | --compiler + * 1 | *path to compiler* + * 2 | --cil + * 3 | f1.cs + * 4 | f2.cs + * 5 | f3.cs + */ +#keyset[id, num] +compilation_args( + int id : @compilation ref, + int num : int ref, + string arg : string ref +); + +/** + * The source files that are compiled by a compiler invocation. + * If `id` is for the compiler invocation + * + * csc f1.cs f2.cs f3.cs + * + * then there will be rows for + * + * num | arg + * --- | --- + * 0 | f1.cs + * 1 | f2.cs + * 2 | f3.cs + */ +#keyset[id, num] +compilation_compiling_files( + int id : @compilation ref, + int num : int ref, + int file : @file ref +); + +/** + * The references used by a compiler invocation. + * If `id` is for the compiler invocation + * + * csc f1.cs f2.cs f3.cs /r:ref1.dll /r:ref2.dll /r:ref3.dll + * + * then there will be rows for + * + * num | arg + * --- | --- + * 0 | ref1.dll + * 1 | ref2.dll + * 2 | ref3.dll + */ +#keyset[id, num] +compilation_referencing_files( + int id : @compilation ref, + int num : int ref, + int file : @file ref +); + +/** + * The time taken by the extractor for a compiler invocation. + * + * For each file `num`, there will be rows for + * + * kind | seconds + * ---- | --- + * 1 | CPU seconds used by the extractor frontend + * 2 | Elapsed seconds during the extractor frontend + * 3 | CPU seconds used by the extractor backend + * 4 | Elapsed seconds during the extractor backend + */ +#keyset[id, num, kind] +compilation_time( + int id : @compilation ref, + int num : int ref, + /* kind: + 1 = frontend_cpu_seconds + 2 = frontend_elapsed_seconds + 3 = extractor_cpu_seconds + 4 = extractor_elapsed_seconds + */ + int kind : int ref, + float seconds : float ref +); + +/** + * An error or warning generated by the extractor. + * The diagnostic message `diagnostic` was generated during compiler + * invocation `compilation`, and is the `file_number_diagnostic_number`th + * message generated while extracting the `file_number`th file of that + * invocation. + */ +#keyset[compilation, file_number, file_number_diagnostic_number] +diagnostic_for( + unique int diagnostic : @diagnostic ref, + int compilation : @compilation ref, + int file_number : int ref, + int file_number_diagnostic_number : int ref +); + +diagnostics( + unique int id: @diagnostic, + int severity: int ref, + string error_tag: string ref, + string error_message: string ref, + string full_error_message: string ref, + int location: @location_default ref +); + +extractor_messages( + unique int id: @extractor_message, + int severity: int ref, + string origin : string ref, + string text : string ref, + string entity : string ref, + int location: @location_default ref, + string stack_trace : string ref +); + +/** + * If extraction was successful, then `cpu_seconds` and + * `elapsed_seconds` are the CPU time and elapsed time (respectively) + * that extraction took for compiler invocation `id`. + */ +compilation_finished( + unique int id : @compilation ref, + float cpu_seconds : float ref, + float elapsed_seconds : float ref +); + +compilation_assembly( + unique int id : @compilation ref, + int assembly: @assembly ref +) + +/* + * External artifacts + */ + +externalDefects( + unique int id: @externalDefect, + string queryPath: string ref, + int location: @location ref, + string message: string ref, + float severity: float ref); + +externalMetrics( + unique int id: @externalMetric, + string queryPath: string ref, + int location: @location ref, + float value: float ref); + +externalData( + int id: @externalDataElement, + string path: string ref, + int column: int ref, + string value: string ref); + +snapshotDate( + unique date snapshotDate: date ref); + +sourceLocationPrefix( + string prefix: string ref); + +/* + * Duplicate code + */ + +duplicateCode( + unique int id: @duplication, + string relativePath: string ref, + int equivClass: int ref); + +similarCode( + unique int id: @similarity, + string relativePath: string ref, + int equivClass: int ref); + +@duplication_or_similarity = @duplication | @similarity + +tokens( + int id: @duplication_or_similarity ref, + int offset: int ref, + int beginLine: int ref, + int beginColumn: int ref, + int endLine: int ref, + int endColumn: int ref); + +/* + * C# dbscheme + */ + +/** ELEMENTS **/ + +@element = @declaration | @stmt | @expr | @modifier | @attribute | @namespace_declaration + | @using_directive | @type_parameter_constraints | @external_element + | @xmllocatable | @asp_element | @namespace | @preprocessor_directive; + +@declaration = @callable | @generic | @assignable | @namespace; + +@named_element = @namespace | @declaration; + +@declaration_with_accessors = @property | @indexer | @event; + +@assignable = @variable | @assignable_with_accessors | @event; + +@assignable_with_accessors = @property | @indexer; + +@external_element = @externalMetric | @externalDefect | @externalDataElement; + +@attributable = @assembly | @field | @parameter | @operator | @method | @constructor + | @destructor | @callable_accessor | @value_or_ref_type | @declaration_with_accessors + | @local_function; + +/** LOCATIONS, ASEMMBLIES, MODULES, FILES and FOLDERS **/ + +@location = @location_default | @assembly; + +locations_default( + unique int id: @location_default, + int file: @file ref, + int beginLine: int ref, + int beginColumn: int ref, + int endLine: int ref, + int endColumn: int ref); + +locations_mapped( + unique int id: @location_default ref, + int mapped_to: @location_default ref); + +@sourceline = @file | @callable | @xmllocatable; + +numlines( + int element_id: @sourceline ref, + int num_lines: int ref, + int num_code: int ref, + int num_comment: int ref); + +assemblies( + unique int id: @assembly, + int file: @file ref, + string fullname: string ref, + string name: string ref, + string version: string ref); + +/* + fromSource(0) = unknown, + fromSource(1) = from source, + fromSource(2) = from library +*/ +files( + unique int id: @file, + string name: string ref, + string simple: string ref, + string ext: string ref, + int fromSource: int ref); + +folders( + unique int id: @folder, + string name: string ref, + string simple: string ref); + +@container = @folder | @file ; + +containerparent( + int parent: @container ref, + unique int child: @container ref); + +file_extraction_mode( + unique int file: @file ref, + int mode: int ref + /* 0 = normal, 1 = standalone extractor */ + ); + +/** NAMESPACES **/ + +@type_container = @namespace | @type; + +namespaces( + unique int id: @namespace, + string name: string ref); + +namespace_declarations( + unique int id: @namespace_declaration, + int namespace_id: @namespace ref); + +namespace_declaration_location( + unique int id: @namespace_declaration ref, + int loc: @location ref); + +parent_namespace( + unique int child_id: @type_container ref, + int namespace_id: @namespace ref); + +@declaration_or_directive = @namespace_declaration | @type | @using_directive; + +parent_namespace_declaration( + int child_id: @declaration_or_directive ref, // cannot be unique because of partial classes + int namespace_id: @namespace_declaration ref); + +@using_directive = @using_namespace_directive | @using_static_directive; + +using_namespace_directives( + unique int id: @using_namespace_directive, + int namespace_id: @namespace ref); + +using_static_directives( + unique int id: @using_static_directive, + int type_id: @type_or_ref ref); + +using_directive_location( + unique int id: @using_directive ref, + int loc: @location ref); + +@preprocessor_directive = @pragma_warning | @pragma_checksum | @directive_define | @directive_undefine | @directive_warning + | @directive_error | @directive_nullable | @directive_line | @directive_region | @directive_endregion | @directive_if + | @directive_elif | @directive_else | @directive_endif; + +@conditional_directive = @directive_if | @directive_elif; +@branch_directive = @directive_if | @directive_elif | @directive_else; + +directive_ifs( + unique int id: @directive_if, + int branchTaken: int ref, /* 0: false, 1: true */ + int conditionValue: int ref); /* 0: false, 1: true */ + +directive_elifs( + unique int id: @directive_elif, + int branchTaken: int ref, /* 0: false, 1: true */ + int conditionValue: int ref, /* 0: false, 1: true */ + int parent: @directive_if ref, + int index: int ref); + +directive_elses( + unique int id: @directive_else, + int branchTaken: int ref, /* 0: false, 1: true */ + int parent: @directive_if ref, + int index: int ref); + +#keyset[id, start] +directive_endifs( + unique int id: @directive_endif, + unique int start: @directive_if ref); + +directive_define_symbols( + unique int id: @define_symbol_expr ref, + string name: string ref); + +directive_regions( + unique int id: @directive_region, + string name: string ref); + +#keyset[id, start] +directive_endregions( + unique int id: @directive_endregion, + unique int start: @directive_region ref); + +directive_lines( + unique int id: @directive_line, + int kind: int ref); /* 0: default, 1: hidden, 2: numeric */ + +directive_line_value( + unique int id: @directive_line ref, + int line: int ref); + +directive_line_file( + unique int id: @directive_line ref, + int file: @file ref +) + +directive_nullables( + unique int id: @directive_nullable, + int setting: int ref, /* 0: disable, 1: enable, 2: restore */ + int target: int ref); /* 0: none, 1: annotations, 2: warnings */ + +directive_warnings( + unique int id: @directive_warning, + string message: string ref); + +directive_errors( + unique int id: @directive_error, + string message: string ref); + +directive_undefines( + unique int id: @directive_undefine, + string name: string ref); + +directive_defines( + unique int id: @directive_define, + string name: string ref); + +pragma_checksums( + unique int id: @pragma_checksum, + int file: @file ref, + string guid: string ref, + string bytes: string ref); + +pragma_warnings( + unique int id: @pragma_warning, + int kind: int ref /* 0 = disable, 1 = restore */); + +#keyset[id, index] +pragma_warning_error_codes( + int id: @pragma_warning ref, + string errorCode: string ref, + int index: int ref); + +preprocessor_directive_location( + unique int id: @preprocessor_directive ref, + int loc: @location ref); + +preprocessor_directive_compilation( + unique int id: @preprocessor_directive ref, + int compilation: @compilation ref); + +preprocessor_directive_active( + unique int id: @preprocessor_directive ref, + int active: int ref); /* 0: false, 1: true */ + +/** TYPES **/ + +types( + unique int id: @type, + int kind: int ref, + string name: string ref); + +case @type.kind of + 1 = @bool_type +| 2 = @char_type +| 3 = @decimal_type +| 4 = @sbyte_type +| 5 = @short_type +| 6 = @int_type +| 7 = @long_type +| 8 = @byte_type +| 9 = @ushort_type +| 10 = @uint_type +| 11 = @ulong_type +| 12 = @float_type +| 13 = @double_type +| 14 = @enum_type +| 15 = @struct_type +| 17 = @class_type +| 19 = @interface_type +| 20 = @delegate_type +| 21 = @null_type +| 22 = @type_parameter +| 23 = @pointer_type +| 24 = @nullable_type +| 25 = @array_type +| 26 = @void_type +| 27 = @int_ptr_type +| 28 = @uint_ptr_type +| 29 = @dynamic_type +| 30 = @arglist_type +| 31 = @unknown_type +| 32 = @tuple_type +| 33 = @function_pointer_type + ; + +@simple_type = @bool_type | @char_type | @integral_type | @floating_point_type | @decimal_type; +@integral_type = @signed_integral_type | @unsigned_integral_type; +@signed_integral_type = @sbyte_type | @short_type | @int_type | @long_type; +@unsigned_integral_type = @byte_type | @ushort_type | @uint_type | @ulong_type; +@floating_point_type = @float_type | @double_type; +@value_type = @simple_type | @enum_type | @struct_type | @nullable_type | @int_ptr_type + | @uint_ptr_type | @tuple_type; +@ref_type = @class_type | @interface_type | @array_type | @delegate_type | @null_type + | @dynamic_type; +@value_or_ref_type = @value_type | @ref_type; + +typerefs( + unique int id: @typeref, + string name: string ref); + +typeref_type( + int id: @typeref ref, + unique int typeId: @type ref); + +@type_or_ref = @type | @typeref; + +array_element_type( + unique int array: @array_type ref, + int dimension: int ref, + int rank: int ref, + int element: @type_or_ref ref); + +nullable_underlying_type( + unique int nullable: @nullable_type ref, + int underlying: @type_or_ref ref); + +pointer_referent_type( + unique int pointer: @pointer_type ref, + int referent: @type_or_ref ref); + +enum_underlying_type( + unique int enum_id: @enum_type ref, + int underlying_type_id: @type_or_ref ref); + +delegate_return_type( + unique int delegate_id: @delegate_type ref, + int return_type_id: @type_or_ref ref); + +function_pointer_return_type( + unique int function_pointer_id: @function_pointer_type ref, + int return_type_id: @type_or_ref ref); + +extend( + int sub: @type ref, + int super: @type_or_ref ref); + +anonymous_types( + unique int id: @type ref); + +@interface_or_ref = @interface_type | @typeref; + +implement( + int sub: @type ref, + int super: @type_or_ref ref); + +type_location( + int id: @type ref, + int loc: @location ref); + +tuple_underlying_type( + unique int tuple: @tuple_type ref, + int struct: @type_or_ref ref); + +#keyset[tuple, index] +tuple_element( + int tuple: @tuple_type ref, + int index: int ref, + unique int field: @field ref); + +attributes( + unique int id: @attribute, + int type_id: @type_or_ref ref, + int target: @attributable ref); + +attribute_location( + int id: @attribute ref, + int loc: @location ref); + +@type_mention_parent = @element | @type_mention; + +type_mention( + unique int id: @type_mention, + int type_id: @type_or_ref ref, + int parent: @type_mention_parent ref); + +type_mention_location( + unique int id: @type_mention ref, + int loc: @location ref); + +@has_type_annotation = @assignable | @type_parameter | @callable | @expr | @delegate_type | @generic | @function_pointer_type; + +/** + * A direct annotation on an entity, for example `string? x;`. + * + * Annotations: + * 2 = reftype is not annotated "!" + * 3 = reftype is annotated "?" + * 4 = readonly ref type / in parameter + * 5 = ref type parameter, return or local variable + * 6 = out parameter + * + * Note that the annotation depends on the element it annotates. + * @assignable: The annotation is on the type of the assignable, for example the variable type. + * @type_parameter: The annotation is on the reftype constraint + * @callable: The annotation is on the return type + * @array_type: The annotation is on the element type + */ +type_annotation(int id: @has_type_annotation ref, int annotation: int ref); + +nullability(unique int nullability: @nullability, int kind: int ref); + +case @nullability.kind of + 0 = @oblivious +| 1 = @not_annotated +| 2 = @annotated +; + +#keyset[parent, index] +nullability_parent(int nullability: @nullability ref, int index: int ref, int parent: @nullability ref) + +type_nullability(int id: @has_type_annotation ref, int nullability: @nullability ref); + +/** + * The nullable flow state of an expression, as determined by Roslyn. + * 0 = none (default, not populated) + * 1 = not null + * 2 = maybe null + */ +expr_flowstate(unique int id: @expr ref, int state: int ref); + +/** GENERICS **/ + +@generic = @type | @method | @local_function; + +type_parameters( + unique int id: @type_parameter ref, + int index: int ref, + int generic_id: @generic ref, + int variance: int ref /* none = 0, out = 1, in = 2 */); + +#keyset[constructed_id, index] +type_arguments( + int id: @type_or_ref ref, + int index: int ref, + int constructed_id: @generic_or_ref ref); + +@generic_or_ref = @generic | @typeref; + +constructed_generic( + unique int constructed: @generic ref, + int generic: @generic_or_ref ref); + +type_parameter_constraints( + unique int id: @type_parameter_constraints, + int param_id: @type_parameter ref); + +type_parameter_constraints_location( + int id: @type_parameter_constraints ref, + int loc: @location ref); + +general_type_parameter_constraints( + int id: @type_parameter_constraints ref, + int kind: int ref /* class = 1, struct = 2, new = 3 */); + +specific_type_parameter_constraints( + int id: @type_parameter_constraints ref, + int base_id: @type_or_ref ref); + +specific_type_parameter_nullability( + int id: @type_parameter_constraints ref, + int base_id: @type_or_ref ref, + int nullability: @nullability ref); + +/** FUNCTION POINTERS */ + +function_pointer_calling_conventions( + int id: @function_pointer_type ref, + int kind: int ref); + +#keyset[id, index] +has_unmanaged_calling_conventions( + int id: @function_pointer_type ref, + int index: int ref, + int conv_id: @type_or_ref ref); + +/** MODIFIERS */ + +@modifiable = @modifiable_direct | @event_accessor; + +@modifiable_direct = @member | @accessor | @local_function | @anonymous_function_expr; + +modifiers( + unique int id: @modifier, + string name: string ref); + +has_modifiers( + int id: @modifiable_direct ref, + int mod_id: @modifier ref); + +compiler_generated(unique int id: @modifiable_direct ref); + +/** MEMBERS **/ + +@member = @method | @constructor | @destructor | @field | @property | @event | @operator | @indexer | @type; + +@named_exprorstmt = @goto_stmt | @labeled_stmt | @expr; + +@virtualizable = @method | @property | @indexer | @event; + +exprorstmt_name( + unique int parent_id: @named_exprorstmt ref, + string name: string ref); + +nested_types( + unique int id: @type ref, + int declaring_type_id: @type ref, + int unbound_id: @type ref); + +properties( + unique int id: @property, + string name: string ref, + int declaring_type_id: @type ref, + int type_id: @type_or_ref ref, + int unbound_id: @property ref); + +property_location( + int id: @property ref, + int loc: @location ref); + +indexers( + unique int id: @indexer, + string name: string ref, + int declaring_type_id: @type ref, + int type_id: @type_or_ref ref, + int unbound_id: @indexer ref); + +indexer_location( + int id: @indexer ref, + int loc: @location ref); + +accessors( + unique int id: @accessor, + int kind: int ref, + string name: string ref, + int declaring_member_id: @member ref, + int unbound_id: @accessor ref); + +case @accessor.kind of + 1 = @getter +| 2 = @setter + ; + +init_only_accessors( + unique int id: @accessor ref); + +accessor_location( + int id: @accessor ref, + int loc: @location ref); + +events( + unique int id: @event, + string name: string ref, + int declaring_type_id: @type ref, + int type_id: @type_or_ref ref, + int unbound_id: @event ref); + +event_location( + int id: @event ref, + int loc: @location ref); + +event_accessors( + unique int id: @event_accessor, + int kind: int ref, + string name: string ref, + int declaring_event_id: @event ref, + int unbound_id: @event_accessor ref); + +case @event_accessor.kind of + 1 = @add_event_accessor +| 2 = @remove_event_accessor + ; + +event_accessor_location( + int id: @event_accessor ref, + int loc: @location ref); + +operators( + unique int id: @operator, + string name: string ref, + string symbol: string ref, + int declaring_type_id: @type ref, + int type_id: @type_or_ref ref, + int unbound_id: @operator ref); + +operator_location( + int id: @operator ref, + int loc: @location ref); + +constant_value( + int id: @variable ref, + string value: string ref); + +/** CALLABLES **/ + +@callable = @method | @constructor | @destructor | @operator | @callable_accessor | @anonymous_function_expr | @local_function; + +@callable_accessor = @accessor | @event_accessor; + +methods( + unique int id: @method, + string name: string ref, + int declaring_type_id: @type ref, + int type_id: @type_or_ref ref, + int unbound_id: @method ref); + +method_location( + int id: @method ref, + int loc: @location ref); + +constructors( + unique int id: @constructor, + string name: string ref, + int declaring_type_id: @type ref, + int unbound_id: @constructor ref); + +constructor_location( + int id: @constructor ref, + int loc: @location ref); + +destructors( + unique int id: @destructor, + string name: string ref, + int declaring_type_id: @type ref, + int unbound_id: @destructor ref); + +destructor_location( + int id: @destructor ref, + int loc: @location ref); + +overrides( + int id: @callable ref, + int base_id: @callable ref); + +explicitly_implements( + int id: @member ref, + int interface_id: @interface_or_ref ref); + +local_functions( + unique int id: @local_function, + string name: string ref, + int return_type: @type ref, + int unbound_id: @local_function ref); + +local_function_stmts( + unique int fn: @local_function_stmt ref, + int stmt: @local_function ref); + +/** VARIABLES **/ + +@variable = @local_scope_variable | @field; + +@local_scope_variable = @local_variable | @parameter; + +fields( + unique int id: @field, + int kind: int ref, + string name: string ref, + int declaring_type_id: @type ref, + int type_id: @type_or_ref ref, + int unbound_id: @field ref); + +case @field.kind of + 1 = @addressable_field +| 2 = @constant + ; + +field_location( + int id: @field ref, + int loc: @location ref); + +localvars( + unique int id: @local_variable, + int kind: int ref, + string name: string ref, + int implicitly_typed: int ref /* 0 = no, 1 = yes */, + int type_id: @type_or_ref ref, + int parent_id: @local_var_decl_expr ref); + +case @local_variable.kind of + 1 = @addressable_local_variable +| 2 = @local_constant +| 3 = @local_variable_ref + ; + +localvar_location( + unique int id: @local_variable ref, + int loc: @location ref); + +@parameterizable = @callable | @delegate_type | @indexer | @function_pointer_type; + +#keyset[name, parent_id] +#keyset[index, parent_id] +params( + unique int id: @parameter, + string name: string ref, + int type_id: @type_or_ref ref, + int index: int ref, + int mode: int ref, /* value = 0, ref = 1, out = 2, array = 3, this = 4 */ + int parent_id: @parameterizable ref, + int unbound_id: @parameter ref); + +param_location( + int id: @parameter ref, + int loc: @location ref); + +/** STATEMENTS **/ + +@exprorstmt_parent = @control_flow_element | @top_level_exprorstmt_parent; + +statements( + unique int id: @stmt, + int kind: int ref); + +#keyset[index, parent] +stmt_parent( + unique int stmt: @stmt ref, + int index: int ref, + int parent: @control_flow_element ref); + +@top_level_stmt_parent = @callable; + +// [index, parent] is not a keyset because the same parent may be compiled multiple times +stmt_parent_top_level( + unique int stmt: @stmt ref, + int index: int ref, + int parent: @top_level_stmt_parent ref); + +case @stmt.kind of + 1 = @block_stmt +| 2 = @expr_stmt +| 3 = @if_stmt +| 4 = @switch_stmt +| 5 = @while_stmt +| 6 = @do_stmt +| 7 = @for_stmt +| 8 = @foreach_stmt +| 9 = @break_stmt +| 10 = @continue_stmt +| 11 = @goto_stmt +| 12 = @goto_case_stmt +| 13 = @goto_default_stmt +| 14 = @throw_stmt +| 15 = @return_stmt +| 16 = @yield_stmt +| 17 = @try_stmt +| 18 = @checked_stmt +| 19 = @unchecked_stmt +| 20 = @lock_stmt +| 21 = @using_block_stmt +| 22 = @var_decl_stmt +| 23 = @const_decl_stmt +| 24 = @empty_stmt +| 25 = @unsafe_stmt +| 26 = @fixed_stmt +| 27 = @label_stmt +| 28 = @catch +| 29 = @case_stmt +| 30 = @local_function_stmt +| 31 = @using_decl_stmt + ; + +@using_stmt = @using_block_stmt | @using_decl_stmt; + +@labeled_stmt = @label_stmt | @case; + +@decl_stmt = @var_decl_stmt | @const_decl_stmt | @using_decl_stmt; + +@cond_stmt = @if_stmt | @switch_stmt; + +@loop_stmt = @while_stmt | @do_stmt | @for_stmt | @foreach_stmt; + +@jump_stmt = @break_stmt | @goto_any_stmt | @continue_stmt | @throw_stmt | @return_stmt + | @yield_stmt; + +@goto_any_stmt = @goto_default_stmt | @goto_case_stmt | @goto_stmt; + + +stmt_location( + unique int id: @stmt ref, + int loc: @location ref); + +catch_type( + unique int catch_id: @catch ref, + int type_id: @type_or_ref ref, + int kind: int ref /* explicit = 1, implicit = 2 */); + +foreach_stmt_info( + unique int id: @foreach_stmt ref, + int kind: int ref /* non-async = 1, async = 2 */); + +@foreach_symbol = @method | @property | @type_or_ref; + +#keyset[id, kind] +foreach_stmt_desugar( + int id: @foreach_stmt ref, + int symbol: @foreach_symbol ref, + int kind: int ref /* GetEnumeratorMethod = 1, CurrentProperty = 2, MoveNextMethod = 3, DisposeMethod = 4, ElementType = 5 */); + +/** EXPRESSIONS **/ + +expressions( + unique int id: @expr, + int kind: int ref, + int type_id: @type_or_ref ref); + +#keyset[index, parent] +expr_parent( + unique int expr: @expr ref, + int index: int ref, + int parent: @control_flow_element ref); + +@top_level_expr_parent = @attribute | @field | @property | @indexer | @parameter | @directive_if | @directive_elif; + +@top_level_exprorstmt_parent = @top_level_expr_parent | @top_level_stmt_parent; + +// [index, parent] is not a keyset because the same parent may be compiled multiple times +expr_parent_top_level( + unique int expr: @expr ref, + int index: int ref, + int parent: @top_level_exprorstmt_parent ref); + +case @expr.kind of +/* literal */ + 1 = @bool_literal_expr +| 2 = @char_literal_expr +| 3 = @decimal_literal_expr +| 4 = @int_literal_expr +| 5 = @long_literal_expr +| 6 = @uint_literal_expr +| 7 = @ulong_literal_expr +| 8 = @float_literal_expr +| 9 = @double_literal_expr +| 10 = @string_literal_expr +| 11 = @null_literal_expr +/* primary & unary */ +| 12 = @this_access_expr +| 13 = @base_access_expr +| 14 = @local_variable_access_expr +| 15 = @parameter_access_expr +| 16 = @field_access_expr +| 17 = @property_access_expr +| 18 = @method_access_expr +| 19 = @event_access_expr +| 20 = @indexer_access_expr +| 21 = @array_access_expr +| 22 = @type_access_expr +| 23 = @typeof_expr +| 24 = @method_invocation_expr +| 25 = @delegate_invocation_expr +| 26 = @operator_invocation_expr +| 27 = @cast_expr +| 28 = @object_creation_expr +| 29 = @explicit_delegate_creation_expr +| 30 = @implicit_delegate_creation_expr +| 31 = @array_creation_expr +| 32 = @default_expr +| 33 = @plus_expr +| 34 = @minus_expr +| 35 = @bit_not_expr +| 36 = @log_not_expr +| 37 = @post_incr_expr +| 38 = @post_decr_expr +| 39 = @pre_incr_expr +| 40 = @pre_decr_expr +/* multiplicative */ +| 41 = @mul_expr +| 42 = @div_expr +| 43 = @rem_expr +/* additive */ +| 44 = @add_expr +| 45 = @sub_expr +/* shift */ +| 46 = @lshift_expr +| 47 = @rshift_expr +/* relational */ +| 48 = @lt_expr +| 49 = @gt_expr +| 50 = @le_expr +| 51 = @ge_expr +/* equality */ +| 52 = @eq_expr +| 53 = @ne_expr +/* logical */ +| 54 = @bit_and_expr +| 55 = @bit_xor_expr +| 56 = @bit_or_expr +| 57 = @log_and_expr +| 58 = @log_or_expr +/* type testing */ +| 59 = @is_expr +| 60 = @as_expr +/* null coalescing */ +| 61 = @null_coalescing_expr +/* conditional */ +| 62 = @conditional_expr +/* assignment */ +| 63 = @simple_assign_expr +| 64 = @assign_add_expr +| 65 = @assign_sub_expr +| 66 = @assign_mul_expr +| 67 = @assign_div_expr +| 68 = @assign_rem_expr +| 69 = @assign_and_expr +| 70 = @assign_xor_expr +| 71 = @assign_or_expr +| 72 = @assign_lshift_expr +| 73 = @assign_rshift_expr +/* more */ +| 74 = @object_init_expr +| 75 = @collection_init_expr +| 76 = @array_init_expr +| 77 = @checked_expr +| 78 = @unchecked_expr +| 79 = @constructor_init_expr +| 80 = @add_event_expr +| 81 = @remove_event_expr +| 82 = @par_expr +| 83 = @local_var_decl_expr +| 84 = @lambda_expr +| 85 = @anonymous_method_expr +| 86 = @namespace_expr +/* dynamic */ +| 92 = @dynamic_element_access_expr +| 93 = @dynamic_member_access_expr +/* unsafe */ +| 100 = @pointer_indirection_expr +| 101 = @address_of_expr +| 102 = @sizeof_expr +/* async */ +| 103 = @await_expr +/* C# 6.0 */ +| 104 = @nameof_expr +| 105 = @interpolated_string_expr +| 106 = @unknown_expr +/* C# 7.0 */ +| 107 = @throw_expr +| 108 = @tuple_expr +| 109 = @local_function_invocation_expr +| 110 = @ref_expr +| 111 = @discard_expr +/* C# 8.0 */ +| 112 = @range_expr +| 113 = @index_expr +| 114 = @switch_expr +| 115 = @recursive_pattern_expr +| 116 = @property_pattern_expr +| 117 = @positional_pattern_expr +| 118 = @switch_case_expr +| 119 = @assign_coalesce_expr +| 120 = @suppress_nullable_warning_expr +| 121 = @namespace_access_expr +/* C# 9.0 */ +| 122 = @lt_pattern_expr +| 123 = @gt_pattern_expr +| 124 = @le_pattern_expr +| 125 = @ge_pattern_expr +| 126 = @not_pattern_expr +| 127 = @and_pattern_expr +| 128 = @or_pattern_expr +| 129 = @function_pointer_invocation_expr +| 130 = @with_expr +/* Preprocessor */ +| 999 = @define_symbol_expr +; + +@switch = @switch_stmt | @switch_expr; +@case = @case_stmt | @switch_case_expr; +@pattern_match = @case | @is_expr; +@unary_pattern_expr = @not_pattern_expr; +@relational_pattern_expr = @gt_pattern_expr | @lt_pattern_expr | @ge_pattern_expr | @le_pattern_expr; +@binary_pattern_expr = @and_pattern_expr | @or_pattern_expr; + +@integer_literal_expr = @int_literal_expr | @long_literal_expr | @uint_literal_expr | @ulong_literal_expr; +@real_literal_expr = @float_literal_expr | @double_literal_expr | @decimal_literal_expr; +@literal_expr = @bool_literal_expr | @char_literal_expr | @integer_literal_expr | @real_literal_expr + | @string_literal_expr | @null_literal_expr; + +@assign_expr = @simple_assign_expr | @assign_op_expr | @local_var_decl_expr; +@assign_op_expr = @assign_arith_expr | @assign_bitwise_expr | @assign_event_expr | @assign_coalesce_expr; +@assign_event_expr = @add_event_expr | @remove_event_expr; + +@assign_arith_expr = @assign_add_expr | @assign_sub_expr | @assign_mul_expr | @assign_div_expr + | @assign_rem_expr +@assign_bitwise_expr = @assign_and_expr | @assign_or_expr | @assign_xor_expr + | @assign_lshift_expr | @assign_rshift_expr; + +@member_access_expr = @field_access_expr | @property_access_expr | @indexer_access_expr | @event_access_expr + | @method_access_expr | @type_access_expr | @dynamic_member_access_expr; +@access_expr = @member_access_expr | @this_access_expr | @base_access_expr | @assignable_access_expr | @namespace_access_expr; +@element_access_expr = @indexer_access_expr | @array_access_expr | @dynamic_element_access_expr; + +@local_variable_access = @local_variable_access_expr | @local_var_decl_expr; +@local_scope_variable_access_expr = @parameter_access_expr | @local_variable_access; +@variable_access_expr = @local_scope_variable_access_expr | @field_access_expr; + +@assignable_access_expr = @variable_access_expr | @property_access_expr | @element_access_expr + | @event_access_expr | @dynamic_member_access_expr; + +@objectorcollection_init_expr = @object_init_expr | @collection_init_expr; + +@delegate_creation_expr = @explicit_delegate_creation_expr | @implicit_delegate_creation_expr; + +@bin_arith_op_expr = @mul_expr | @div_expr | @rem_expr | @add_expr | @sub_expr; +@incr_op_expr = @pre_incr_expr | @post_incr_expr; +@decr_op_expr = @pre_decr_expr | @post_decr_expr; +@mut_op_expr = @incr_op_expr | @decr_op_expr; +@un_arith_op_expr = @plus_expr | @minus_expr | @mut_op_expr; +@arith_op_expr = @bin_arith_op_expr | @un_arith_op_expr; + +@ternary_log_op_expr = @conditional_expr; +@bin_log_op_expr = @log_and_expr | @log_or_expr | @null_coalescing_expr; +@un_log_op_expr = @log_not_expr; +@log_expr = @un_log_op_expr | @bin_log_op_expr | @ternary_log_op_expr; + +@bin_bit_op_expr = @bit_and_expr | @bit_or_expr | @bit_xor_expr | @lshift_expr + | @rshift_expr; +@un_bit_op_expr = @bit_not_expr; +@bit_expr = @un_bit_op_expr | @bin_bit_op_expr; + +@equality_op_expr = @eq_expr | @ne_expr; +@rel_op_expr = @gt_expr | @lt_expr| @ge_expr | @le_expr; +@comp_expr = @equality_op_expr | @rel_op_expr; + +@op_expr = @assign_expr | @un_op | @bin_op | @ternary_op; + +@ternary_op = @ternary_log_op_expr; +@bin_op = @bin_arith_op_expr | @bin_log_op_expr | @bin_bit_op_expr | @comp_expr; +@un_op = @un_arith_op_expr | @un_log_op_expr | @un_bit_op_expr | @sizeof_expr + | @pointer_indirection_expr | @address_of_expr; + +@anonymous_function_expr = @lambda_expr | @anonymous_method_expr; + +@call = @method_invocation_expr | @constructor_init_expr | @operator_invocation_expr + | @delegate_invocation_expr | @object_creation_expr | @call_access_expr + | @local_function_invocation_expr | @function_pointer_invocation_expr; + +@call_access_expr = @property_access_expr | @event_access_expr | @indexer_access_expr; + +@late_bindable_expr = @dynamic_element_access_expr | @dynamic_member_access_expr + | @object_creation_expr | @method_invocation_expr | @operator_invocation_expr; + +@throw_element = @throw_expr | @throw_stmt; + +@implicitly_typeable_object_creation_expr = @object_creation_expr | @explicit_delegate_creation_expr; + +implicitly_typed_array_creation( + unique int id: @array_creation_expr ref); + +explicitly_sized_array_creation( + unique int id: @array_creation_expr ref); + +stackalloc_array_creation( + unique int id: @array_creation_expr ref); + +implicitly_typed_object_creation( + unique int id: @implicitly_typeable_object_creation_expr ref); + +mutator_invocation_mode( + unique int id: @operator_invocation_expr ref, + int mode: int ref /* prefix = 1, postfix = 2*/); + +expr_compiler_generated( + unique int id: @expr ref); + +expr_value( + unique int id: @expr ref, + string value: string ref); + +expr_call( + unique int caller_id: @expr ref, + int target_id: @callable ref); + +expr_access( + unique int accesser_id: @access_expr ref, + int target_id: @accessible ref); + +@accessible = @method | @assignable | @local_function | @namespace; + +expr_location( + unique int id: @expr ref, + int loc: @location ref); + +dynamic_member_name( + unique int id: @late_bindable_expr ref, + string name: string ref); + +@qualifiable_expr = @member_access_expr + | @method_invocation_expr + | @element_access_expr; + +conditional_access( + unique int id: @qualifiable_expr ref); + +expr_argument( + unique int id: @expr ref, + int mode: int ref); + /* mode is the same as params: value = 0, ref = 1, out = 2 */ + +expr_argument_name( + unique int id: @expr ref, + string name: string ref); + +/** CONTROL/DATA FLOW **/ + +@control_flow_element = @stmt | @expr; + +/* XML Files */ + +xmlEncoding ( + unique int id: @file ref, + string encoding: string ref); + +xmlDTDs( + unique int id: @xmldtd, + string root: string ref, + string publicId: string ref, + string systemId: string ref, + int fileid: @file ref); + +xmlElements( + unique int id: @xmlelement, + string name: string ref, + int parentid: @xmlparent ref, + int idx: int ref, + int fileid: @file ref); + +xmlAttrs( + unique int id: @xmlattribute, + int elementid: @xmlelement ref, + string name: string ref, + string value: string ref, + int idx: int ref, + int fileid: @file ref); + +xmlNs( + int id: @xmlnamespace, + string prefixName: string ref, + string URI: string ref, + int fileid: @file ref); + +xmlHasNs( + int elementId: @xmlnamespaceable ref, + int nsId: @xmlnamespace ref, + int fileid: @file ref); + +xmlComments( + unique int id: @xmlcomment, + string text: string ref, + int parentid: @xmlparent ref, + int fileid: @file ref); + +xmlChars( + unique int id: @xmlcharacters, + string text: string ref, + int parentid: @xmlparent ref, + int idx: int ref, + int isCDATA: int ref, + int fileid: @file ref); + +@xmlparent = @file | @xmlelement; +@xmlnamespaceable = @xmlelement | @xmlattribute; + +xmllocations( + int xmlElement: @xmllocatable ref, + int location: @location_default ref); + +@xmllocatable = @xmlcharacters | @xmlelement | @xmlcomment | @xmlattribute | @xmldtd | @file | @xmlnamespace; + +/* Comments */ + +commentline( + unique int id: @commentline, + int kind: int ref, + string text: string ref, + string rawtext: string ref); + +case @commentline.kind of + 0 = @singlelinecomment +| 1 = @xmldoccomment +| 2 = @multilinecomment; + +commentline_location( + unique int id: @commentline ref, + int loc: @location ref); + +commentblock( + unique int id : @commentblock); + +commentblock_location( + unique int id: @commentblock ref, + int loc: @location ref); + +commentblock_binding( + int id: @commentblock ref, + int entity: @element ref, + int bindtype: int ref); /* 0: Parent, 1: Best, 2: Before, 3: After */ + +commentblock_child( + int id: @commentblock ref, + int commentline: @commentline ref, + int index: int ref); + +/* ASP.NET */ + +case @asp_element.kind of + 0=@asp_close_tag +| 1=@asp_code +| 2=@asp_comment +| 3=@asp_data_binding +| 4=@asp_directive +| 5=@asp_open_tag +| 6=@asp_quoted_string +| 7=@asp_text +| 8=@asp_xml_directive; + +@asp_attribute = @asp_code | @asp_data_binding | @asp_quoted_string; + +asp_elements( + unique int id: @asp_element, + int kind: int ref, + int loc: @location ref); + +asp_comment_server(unique int comment: @asp_comment ref); +asp_code_inline(unique int code: @asp_code ref); +asp_directive_attribute( + int directive: @asp_directive ref, + int index: int ref, + string name: string ref, + int value: @asp_quoted_string ref); +asp_directive_name( + unique int directive: @asp_directive ref, + string name: string ref); +asp_element_body( + unique int element: @asp_element ref, + string body: string ref); +asp_tag_attribute( + int tag: @asp_open_tag ref, + int index: int ref, + string name: string ref, + int attribute: @asp_attribute ref); +asp_tag_name( + unique int tag: @asp_open_tag ref, + string name: string ref); +asp_tag_isempty(int tag: @asp_open_tag ref); + +/* Common Intermediate Language - CIL */ + +case @cil_instruction.opcode of + 0 = @cil_nop +| 1 = @cil_break +| 2 = @cil_ldarg_0 +| 3 = @cil_ldarg_1 +| 4 = @cil_ldarg_2 +| 5 = @cil_ldarg_3 +| 6 = @cil_ldloc_0 +| 7 = @cil_ldloc_1 +| 8 = @cil_ldloc_2 +| 9 = @cil_ldloc_3 +| 10 = @cil_stloc_0 +| 11 = @cil_stloc_1 +| 12 = @cil_stloc_2 +| 13 = @cil_stloc_3 +| 14 = @cil_ldarg_s +| 15 = @cil_ldarga_s +| 16 = @cil_starg_s +| 17 = @cil_ldloc_s +| 18 = @cil_ldloca_s +| 19 = @cil_stloc_s +| 20 = @cil_ldnull +| 21 = @cil_ldc_i4_m1 +| 22 = @cil_ldc_i4_0 +| 23 = @cil_ldc_i4_1 +| 24 = @cil_ldc_i4_2 +| 25 = @cil_ldc_i4_3 +| 26 = @cil_ldc_i4_4 +| 27 = @cil_ldc_i4_5 +| 28 = @cil_ldc_i4_6 +| 29 = @cil_ldc_i4_7 +| 30 = @cil_ldc_i4_8 +| 31 = @cil_ldc_i4_s +| 32 = @cil_ldc_i4 +| 33 = @cil_ldc_i8 +| 34 = @cil_ldc_r4 +| 35 = @cil_ldc_r8 +| 37 = @cil_dup +| 38 = @cil_pop +| 39 = @cil_jmp +| 40 = @cil_call +| 41 = @cil_calli +| 42 = @cil_ret +| 43 = @cil_br_s +| 44 = @cil_brfalse_s +| 45 = @cil_brtrue_s +| 46 = @cil_beq_s +| 47 = @cil_bge_s +| 48 = @cil_bgt_s +| 49 = @cil_ble_s +| 50 = @cil_blt_s +| 51 = @cil_bne_un_s +| 52 = @cil_bge_un_s +| 53 = @cil_bgt_un_s +| 54 = @cil_ble_un_s +| 55 = @cil_blt_un_s +| 56 = @cil_br +| 57 = @cil_brfalse +| 58 = @cil_brtrue +| 59 = @cil_beq +| 60 = @cil_bge +| 61 = @cil_bgt +| 62 = @cil_ble +| 63 = @cil_blt +| 64 = @cil_bne_un +| 65 = @cil_bge_un +| 66 = @cil_bgt_un +| 67 = @cil_ble_un +| 68 = @cil_blt_un +| 69 = @cil_switch +| 70 = @cil_ldind_i1 +| 71 = @cil_ldind_u1 +| 72 = @cil_ldind_i2 +| 73 = @cil_ldind_u2 +| 74 = @cil_ldind_i4 +| 75 = @cil_ldind_u4 +| 76 = @cil_ldind_i8 +| 77 = @cil_ldind_i +| 78 = @cil_ldind_r4 +| 79 = @cil_ldind_r8 +| 80 = @cil_ldind_ref +| 81 = @cil_stind_ref +| 82 = @cil_stind_i1 +| 83 = @cil_stind_i2 +| 84 = @cil_stind_i4 +| 85 = @cil_stind_i8 +| 86 = @cil_stind_r4 +| 87 = @cil_stind_r8 +| 88 = @cil_add +| 89 = @cil_sub +| 90 = @cil_mul +| 91 = @cil_div +| 92 = @cil_div_un +| 93 = @cil_rem +| 94 = @cil_rem_un +| 95 = @cil_and +| 96 = @cil_or +| 97 = @cil_xor +| 98 = @cil_shl +| 99 = @cil_shr +| 100 = @cil_shr_un +| 101 = @cil_neg +| 102 = @cil_not +| 103 = @cil_conv_i1 +| 104 = @cil_conv_i2 +| 105 = @cil_conv_i4 +| 106 = @cil_conv_i8 +| 107 = @cil_conv_r4 +| 108 = @cil_conv_r8 +| 109 = @cil_conv_u4 +| 110 = @cil_conv_u8 +| 111 = @cil_callvirt +| 112 = @cil_cpobj +| 113 = @cil_ldobj +| 114 = @cil_ldstr +| 115 = @cil_newobj +| 116 = @cil_castclass +| 117 = @cil_isinst +| 118 = @cil_conv_r_un +| 121 = @cil_unbox +| 122 = @cil_throw +| 123 = @cil_ldfld +| 124 = @cil_ldflda +| 125 = @cil_stfld +| 126 = @cil_ldsfld +| 127 = @cil_ldsflda +| 128 = @cil_stsfld +| 129 = @cil_stobj +| 130 = @cil_conv_ovf_i1_un +| 131 = @cil_conv_ovf_i2_un +| 132 = @cil_conv_ovf_i4_un +| 133 = @cil_conv_ovf_i8_un +| 134 = @cil_conv_ovf_u1_un +| 135 = @cil_conv_ovf_u2_un +| 136 = @cil_conv_ovf_u4_un +| 137 = @cil_conv_ovf_u8_un +| 138 = @cil_conv_ovf_i_un +| 139 = @cil_conv_ovf_u_un +| 140 = @cil_box +| 141 = @cil_newarr +| 142 = @cil_ldlen +| 143 = @cil_ldelema +| 144 = @cil_ldelem_i1 +| 145 = @cil_ldelem_u1 +| 146 = @cil_ldelem_i2 +| 147 = @cil_ldelem_u2 +| 148 = @cil_ldelem_i4 +| 149 = @cil_ldelem_u4 +| 150 = @cil_ldelem_i8 +| 151 = @cil_ldelem_i +| 152 = @cil_ldelem_r4 +| 153 = @cil_ldelem_r8 +| 154 = @cil_ldelem_ref +| 155 = @cil_stelem_i +| 156 = @cil_stelem_i1 +| 157 = @cil_stelem_i2 +| 158 = @cil_stelem_i4 +| 159 = @cil_stelem_i8 +| 160 = @cil_stelem_r4 +| 161 = @cil_stelem_r8 +| 162 = @cil_stelem_ref +| 163 = @cil_ldelem +| 164 = @cil_stelem +| 165 = @cil_unbox_any +| 179 = @cil_conv_ovf_i1 +| 180 = @cil_conv_ovf_u1 +| 181 = @cil_conv_ovf_i2 +| 182 = @cil_conv_ovf_u2 +| 183 = @cil_conv_ovf_i4 +| 184 = @cil_conv_ovf_u4 +| 185 = @cil_conv_ovf_i8 +| 186 = @cil_conv_ovf_u8 +| 194 = @cil_refanyval +| 195 = @cil_ckinfinite +| 198 = @cil_mkrefany +| 208 = @cil_ldtoken +| 209 = @cil_conv_u2 +| 210 = @cil_conv_u1 +| 211 = @cil_conv_i +| 212 = @cil_conv_ovf_i +| 213 = @cil_conv_ovf_u +| 214 = @cil_add_ovf +| 215 = @cil_add_ovf_un +| 216 = @cil_mul_ovf +| 217 = @cil_mul_ovf_un +| 218 = @cil_sub_ovf +| 219 = @cil_sub_ovf_un +| 220 = @cil_endfinally +| 221 = @cil_leave +| 222 = @cil_leave_s +| 223 = @cil_stind_i +| 224 = @cil_conv_u +| 65024 = @cil_arglist +| 65025 = @cil_ceq +| 65026 = @cil_cgt +| 65027 = @cil_cgt_un +| 65028 = @cil_clt +| 65029 = @cil_clt_un +| 65030 = @cil_ldftn +| 65031 = @cil_ldvirtftn +| 65033 = @cil_ldarg +| 65034 = @cil_ldarga +| 65035 = @cil_starg +| 65036 = @cil_ldloc +| 65037 = @cil_ldloca +| 65038 = @cil_stloc +| 65039 = @cil_localloc +| 65041 = @cil_endfilter +| 65042 = @cil_unaligned +| 65043 = @cil_volatile +| 65044 = @cil_tail +| 65045 = @cil_initobj +| 65046 = @cil_constrained +| 65047 = @cil_cpblk +| 65048 = @cil_initblk +| 65050 = @cil_rethrow +| 65052 = @cil_sizeof +| 65053 = @cil_refanytype +| 65054 = @cil_readonly +; + +// CIL ignored instructions + +@cil_ignore = @cil_nop | @cil_break | @cil_volatile | @cil_unaligned; + +// CIL local/parameter/field access + +@cil_ldarg_any = @cil_ldarg_0 | @cil_ldarg_1 | @cil_ldarg_2 | @cil_ldarg_3 | @cil_ldarg_s | @cil_ldarga_s | @cil_ldarg | @cil_ldarga; +@cil_starg_any = @cil_starg | @cil_starg_s; + +@cil_ldloc_any = @cil_ldloc_0 | @cil_ldloc_1 | @cil_ldloc_2 | @cil_ldloc_3 | @cil_ldloc_s | @cil_ldloca_s | @cil_ldloc | @cil_ldloca; +@cil_stloc_any = @cil_stloc_0 | @cil_stloc_1 | @cil_stloc_2 | @cil_stloc_3 | @cil_stloc_s | @cil_stloc; + +@cil_ldfld_any = @cil_ldfld | @cil_ldsfld | @cil_ldsflda | @cil_ldflda; +@cil_stfld_any = @cil_stfld | @cil_stsfld; + +@cil_local_access = @cil_stloc_any | @cil_ldloc_any; +@cil_arg_access = @cil_starg_any | @cil_ldarg_any; +@cil_read_access = @cil_ldloc_any | @cil_ldarg_any | @cil_ldfld_any; +@cil_write_access = @cil_stloc_any | @cil_starg_any | @cil_stfld_any; + +@cil_stack_access = @cil_local_access | @cil_arg_access; +@cil_field_access = @cil_ldfld_any | @cil_stfld_any; + +@cil_access = @cil_read_access | @cil_write_access; + +// CIL constant/literal instructions + +@cil_ldc_i = @cil_ldc_i4_any | @cil_ldc_i8; + +@cil_ldc_i4_any = @cil_ldc_i4_m1 | @cil_ldc_i4_0 | @cil_ldc_i4_1 | @cil_ldc_i4_2 | @cil_ldc_i4_3 | + @cil_ldc_i4_4 | @cil_ldc_i4_5 | @cil_ldc_i4_6 | @cil_ldc_i4_7 | @cil_ldc_i4_8 | @cil_ldc_i4_s | @cil_ldc_i4; + +@cil_ldc_r = @cil_ldc_r4 | @cil_ldc_r8; + +@cil_literal = @cil_ldnull | @cil_ldc_i | @cil_ldc_r | @cil_ldstr; + +// Control flow + +@cil_conditional_jump = @cil_binary_jump | @cil_unary_jump; +@cil_binary_jump = @cil_beq_s | @cil_bge_s | @cil_bgt_s | @cil_ble_s | @cil_blt_s | + @cil_bne_un_s | @cil_bge_un_s | @cil_bgt_un_s | @cil_ble_un_s | @cil_blt_un_s | + @cil_beq | @cil_bge | @cil_bgt | @cil_ble | @cil_blt | + @cil_bne_un | @cil_bge_un | @cil_bgt_un | @cil_ble_un | @cil_blt_un; +@cil_unary_jump = @cil_brfalse_s | @cil_brtrue_s | @cil_brfalse | @cil_brtrue | @cil_switch; +@cil_unconditional_jump = @cil_br | @cil_br_s | @cil_leave_any; +@cil_leave_any = @cil_leave | @cil_leave_s; +@cil_jump = @cil_unconditional_jump | @cil_conditional_jump; + +// CIL call instructions + +@cil_call_any = @cil_jmp | @cil_call | @cil_calli | @cil_tail | @cil_callvirt | @cil_newobj; + +// CIL expression instructions + +@cil_expr = @cil_literal | @cil_binary_expr | @cil_unary_expr | @cil_call_any | @cil_read_access | + @cil_newarr | @cil_ldtoken | @cil_sizeof | + @cil_ldftn | @cil_ldvirtftn | @cil_localloc | @cil_mkrefany | @cil_refanytype | @cil_arglist | @cil_dup; + +@cil_unary_expr = + @cil_conversion_operation | @cil_unary_arithmetic_operation | @cil_unary_bitwise_operation| + @cil_ldlen | @cil_isinst | @cil_box | @cil_ldobj | @cil_castclass | @cil_unbox_any | + @cil_ldind | @cil_unbox; + +@cil_conversion_operation = + @cil_conv_i1 | @cil_conv_i2 | @cil_conv_i4 | @cil_conv_i8 | + @cil_conv_u1 | @cil_conv_u2 | @cil_conv_u4 | @cil_conv_u8 | + @cil_conv_ovf_i | @cil_conv_ovf_i_un | @cil_conv_ovf_i1 | @cil_conv_ovf_i1_un | + @cil_conv_ovf_i2 | @cil_conv_ovf_i2_un | @cil_conv_ovf_i4 | @cil_conv_ovf_i4_un | + @cil_conv_ovf_i8 | @cil_conv_ovf_i8_un | @cil_conv_ovf_u | @cil_conv_ovf_u_un | + @cil_conv_ovf_u1 | @cil_conv_ovf_u1_un | @cil_conv_ovf_u2 | @cil_conv_ovf_u2_un | + @cil_conv_ovf_u4 | @cil_conv_ovf_u4_un | @cil_conv_ovf_u8 | @cil_conv_ovf_u8_un | + @cil_conv_r4 | @cil_conv_r8 | @cil_conv_ovf_u2 | @cil_conv_ovf_u2_un | + @cil_conv_i | @cil_conv_u | @cil_conv_r_un; + +@cil_ldind = @cil_ldind_i | @cil_ldind_i1 | @cil_ldind_i2 | @cil_ldind_i4 | @cil_ldind_i8 | + @cil_ldind_r4 | @cil_ldind_r8 | @cil_ldind_ref | @cil_ldind_u1 | @cil_ldind_u2 | @cil_ldind_u4; + +@cil_stind = @cil_stind_i | @cil_stind_i1 | @cil_stind_i2 | @cil_stind_i4 | @cil_stind_i8 | + @cil_stind_r4 | @cil_stind_r8 | @cil_stind_ref; + +@cil_bitwise_operation = @cil_binary_bitwise_operation | @cil_unary_bitwise_operation; + +@cil_binary_bitwise_operation = @cil_and | @cil_or | @cil_xor | @cil_shr | @cil_shr | @cil_shr_un | @cil_shl; + +@cil_binary_arithmetic_operation = @cil_add | @cil_sub | @cil_mul | @cil_div | @cil_div_un | + @cil_rem | @cil_rem_un | @cil_add_ovf | @cil_add_ovf_un | @cil_mul_ovf | @cil_mul_ovf_un | + @cil_sub_ovf | @cil_sub_ovf_un; + +@cil_unary_bitwise_operation = @cil_not; + +@cil_binary_expr = @cil_binary_arithmetic_operation | @cil_binary_bitwise_operation | @cil_read_array | @cil_comparison_operation; + +@cil_unary_arithmetic_operation = @cil_neg; + +@cil_comparison_operation = @cil_cgt_un | @cil_ceq | @cil_cgt | @cil_clt | @cil_clt_un; + +// Elements that retrieve an address of something +@cil_read_ref = @cil_ldloca_s | @cil_ldarga_s | @cil_ldflda | @cil_ldsflda | @cil_ldelema; + +// CIL array instructions + +@cil_read_array = + @cil_ldelem | @cil_ldelema | @cil_ldelem_i1 | @cil_ldelem_ref | @cil_ldelem_i | + @cil_ldelem_i1 | @cil_ldelem_i2 | @cil_ldelem_i4 | @cil_ldelem_i8 | @cil_ldelem_r4 | + @cil_ldelem_r8 | @cil_ldelem_u1 | @cil_ldelem_u2 | @cil_ldelem_u4; + +@cil_write_array = @cil_stelem | @cil_stelem_ref | + @cil_stelem_i | @cil_stelem_i1 | @cil_stelem_i2 | @cil_stelem_i4 | @cil_stelem_i8 | + @cil_stelem_r4 | @cil_stelem_r8; + +@cil_throw_any = @cil_throw | @cil_rethrow; + +#keyset[impl, index] +cil_instruction( + unique int id: @cil_instruction, + int opcode: int ref, + int index: int ref, + int impl: @cil_method_implementation ref); + +cil_jump( + unique int instruction: @cil_jump ref, + int target: @cil_instruction ref); + +cil_access( + unique int instruction: @cil_instruction ref, + int target: @cil_accessible ref); + +cil_value( + unique int instruction: @cil_literal ref, + string value: string ref); + +#keyset[instruction, index] +cil_switch( + int instruction: @cil_switch ref, + int index: int ref, + int target: @cil_instruction ref); + +cil_instruction_location( + unique int id: @cil_instruction ref, + int loc: @location ref); + +cil_type_location( + int id: @cil_type ref, + int loc: @location ref); + +cil_method_location( + int id: @cil_method ref, + int loc: @location ref); + +@cil_namespace = @namespace; + +@cil_type_container = @cil_type | @cil_namespace | @cil_method; + +case @cil_type.kind of + 0 = @cil_valueorreftype +| 1 = @cil_typeparameter +| 2 = @cil_array_type +| 3 = @cil_pointer_type +| 4 = @cil_function_pointer_type +; + +cil_type( + unique int id: @cil_type, + string name: string ref, + int kind: int ref, + int parent: @cil_type_container ref, + int sourceDecl: @cil_type ref); + +cil_pointer_type( + unique int id: @cil_pointer_type ref, + int pointee: @cil_type ref); + +cil_array_type( + unique int id: @cil_array_type ref, + int element_type: @cil_type ref, + int rank: int ref); + +cil_function_pointer_return_type( + unique int id: @cil_function_pointer_type ref, + int return_type: @cil_type ref); + +cil_method( + unique int id: @cil_method, + string name: string ref, + int parent: @cil_type ref, + int return_type: @cil_type ref); + +cil_method_source_declaration( + unique int method: @cil_method ref, + int source: @cil_method ref); + +cil_method_implementation( + unique int id: @cil_method_implementation, + int method: @cil_method ref, + int location: @assembly ref); + +cil_implements( + int id: @cil_method ref, + int decl: @cil_method ref); + +#keyset[parent, name] +cil_field( + unique int id: @cil_field, + int parent: @cil_type ref, + string name: string ref, + int field_type: @cil_type ref); + +@cil_element = @cil_instruction | @cil_declaration | @cil_handler | @cil_attribute | @cil_namespace; +@cil_named_element = @cil_declaration | @cil_namespace; +@cil_declaration = @cil_variable | @cil_method | @cil_type | @cil_member; +@cil_accessible = @cil_declaration; +@cil_variable = @cil_field | @cil_stack_variable; +@cil_stack_variable = @cil_local_variable | @cil_parameter; +@cil_member = @cil_method | @cil_type | @cil_field | @cil_property | @cil_event; +@cil_custom_modifier_receiver = @cil_method | @cil_property | @cil_parameter | @cil_field | @cil_function_pointer_type; +@cil_parameterizable = @cil_method | @cil_function_pointer_type; +@cil_has_type_annotation = @cil_stack_variable | @cil_property | @cil_method | @cil_function_pointer_type; + +#keyset[parameterizable, index] +cil_parameter( + unique int id: @cil_parameter, + int parameterizable: @cil_parameterizable ref, + int index: int ref, + int param_type: @cil_type ref); + +cil_parameter_in(unique int id: @cil_parameter ref); +cil_parameter_out(unique int id: @cil_parameter ref); + +cil_setter(unique int prop: @cil_property ref, + int method: @cil_method ref); + +#keyset[id, modifier] +cil_custom_modifiers( + int id: @cil_custom_modifier_receiver ref, + int modifier: @cil_type ref, + int kind: int ref); // modreq: 1, modopt: 0 + +cil_type_annotation( + int id: @cil_has_type_annotation ref, + int annotation: int ref); + +cil_getter(unique int prop: @cil_property ref, + int method: @cil_method ref); + +cil_adder(unique int event: @cil_event ref, + int method: @cil_method ref); + +cil_remover(unique int event: @cil_event ref, int method: @cil_method ref); + +cil_raiser(unique int event: @cil_event ref, int method: @cil_method ref); + +cil_property( + unique int id: @cil_property, + int parent: @cil_type ref, + string name: string ref, + int property_type: @cil_type ref); + +#keyset[parent, name] +cil_event(unique int id: @cil_event, + int parent: @cil_type ref, + string name: string ref, + int event_type: @cil_type ref); + +#keyset[impl, index] +cil_local_variable( + unique int id: @cil_local_variable, + int impl: @cil_method_implementation ref, + int index: int ref, + int var_type: @cil_type ref); + +cil_function_pointer_calling_conventions( + int id: @cil_function_pointer_type ref, + int kind: int ref); + +// CIL handlers (exception handlers etc). + +case @cil_handler.kind of + 0 = @cil_catch_handler +| 1 = @cil_filter_handler +| 2 = @cil_finally_handler +| 4 = @cil_fault_handler +; + +#keyset[impl, index] +cil_handler( + unique int id: @cil_handler, + int impl: @cil_method_implementation ref, + int index: int ref, + int kind: int ref, + int try_start: @cil_instruction ref, + int try_end: @cil_instruction ref, + int handler_start: @cil_instruction ref); + +cil_handler_filter( + unique int id: @cil_handler ref, + int filter_start: @cil_instruction ref); + +cil_handler_type( + unique int id: @cil_handler ref, + int catch_type: @cil_type ref); + +@cil_controlflow_node = @cil_entry_point | @cil_instruction; + +@cil_entry_point = @cil_method_implementation | @cil_handler; + +@cil_dataflow_node = @cil_instruction | @cil_variable | @cil_method; + +cil_method_stack_size( + unique int method: @cil_method_implementation ref, + int size: int ref); + +// CIL modifiers + +cil_public(int id: @cil_member ref); +cil_private(int id: @cil_member ref); +cil_protected(int id: @cil_member ref); +cil_internal(int id: @cil_member ref); +cil_static(int id: @cil_member ref); +cil_sealed(int id: @cil_member ref); +cil_virtual(int id: @cil_method ref); +cil_abstract(int id: @cil_member ref); +cil_class(int id: @cil_type ref); +cil_interface(int id: @cil_type ref); +cil_security(int id: @cil_member ref); +cil_requiresecobject(int id: @cil_method ref); +cil_specialname(int id: @cil_method ref); +cil_newslot(int id: @cil_method ref); + +cil_base_class(unique int id: @cil_type ref, int base: @cil_type ref); +cil_base_interface(int id: @cil_type ref, int base: @cil_type ref); +cil_enum_underlying_type(unique int id: @cil_type ref, int underlying: @cil_type ref); + +#keyset[unbound, index] +cil_type_parameter( + int unbound: @cil_member ref, + int index: int ref, + int param: @cil_typeparameter ref); + +#keyset[bound, index] +cil_type_argument( + int bound: @cil_member ref, + int index: int ref, + int t: @cil_type ref); + +// CIL type parameter constraints + +cil_typeparam_covariant(int tp: @cil_typeparameter ref); +cil_typeparam_contravariant(int tp: @cil_typeparameter ref); +cil_typeparam_class(int tp: @cil_typeparameter ref); +cil_typeparam_struct(int tp: @cil_typeparameter ref); +cil_typeparam_new(int tp: @cil_typeparameter ref); +cil_typeparam_constraint(int tp: @cil_typeparameter ref, int supertype: @cil_type ref); + +// CIL attributes + +cil_attribute( + unique int attributeid: @cil_attribute, + int element: @cil_declaration ref, + int constructor: @cil_method ref); + +#keyset[attribute_id, param] +cil_attribute_named_argument( + int attribute_id: @cil_attribute ref, + string param: string ref, + string value: string ref); + +#keyset[attribute_id, index] +cil_attribute_positional_argument( + int attribute_id: @cil_attribute ref, + int index: int ref, + string value: string ref); + + +// Common .Net data model covering both C# and CIL + +// Common elements +@dotnet_element = @element | @cil_element; +@dotnet_named_element = @named_element | @cil_named_element; +@dotnet_callable = @callable | @cil_method; +@dotnet_variable = @variable | @cil_variable; +@dotnet_field = @field | @cil_field; +@dotnet_parameter = @parameter | @cil_parameter; +@dotnet_declaration = @declaration | @cil_declaration; +@dotnet_member = @member | @cil_member; +@dotnet_event = @event | @cil_event; +@dotnet_property = @property | @cil_property | @indexer; +@dotnet_parameterizable = @parameterizable | @cil_parameterizable; + +// Common types +@dotnet_type = @type | @cil_type; +@dotnet_call = @call | @cil_call_any; +@dotnet_throw = @throw_element | @cil_throw_any; +@dotnet_valueorreftype = @cil_valueorreftype | @value_or_ref_type | @cil_array_type | @void_type; +@dotnet_typeparameter = @type_parameter | @cil_typeparameter; +@dotnet_array_type = @array_type | @cil_array_type; +@dotnet_pointer_type = @pointer_type | @cil_pointer_type; +@dotnet_type_parameter = @type_parameter | @cil_typeparameter; +@dotnet_generic = @dotnet_valueorreftype | @dotnet_callable; + +// Attributes +@dotnet_attribute = @attribute | @cil_attribute; + +// Expressions +@dotnet_expr = @expr | @cil_expr; + +// Literals +@dotnet_literal = @literal_expr | @cil_literal; +@dotnet_string_literal = @string_literal_expr | @cil_ldstr; +@dotnet_int_literal = @integer_literal_expr | @cil_ldc_i; +@dotnet_float_literal = @float_literal_expr | @cil_ldc_r; +@dotnet_null_literal = @null_literal_expr | @cil_ldnull; + +@metadata_entity = @cil_method | @cil_type | @cil_field | @cil_property | @field | @property | + @callable | @value_or_ref_type | @void_type; + +#keyset[entity, location] +metadata_handle(int entity : @metadata_entity ref, int location: @assembly ref, int handle: int ref) diff --git a/csharp/upgrades/9258e9b38d85f92cee9559f2ed21e241f0c7a29e/upgrade.properties b/csharp/upgrades/9258e9b38d85f92cee9559f2ed21e241f0c7a29e/upgrade.properties new file mode 100644 index 00000000000..64b44aecc3c --- /dev/null +++ b/csharp/upgrades/9258e9b38d85f92cee9559f2ed21e241f0c7a29e/upgrade.properties @@ -0,0 +1,2 @@ +description: Removed unique base class constraint +compatibility: backwards From d27316eb3e7227930e966d7eba441496e4281dad Mon Sep 17 00:00:00 2001 From: Jonathan Leitschuh Date: Mon, 10 May 2021 11:55:31 -0400 Subject: [PATCH 102/168] Apply suggestions from code review Co-authored-by: Marcono1234 --- java/change-notes/2021-05-05-kryo-improvements.md | 2 +- java/ql/src/semmle/code/java/frameworks/Kryo.qll | 2 +- java/ql/src/semmle/code/java/security/UnsafeDeserialization.qll | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/java/change-notes/2021-05-05-kryo-improvements.md b/java/change-notes/2021-05-05-kryo-improvements.md index f8ba79eb863..5b39d1daae0 100644 --- a/java/change-notes/2021-05-05-kryo-improvements.md +++ b/java/change-notes/2021-05-05-kryo-improvements.md @@ -1,3 +1,3 @@ lgtm,codescanning * Add support for version 5 of the Kryo serialzation/deserialization framework. -* Add support for detecting safe uses of Kryo utilizing `KryoPool.Builder. (#4992)[https://github.com/github/codeql/issues/4992] +* Add support for detecting safe uses of Kryo utilizing `KryoPool.Builder`. [#4992](https://github.com/github/codeql/issues/4992) diff --git a/java/ql/src/semmle/code/java/frameworks/Kryo.qll b/java/ql/src/semmle/code/java/frameworks/Kryo.qll index 16873fc751f..317148d56b5 100644 --- a/java/ql/src/semmle/code/java/frameworks/Kryo.qll +++ b/java/ql/src/semmle/code/java/frameworks/Kryo.qll @@ -47,7 +47,7 @@ class KryoPoolBuilder extends RefType { } /** - * A Kryo pool builder method used a fluent API call chain. + * A Kryo pool builder method used in a fluent API call chain. */ class KryoPoolBuilderMethod extends Method { KryoPoolBuilderMethod() { diff --git a/java/ql/src/semmle/code/java/security/UnsafeDeserialization.qll b/java/ql/src/semmle/code/java/security/UnsafeDeserialization.qll index def37c0964e..a1561c0e6cd 100644 --- a/java/ql/src/semmle/code/java/security/UnsafeDeserialization.qll +++ b/java/ql/src/semmle/code/java/security/UnsafeDeserialization.qll @@ -57,7 +57,7 @@ class SafeKryo extends DataFlow2::Configuration { } /** - * Holds when a funcitonal expression is used to create a `KryoPool.Builder`. + * Holds when a functional expression is used to create a `KryoPool.Builder`. * Eg. `new KryoPool.Builder(() -> new Kryo())` */ private predicate stepKryoPoolBuilderFactoryArgToConstructor( From 54f191cfe37e136bcc7189b2fb01f44dbb01758b Mon Sep 17 00:00:00 2001 From: Erik Krogh Kristensen Date: Tue, 11 May 2021 11:23:03 +0200 Subject: [PATCH 103/168] add support for rejected promise values in API graphs --- .../ql/src/semmle/javascript/ApiGraphs.qll | 20 +++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/javascript/ql/src/semmle/javascript/ApiGraphs.qll b/javascript/ql/src/semmle/javascript/ApiGraphs.qll index 385dc1e76d3..590142590a4 100644 --- a/javascript/ql/src/semmle/javascript/ApiGraphs.qll +++ b/javascript/ql/src/semmle/javascript/ApiGraphs.qll @@ -183,6 +183,11 @@ module API { */ Node getPromised() { result = getASuccessor(Label::promised()) } + /** + * Gets a node representing the error wrapped in the `Promise` object represented by this node. + */ + Node getPromisedError() { result = getASuccessor(Label::promisedError()) } + /** * Gets a string representation of the lexicographically least among all shortest access paths * from the root to this node. @@ -468,6 +473,9 @@ module API { or lbl = Label::promised() and PromiseFlow::storeStep(rhs, pred, Promises::valueProp()) + or + lbl = Label::promisedError() and + PromiseFlow::storeStep(rhs, pred, Promises::errorProp()) ) or exists(DataFlow::ClassNode cls, string name | @@ -482,6 +490,12 @@ module API { rhs = f.getAReturn() ) or + exists(DataFlow::FunctionNode f | + base = MkAsyncFuncResult(f) and + lbl = Label::promisedError() and + rhs = f.getExceptionalReturn() + ) + or exists(int i | lbl = Label::parameter(i) and argumentPassing(base, i, rhs) @@ -559,6 +573,9 @@ module API { or lbl = Label::promised() and PromiseFlow::loadStep(pred, ref, Promises::valueProp()) + or + lbl = Label::promisedError() and + PromiseFlow::loadStep(pred, ref, Promises::errorProp()) ) or exists(DataFlow::Node def, DataFlow::FunctionNode fn | @@ -962,6 +979,9 @@ private module Label { /** Gets the `promised` edge label connecting a promise to its contained value. */ string promised() { result = "promised" } + + /** Gets the `promisedError` edge label connecting a promise to its rejected value. */ + string promisedError() { result = "promisedError" } } private class NodeModuleSourcesNodes extends DataFlow::SourceNode::Range { From 52991dc4a1ec07410fa2818776f756c12418f707 Mon Sep 17 00:00:00 2001 From: Erik Krogh Kristensen Date: Tue, 11 May 2021 11:23:51 +0200 Subject: [PATCH 104/168] rewrite the axios model to use API graphs --- .../javascript/frameworks/ClientRequests.qll | 23 +++++++------------ 1 file changed, 8 insertions(+), 15 deletions(-) diff --git a/javascript/ql/src/semmle/javascript/frameworks/ClientRequests.qll b/javascript/ql/src/semmle/javascript/frameworks/ClientRequests.qll index 451ad3f24d2..78f7ec92a16 100644 --- a/javascript/ql/src/semmle/javascript/frameworks/ClientRequests.qll +++ b/javascript/ql/src/semmle/javascript/frameworks/ClientRequests.qll @@ -206,19 +206,14 @@ module ClientRequest { /** * A model of a URL request made using the `axios` library. */ - class AxiosUrlRequest extends ClientRequest::Range { + class AxiosUrlRequest extends ClientRequest::Range, API::CallNode { string method; AxiosUrlRequest() { - exists(string moduleName, DataFlow::SourceNode callee | this = callee.getACall() | - moduleName = "axios" and - ( - callee = DataFlow::moduleImport(moduleName) and method = "request" - or - callee = DataFlow::moduleMember(moduleName, method) and - (method = httpMethodName() or method = "request") - ) - ) + this = API::moduleImport("axios").getACall() and method = "request" + or + this = API::moduleImport("axios").getMember(method).getACall() and + method = [httpMethodName(), "request"] } private int getOptionsArgIndex() { @@ -247,12 +242,10 @@ module ClientRequest { method = "request" and result = getOptionArgument(0, "data") or - (method = "post" or method = "put" or method = "put") and - (result = getArgument(1) or result = getOptionArgument(2, "data")) + method = ["post", "put", "put"] and + result = [getArgument(1), getOptionArgument(2, "data")] or - exists(string name | name = "headers" or name = "params" | - result = getOptionArgument([0 .. 2], name) - ) + result = getOptionArgument([0 .. 2], ["headers", "params"]) } /** Gets the response type from the options passed in. */ From 99e98419dc3e38c06c2cf6ec6136d88efeae7af6 Mon Sep 17 00:00:00 2001 From: Erik Krogh Kristensen Date: Tue, 11 May 2021 11:24:21 +0200 Subject: [PATCH 105/168] add support for error values in an axios client request --- .../semmle/javascript/frameworks/ClientRequests.qll | 5 +++++ .../ClientRequests/ClientRequests.expected | 5 +++++ .../library-tests/frameworks/ClientRequests/tst.js | 13 +++++++++++++ 3 files changed, 23 insertions(+) diff --git a/javascript/ql/src/semmle/javascript/frameworks/ClientRequests.qll b/javascript/ql/src/semmle/javascript/frameworks/ClientRequests.qll index 78f7ec92a16..477f9354d03 100644 --- a/javascript/ql/src/semmle/javascript/frameworks/ClientRequests.qll +++ b/javascript/ql/src/semmle/javascript/frameworks/ClientRequests.qll @@ -268,6 +268,11 @@ module ClientRequest { responseType = getResponseType() and promise = true and result = this + or + responseType = getResponseType() and + promise = false and + result = + getReturn().getPromisedError().getMember("response").getMember("data").getAnImmediateUse() } } diff --git a/javascript/ql/test/library-tests/frameworks/ClientRequests/ClientRequests.expected b/javascript/ql/test/library-tests/frameworks/ClientRequests/ClientRequests.expected index e448b888e12..c6542eb009d 100644 --- a/javascript/ql/test/library-tests/frameworks/ClientRequests/ClientRequests.expected +++ b/javascript/ql/test/library-tests/frameworks/ClientRequests/ClientRequests.expected @@ -87,6 +87,7 @@ test_ClientRequest | tst.js:271:3:271:61 | proxy.w ... 080' }) | | tst.js:274:1:283:2 | httpPro ... true\\n}) | | tst.js:286:20:286:55 | new Web ... :8080') | +| tst.js:296:5:299:6 | axios({ ... \\n }) | test_getADataNode | tst.js:53:5:53:23 | axios({data: data}) | tst.js:53:18:53:21 | data | | tst.js:57:5:57:39 | axios.p ... data2}) | tst.js:57:19:57:23 | data1 | @@ -227,6 +228,8 @@ test_getUrl | tst.js:271:3:271:61 | proxy.w ... 080' }) | tst.js:271:33:271:58 | 'http:/ ... m:8080' | | tst.js:274:1:283:2 | httpPro ... true\\n}) | tst.js:275:13:281:5 | {\\n ... ,\\n } | | tst.js:286:20:286:55 | new Web ... :8080') | tst.js:286:34:286:54 | 'ws://l ... t:8080' | +| tst.js:296:5:299:6 | axios({ ... \\n }) | tst.js:296:11:299:5 | {\\n ... ,\\n } | +| tst.js:296:5:299:6 | axios({ ... \\n }) | tst.js:298:14:298:44 | "http:/ ... -axios" | test_getAResponseDataNode | tst.js:19:5:19:23 | requestPromise(url) | tst.js:19:5:19:23 | requestPromise(url) | text | true | | tst.js:21:5:21:23 | superagent.get(url) | tst.js:21:5:21:23 | superagent.get(url) | stream | true | @@ -294,3 +297,5 @@ test_getAResponseDataNode | tst.js:235:5:237:6 | needle. ... \\n }) | tst.js:235:67:235:70 | resp | fetch.response | false | | tst.js:235:5:237:6 | needle. ... \\n }) | tst.js:235:73:235:76 | body | json | false | | tst.js:286:20:286:55 | new Web ... :8080') | tst.js:291:44:291:53 | event.data | json | false | +| tst.js:296:5:299:6 | axios({ ... \\n }) | tst.js:296:5:299:6 | axios({ ... \\n }) | json | true | +| tst.js:296:5:299:6 | axios({ ... \\n }) | tst.js:303:26:303:42 | err.response.data | json | false | diff --git a/javascript/ql/test/library-tests/frameworks/ClientRequests/tst.js b/javascript/ql/test/library-tests/frameworks/ClientRequests/tst.js index bc15565c072..f284ffaa407 100644 --- a/javascript/ql/test/library-tests/frameworks/ClientRequests/tst.js +++ b/javascript/ql/test/library-tests/frameworks/ClientRequests/tst.js @@ -290,4 +290,17 @@ function webSocket() { socket.addEventListener('message', function (event) { console.log("Data from server: " + event.data); }); +} + +function moreAxios() { + axios({ + method: 'GET', + url: "http://example.org/more-axios", + }).then( + x => res.send(x.data), + (err) => { + const status = err.response.status; + const data = err.response.data; + } + ); } \ No newline at end of file From 717070c7e439a1d99512a1f4238d157daef4cbc3 Mon Sep 17 00:00:00 2001 From: Tamas Vajk Date: Tue, 11 May 2021 13:11:35 +0200 Subject: [PATCH 106/168] Fix/cleanup passed and default arguments values --- .../Entities/Types/FunctionPointerType.cs | 2 +- .../Entities/Types/TupleType.cs | 2 +- .../SymbolExtensions.cs | 18 +++++++++--------- 3 files changed, 11 insertions(+), 11 deletions(-) diff --git a/csharp/extractor/Semmle.Extraction.CSharp/Entities/Types/FunctionPointerType.cs b/csharp/extractor/Semmle.Extraction.CSharp/Entities/Types/FunctionPointerType.cs index 37ba909ec46..4c3ab516172 100644 --- a/csharp/extractor/Semmle.Extraction.CSharp/Entities/Types/FunctionPointerType.cs +++ b/csharp/extractor/Semmle.Extraction.CSharp/Entities/Types/FunctionPointerType.cs @@ -13,7 +13,7 @@ namespace Semmle.Extraction.CSharp.Entities public override void WriteId(EscapingTextWriter trapFile) { - Symbol.BuildTypeId(Context, trapFile, Symbol); + Symbol.BuildTypeId(Context, trapFile, Symbol, constructUnderlyingTupleType: false); trapFile.Write(";functionpointertype"); } diff --git a/csharp/extractor/Semmle.Extraction.CSharp/Entities/Types/TupleType.cs b/csharp/extractor/Semmle.Extraction.CSharp/Entities/Types/TupleType.cs index ddbf1ac52a6..56db07671d7 100644 --- a/csharp/extractor/Semmle.Extraction.CSharp/Entities/Types/TupleType.cs +++ b/csharp/extractor/Semmle.Extraction.CSharp/Entities/Types/TupleType.cs @@ -32,7 +32,7 @@ namespace Semmle.Extraction.CSharp.Entities public override void WriteId(EscapingTextWriter trapFile) { - Symbol.BuildTypeId(Context, trapFile, Symbol); + Symbol.BuildTypeId(Context, trapFile, Symbol, constructUnderlyingTupleType: false); trapFile.Write(";tuple"); } diff --git a/csharp/extractor/Semmle.Extraction.CSharp/SymbolExtensions.cs b/csharp/extractor/Semmle.Extraction.CSharp/SymbolExtensions.cs index 91f0d0edf20..0bda977a6d5 100644 --- a/csharp/extractor/Semmle.Extraction.CSharp/SymbolExtensions.cs +++ b/csharp/extractor/Semmle.Extraction.CSharp/SymbolExtensions.cs @@ -158,7 +158,7 @@ namespace Semmle.Extraction.CSharp /// The trap builder used to store the result. /// The outer symbol being defined (to avoid recursive ids). /// Whether to build a type ID for the underlying `System.ValueTuple` struct in the case of tuple types. - public static void BuildTypeId(this ITypeSymbol type, Context cx, EscapingTextWriter trapFile, ISymbol symbolBeingDefined, bool constructUnderlyingTupleType = false) + public static void BuildTypeId(this ITypeSymbol type, Context cx, EscapingTextWriter trapFile, ISymbol symbolBeingDefined, bool constructUnderlyingTupleType) { using (cx.StackGuard) { @@ -166,7 +166,7 @@ namespace Semmle.Extraction.CSharp { case TypeKind.Array: var array = (IArrayTypeSymbol)type; - array.ElementType.BuildOrWriteId(cx, trapFile, symbolBeingDefined); + array.ElementType.BuildOrWriteId(cx, trapFile, symbolBeingDefined, constructUnderlyingTupleType: false); array.BuildArraySuffix(trapFile); return; case TypeKind.Class: @@ -180,12 +180,12 @@ namespace Semmle.Extraction.CSharp return; case TypeKind.Pointer: var ptr = (IPointerTypeSymbol)type; - ptr.PointedAtType.BuildOrWriteId(cx, trapFile, symbolBeingDefined); + ptr.PointedAtType.BuildOrWriteId(cx, trapFile, symbolBeingDefined, constructUnderlyingTupleType: false); trapFile.Write('*'); return; case TypeKind.TypeParameter: var tp = (ITypeParameterSymbol)type; - tp.ContainingSymbol.BuildOrWriteId(cx, trapFile, symbolBeingDefined); + tp.ContainingSymbol.BuildOrWriteId(cx, trapFile, symbolBeingDefined, constructUnderlyingTupleType: false); trapFile.Write('_'); trapFile.Write(tp.Name); return; @@ -202,7 +202,7 @@ namespace Semmle.Extraction.CSharp } } - private static void BuildOrWriteId(this ISymbol? symbol, Context cx, EscapingTextWriter trapFile, ISymbol symbolBeingDefined, bool constructUnderlyingTupleType = false) + private static void BuildOrWriteId(this ISymbol? symbol, Context cx, EscapingTextWriter trapFile, ISymbol symbolBeingDefined, bool constructUnderlyingTupleType) { if (symbol is null) { @@ -245,7 +245,7 @@ namespace Semmle.Extraction.CSharp /// . /// public static void BuildOrWriteId(this ISymbol? symbol, Context cx, EscapingTextWriter trapFile, ISymbol symbolBeingDefined) => - symbol.BuildOrWriteId(cx, trapFile, symbolBeingDefined, true); + symbol.BuildOrWriteId(cx, trapFile, symbolBeingDefined, constructUnderlyingTupleType: false); /// /// Constructs an array suffix string for this array type symbol. @@ -292,7 +292,7 @@ namespace Semmle.Extraction.CSharp { trapFile.Write((f.CorrespondingTupleField ?? f).Name); trapFile.Write(":"); - f.Type.BuildOrWriteId(cx, trapFile, symbolBeingDefined); + f.Type.BuildOrWriteId(cx, trapFile, symbolBeingDefined, constructUnderlyingTupleType: false); } ); trapFile.Write(")"); @@ -303,7 +303,7 @@ namespace Semmle.Extraction.CSharp { if (named.ContainingType is not null) { - named.ContainingType.BuildOrWriteId(cx, trapFile, symbolBeingDefined); + named.ContainingType.BuildOrWriteId(cx, trapFile, symbolBeingDefined, constructUnderlyingTupleType: false); trapFile.Write('.'); } else if (named.ContainingNamespace is not null) @@ -335,7 +335,7 @@ namespace Semmle.Extraction.CSharp // a constructed type with different nullability of its members and methods, // so we need to create a distinct database entity for it. trapFile.BuildList(",", named.GetAnnotatedTypeArguments(), - ta => ta.Symbol.BuildOrWriteId(cx, trapFile, symbolBeingDefined) + ta => ta.Symbol.BuildOrWriteId(cx, trapFile, symbolBeingDefined, constructUnderlyingTupleType: false) ); trapFile.Write('>'); } From 24d8abd2c2337aa21c048fe4f1cffed5b55cb41a Mon Sep 17 00:00:00 2001 From: Mathias Vorreiter Pedersen Date: Tue, 11 May 2021 14:27:53 +0200 Subject: [PATCH 107/168] C++: Add false positive testcase when an absolute value is used in comparison. --- .../semmle/tainted/ArithmeticTainted.expected | 4 ++++ .../Security/CWE/CWE-190/semmle/tainted/test5.cpp | 12 ++++++++++++ 2 files changed, 16 insertions(+) diff --git a/cpp/ql/test/query-tests/Security/CWE/CWE-190/semmle/tainted/ArithmeticTainted.expected b/cpp/ql/test/query-tests/Security/CWE/CWE-190/semmle/tainted/ArithmeticTainted.expected index bdf00e0a5df..ee55beac3a2 100644 --- a/cpp/ql/test/query-tests/Security/CWE/CWE-190/semmle/tainted/ArithmeticTainted.expected +++ b/cpp/ql/test/query-tests/Security/CWE/CWE-190/semmle/tainted/ArithmeticTainted.expected @@ -3,6 +3,10 @@ | test5.cpp:17:6:17:18 | call to getTaintedInt | $@ flows to here and is used in arithmetic, potentially causing an overflow. | test5.cpp:9:7:9:9 | buf | User-provided value | | test5.cpp:19:6:19:6 | y | $@ flows to here and is used in arithmetic, potentially causing an overflow. | test5.cpp:9:7:9:9 | buf | User-provided value | | test5.cpp:19:6:19:6 | y | $@ flows to here and is used in arithmetic, potentially causing an underflow. | test5.cpp:9:7:9:9 | buf | User-provided value | +| test5.cpp:30:17:30:23 | tainted | $@ flows to here and is used in arithmetic, potentially causing an overflow. | test5.cpp:9:7:9:9 | buf | User-provided value | +| test5.cpp:30:17:30:23 | tainted | $@ flows to here and is used in arithmetic, potentially causing an underflow. | test5.cpp:9:7:9:9 | buf | User-provided value | +| test5.cpp:30:27:30:33 | tainted | $@ flows to here and is used in arithmetic, potentially causing an overflow. | test5.cpp:9:7:9:9 | buf | User-provided value | +| test5.cpp:30:27:30:33 | tainted | $@ flows to here and is used in arithmetic, potentially causing an underflow. | test5.cpp:9:7:9:9 | buf | User-provided value | | test.c:14:15:14:28 | maxConnections | $@ flows to here and is used in arithmetic, potentially causing an overflow. | test.c:11:29:11:32 | argv | User-provided value | | test.c:14:15:14:28 | maxConnections | $@ flows to here and is used in arithmetic, potentially causing an underflow. | test.c:11:29:11:32 | argv | User-provided value | | test.c:44:7:44:10 | len2 | $@ flows to here and is used in arithmetic, potentially causing an underflow. | test.c:41:17:41:20 | argv | User-provided value | diff --git a/cpp/ql/test/query-tests/Security/CWE/CWE-190/semmle/tainted/test5.cpp b/cpp/ql/test/query-tests/Security/CWE/CWE-190/semmle/tainted/test5.cpp index 527c603d1b8..de0baa93bb1 100644 --- a/cpp/ql/test/query-tests/Security/CWE/CWE-190/semmle/tainted/test5.cpp +++ b/cpp/ql/test/query-tests/Security/CWE/CWE-190/semmle/tainted/test5.cpp @@ -18,3 +18,15 @@ void useTaintedInt() y = getTaintedInt(); y = y * 1024; // BAD: arithmetic on a tainted value } + +typedef long long int intmax_t; + +intmax_t imaxabs(intmax_t j); + +void useTaintedIntWithGuard() { + int tainted = getTaintedInt(); + + if(imaxabs(tainted) <= 100) { + int product = tainted * tainted; // GOOD: can't underflow/overflow [FALSE POSITIVE] + } +} \ No newline at end of file From 0d9a85ca6b30511e41c1924161fae2193cec3b71 Mon Sep 17 00:00:00 2001 From: Jonathan Leitschuh Date: Tue, 11 May 2021 08:29:50 -0400 Subject: [PATCH 108/168] Update java/change-notes/2021-05-05-kryo-improvements.md Co-authored-by: Anders Schack-Mulligen --- java/change-notes/2021-05-05-kryo-improvements.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/java/change-notes/2021-05-05-kryo-improvements.md b/java/change-notes/2021-05-05-kryo-improvements.md index 5b39d1daae0..dbacb10099b 100644 --- a/java/change-notes/2021-05-05-kryo-improvements.md +++ b/java/change-notes/2021-05-05-kryo-improvements.md @@ -1,3 +1,3 @@ lgtm,codescanning -* Add support for version 5 of the Kryo serialzation/deserialization framework. +* Add support for version 5 of the Kryo serialization/deserialization framework. * Add support for detecting safe uses of Kryo utilizing `KryoPool.Builder`. [#4992](https://github.com/github/codeql/issues/4992) From 48e783184cd82692208d21d6286deeb38924804f Mon Sep 17 00:00:00 2001 From: Mathias Vorreiter Pedersen Date: Tue, 11 May 2021 14:30:28 +0200 Subject: [PATCH 109/168] C++: Fix false positive by recognizing more absolute value functions in Overflow.qll --- cpp/ql/src/semmle/code/cpp/security/Overflow.qll | 2 +- .../CWE/CWE-190/semmle/tainted/ArithmeticTainted.expected | 4 ---- .../query-tests/Security/CWE/CWE-190/semmle/tainted/test5.cpp | 2 +- 3 files changed, 2 insertions(+), 6 deletions(-) diff --git a/cpp/ql/src/semmle/code/cpp/security/Overflow.qll b/cpp/ql/src/semmle/code/cpp/security/Overflow.qll index 6cf82791d52..b8ed406cb4a 100644 --- a/cpp/ql/src/semmle/code/cpp/security/Overflow.qll +++ b/cpp/ql/src/semmle/code/cpp/security/Overflow.qll @@ -12,7 +12,7 @@ import semmle.code.cpp.rangeanalysis.RangeAnalysisUtils * Holds if the value of `use` is guarded using `abs`. */ predicate guardedAbs(Operation e, Expr use) { - exists(FunctionCall fc | fc.getTarget().getName() = "abs" | + exists(FunctionCall fc | fc.getTarget().getName() = ["abs", "labs", "llabs", "imaxabs"] | fc.getArgument(0).getAChild*() = use and guardedLesser(e, fc) ) diff --git a/cpp/ql/test/query-tests/Security/CWE/CWE-190/semmle/tainted/ArithmeticTainted.expected b/cpp/ql/test/query-tests/Security/CWE/CWE-190/semmle/tainted/ArithmeticTainted.expected index ee55beac3a2..bdf00e0a5df 100644 --- a/cpp/ql/test/query-tests/Security/CWE/CWE-190/semmle/tainted/ArithmeticTainted.expected +++ b/cpp/ql/test/query-tests/Security/CWE/CWE-190/semmle/tainted/ArithmeticTainted.expected @@ -3,10 +3,6 @@ | test5.cpp:17:6:17:18 | call to getTaintedInt | $@ flows to here and is used in arithmetic, potentially causing an overflow. | test5.cpp:9:7:9:9 | buf | User-provided value | | test5.cpp:19:6:19:6 | y | $@ flows to here and is used in arithmetic, potentially causing an overflow. | test5.cpp:9:7:9:9 | buf | User-provided value | | test5.cpp:19:6:19:6 | y | $@ flows to here and is used in arithmetic, potentially causing an underflow. | test5.cpp:9:7:9:9 | buf | User-provided value | -| test5.cpp:30:17:30:23 | tainted | $@ flows to here and is used in arithmetic, potentially causing an overflow. | test5.cpp:9:7:9:9 | buf | User-provided value | -| test5.cpp:30:17:30:23 | tainted | $@ flows to here and is used in arithmetic, potentially causing an underflow. | test5.cpp:9:7:9:9 | buf | User-provided value | -| test5.cpp:30:27:30:33 | tainted | $@ flows to here and is used in arithmetic, potentially causing an overflow. | test5.cpp:9:7:9:9 | buf | User-provided value | -| test5.cpp:30:27:30:33 | tainted | $@ flows to here and is used in arithmetic, potentially causing an underflow. | test5.cpp:9:7:9:9 | buf | User-provided value | | test.c:14:15:14:28 | maxConnections | $@ flows to here and is used in arithmetic, potentially causing an overflow. | test.c:11:29:11:32 | argv | User-provided value | | test.c:14:15:14:28 | maxConnections | $@ flows to here and is used in arithmetic, potentially causing an underflow. | test.c:11:29:11:32 | argv | User-provided value | | test.c:44:7:44:10 | len2 | $@ flows to here and is used in arithmetic, potentially causing an underflow. | test.c:41:17:41:20 | argv | User-provided value | diff --git a/cpp/ql/test/query-tests/Security/CWE/CWE-190/semmle/tainted/test5.cpp b/cpp/ql/test/query-tests/Security/CWE/CWE-190/semmle/tainted/test5.cpp index de0baa93bb1..92eb71ad541 100644 --- a/cpp/ql/test/query-tests/Security/CWE/CWE-190/semmle/tainted/test5.cpp +++ b/cpp/ql/test/query-tests/Security/CWE/CWE-190/semmle/tainted/test5.cpp @@ -27,6 +27,6 @@ void useTaintedIntWithGuard() { int tainted = getTaintedInt(); if(imaxabs(tainted) <= 100) { - int product = tainted * tainted; // GOOD: can't underflow/overflow [FALSE POSITIVE] + int product = tainted * tainted; // GOOD: can't underflow/overflow } } \ No newline at end of file From d66506b0a388f40634f80318a4e8faedcf5909e7 Mon Sep 17 00:00:00 2001 From: Tom Hvitved Date: Tue, 11 May 2021 14:40:10 +0200 Subject: [PATCH 110/168] Data flow: Rename `{Argument,Parameter}NodeExt` to `{Arg,Param}Node` --- .../cpp/dataflow/internal/DataFlowImpl.qll | 112 ++++++++---------- .../cpp/dataflow/internal/DataFlowImpl2.qll | 112 ++++++++---------- .../cpp/dataflow/internal/DataFlowImpl3.qll | 112 ++++++++---------- .../cpp/dataflow/internal/DataFlowImpl4.qll | 112 ++++++++---------- .../dataflow/internal/DataFlowImplCommon.qll | 94 +++++++-------- .../dataflow/internal/DataFlowImplLocal.qll | 112 ++++++++---------- .../cpp/ir/dataflow/internal/DataFlowImpl.qll | 112 ++++++++---------- .../ir/dataflow/internal/DataFlowImpl2.qll | 112 ++++++++---------- .../ir/dataflow/internal/DataFlowImpl3.qll | 112 ++++++++---------- .../ir/dataflow/internal/DataFlowImpl4.qll | 112 ++++++++---------- .../dataflow/internal/DataFlowImplCommon.qll | 94 +++++++-------- .../csharp/dataflow/internal/DataFlowImpl.qll | 112 ++++++++---------- .../dataflow/internal/DataFlowImpl2.qll | 112 ++++++++---------- .../dataflow/internal/DataFlowImpl3.qll | 112 ++++++++---------- .../dataflow/internal/DataFlowImpl4.qll | 112 ++++++++---------- .../dataflow/internal/DataFlowImpl5.qll | 112 ++++++++---------- .../dataflow/internal/DataFlowImplCommon.qll | 94 +++++++-------- .../java/dataflow/internal/DataFlowImpl.qll | 112 ++++++++---------- .../java/dataflow/internal/DataFlowImpl2.qll | 112 ++++++++---------- .../java/dataflow/internal/DataFlowImpl3.qll | 112 ++++++++---------- .../java/dataflow/internal/DataFlowImpl4.qll | 112 ++++++++---------- .../java/dataflow/internal/DataFlowImpl5.qll | 112 ++++++++---------- .../java/dataflow/internal/DataFlowImpl6.qll | 112 ++++++++---------- .../dataflow/internal/DataFlowImplCommon.qll | 94 +++++++-------- .../dataflow/new/internal/DataFlowImpl.qll | 112 ++++++++---------- .../dataflow/new/internal/DataFlowImpl2.qll | 112 ++++++++---------- .../dataflow/new/internal/DataFlowImpl3.qll | 112 ++++++++---------- .../dataflow/new/internal/DataFlowImpl4.qll | 112 ++++++++---------- .../new/internal/DataFlowImplCommon.qll | 94 +++++++-------- 29 files changed, 1372 insertions(+), 1786 deletions(-) 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 cf83530bcbd..058d66b1496 100644 --- a/cpp/ql/src/semmle/code/cpp/dataflow/internal/DataFlowImpl.qll +++ b/cpp/ql/src/semmle/code/cpp/dataflow/internal/DataFlowImpl.qll @@ -385,7 +385,7 @@ private module Stage1 { */ pragma[nomagic] private predicate fwdFlowIsEntered(DataFlowCall call, Cc cc, Configuration config) { - exists(ArgumentNodeExt arg | + exists(ArgNode arg | fwdFlow(arg, cc, config) and viableParamArg(call, _, arg) ) @@ -512,24 +512,22 @@ private module Stage1 { pragma[nomagic] predicate viableParamArgNodeCandFwd1( - DataFlowCall call, ParameterNodeExt p, ArgumentNodeExt arg, Configuration config + DataFlowCall call, ParamNode p, ArgNode arg, Configuration config ) { viableParamArg(call, p, arg) and fwdFlow(arg, config) } pragma[nomagic] - private predicate revFlowIn( - DataFlowCall call, ArgumentNodeExt arg, boolean toReturn, Configuration config - ) { - exists(ParameterNodeExt p | + private predicate revFlowIn(DataFlowCall call, ArgNode arg, boolean toReturn, Configuration config) { + exists(ParamNode p | revFlow(p, toReturn, config) and viableParamArgNodeCandFwd1(call, p, arg, config) ) } pragma[nomagic] - private predicate revFlowInToReturn(DataFlowCall call, ArgumentNodeExt arg, Configuration config) { + private predicate revFlowInToReturn(DataFlowCall call, ArgNode arg, Configuration config) { revFlowIn(call, arg, true, config) } @@ -594,9 +592,7 @@ private module Stage1 { * Holds if flow may enter through `p` and reach a return node making `p` a * candidate for the origin of a summary. */ - predicate parameterMayFlowThrough( - ParameterNodeExt p, DataFlowCallable c, Ap ap, Configuration config - ) { + predicate parameterMayFlowThrough(ParamNode p, DataFlowCallable c, Ap ap, Configuration config) { exists(ReturnKindExt kind | throughFlowNodeCand(p, config) and returnFlowCallableNodeCand(c, kind, config) and @@ -662,7 +658,7 @@ private predicate flowOutOfCallNodeCand1( pragma[nomagic] private predicate viableParamArgNodeCand1( - DataFlowCall call, ParameterNodeExt p, ArgumentNodeExt arg, Configuration config + DataFlowCall call, ParamNode p, ArgNode arg, Configuration config ) { Stage1::viableParamArgNodeCandFwd1(call, p, arg, config) and Stage1::revFlow(arg, config) @@ -674,7 +670,7 @@ private predicate viableParamArgNodeCand1( */ pragma[nomagic] private predicate flowIntoCallNodeCand1( - DataFlowCall call, ArgumentNodeExt arg, ParameterNodeExt p, Configuration config + DataFlowCall call, ArgNode arg, ParamNode p, Configuration config ) { viableParamArgNodeCand1(call, p, arg, config) and Stage1::revFlow(p, config) and @@ -734,8 +730,7 @@ private predicate flowOutOfCallNodeCand1( */ pragma[nomagic] private predicate flowIntoCallNodeCand1( - DataFlowCall call, ArgumentNodeExt arg, ParameterNodeExt p, boolean allowsFieldFlow, - Configuration config + DataFlowCall call, ArgNode arg, ParamNode p, boolean allowsFieldFlow, Configuration config ) { flowIntoCallNodeCand1(call, arg, p, config) and exists(int b, int j | @@ -943,10 +938,10 @@ private module Stage2 { pragma[nomagic] private predicate fwdFlowIn( - DataFlowCall call, ParameterNodeExt p, Cc outercc, Cc innercc, ApOption argAp, Ap ap, + DataFlowCall call, ParamNode p, Cc outercc, Cc innercc, ApOption argAp, Ap ap, Configuration config ) { - exists(ArgumentNodeExt arg, boolean allowsFieldFlow | + exists(ArgNode arg, boolean allowsFieldFlow | fwdFlow(arg, outercc, argAp, ap, config) and flowIntoCall(call, arg, p, allowsFieldFlow, config) and innercc = getCallContextCall(call, getNodeEnclosingCallable(p), outercc) @@ -991,7 +986,7 @@ private module Stage2 { private predicate fwdFlowIsEntered( DataFlowCall call, Cc cc, ApOption argAp, Ap ap, Configuration config ) { - exists(ParameterNodeExt p | + exists(ParamNode p | fwdFlowIn(call, p, cc, _, argAp, ap, config) and PrevStage::parameterMayFlowThrough(p, _, getApprox(ap), config) ) @@ -1132,10 +1127,9 @@ private module Stage2 { pragma[nomagic] private predicate revFlowIn( - DataFlowCall call, ArgumentNodeExt arg, boolean toReturn, ApOption returnAp, Ap ap, - Configuration config + DataFlowCall call, ArgNode arg, boolean toReturn, ApOption returnAp, Ap ap, Configuration config ) { - exists(ParameterNodeExt p, boolean allowsFieldFlow | + exists(ParamNode p, boolean allowsFieldFlow | revFlow(p, toReturn, returnAp, ap, config) and flowIntoCall(call, arg, p, allowsFieldFlow, config) | @@ -1145,7 +1139,7 @@ private module Stage2 { pragma[nomagic] private predicate revFlowInToReturn( - DataFlowCall call, ArgumentNodeExt arg, Ap returnAp, Ap ap, Configuration config + DataFlowCall call, ArgNode arg, Ap returnAp, Ap ap, Configuration config ) { revFlowIn(call, arg, true, apSome(returnAp), ap, config) } @@ -1198,15 +1192,13 @@ private module Stage2 { pragma[noinline] private predicate parameterFlow( - ParameterNodeExt p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config + ParamNode p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config ) { revFlow(p, true, apSome(ap0), ap, config) and c = getNodeEnclosingCallable(p) } - predicate parameterMayFlowThrough( - ParameterNodeExt p, DataFlowCallable c, Ap ap, Configuration config - ) { + predicate parameterMayFlowThrough(ParamNode p, DataFlowCallable c, Ap ap, Configuration config) { exists(ReturnNodeExt ret, Ap ap0, ReturnKindExt kind, int pos | parameterFlow(p, ap, ap0, c, config) and c = getNodeEnclosingCallable(ret) and @@ -1246,8 +1238,7 @@ private predicate flowOutOfCallNodeCand2( pragma[nomagic] private predicate flowIntoCallNodeCand2( - DataFlowCall call, ArgumentNodeExt node1, ParameterNodeExt node2, boolean allowsFieldFlow, - Configuration config + DataFlowCall call, ArgNode node1, ParamNode node2, boolean allowsFieldFlow, Configuration config ) { flowIntoCallNodeCand1(call, node1, node2, allowsFieldFlow, config) and Stage2::revFlow(node2, pragma[only_bind_into](config)) and @@ -1276,7 +1267,7 @@ private module LocalFlowBigStep { config.isSource(node) or jumpStep(_, node, config) or additionalJumpStep(_, node, config) or - node instanceof ParameterNodeExt or + node instanceof ParamNode or node instanceof OutNodeExt or store(_, _, node, _) or read(_, _, node) or @@ -1586,10 +1577,10 @@ private module Stage3 { pragma[nomagic] private predicate fwdFlowIn( - DataFlowCall call, ParameterNodeExt p, Cc outercc, Cc innercc, ApOption argAp, Ap ap, + DataFlowCall call, ParamNode p, Cc outercc, Cc innercc, ApOption argAp, Ap ap, Configuration config ) { - exists(ArgumentNodeExt arg, boolean allowsFieldFlow | + exists(ArgNode arg, boolean allowsFieldFlow | fwdFlow(arg, outercc, argAp, ap, config) and flowIntoCall(call, arg, p, allowsFieldFlow, config) and innercc = getCallContextCall(call, getNodeEnclosingCallable(p), outercc) @@ -1634,7 +1625,7 @@ private module Stage3 { private predicate fwdFlowIsEntered( DataFlowCall call, Cc cc, ApOption argAp, Ap ap, Configuration config ) { - exists(ParameterNodeExt p | + exists(ParamNode p | fwdFlowIn(call, p, cc, _, argAp, ap, config) and PrevStage::parameterMayFlowThrough(p, _, unbindApa(getApprox(ap)), config) ) @@ -1775,10 +1766,9 @@ private module Stage3 { pragma[nomagic] private predicate revFlowIn( - DataFlowCall call, ArgumentNodeExt arg, boolean toReturn, ApOption returnAp, Ap ap, - Configuration config + DataFlowCall call, ArgNode arg, boolean toReturn, ApOption returnAp, Ap ap, Configuration config ) { - exists(ParameterNodeExt p, boolean allowsFieldFlow | + exists(ParamNode p, boolean allowsFieldFlow | revFlow(p, toReturn, returnAp, ap, config) and flowIntoCall(call, arg, p, allowsFieldFlow, config) | @@ -1788,7 +1778,7 @@ private module Stage3 { pragma[nomagic] private predicate revFlowInToReturn( - DataFlowCall call, ArgumentNodeExt arg, Ap returnAp, Ap ap, Configuration config + DataFlowCall call, ArgNode arg, Ap returnAp, Ap ap, Configuration config ) { revFlowIn(call, arg, true, apSome(returnAp), ap, config) } @@ -1841,15 +1831,13 @@ private module Stage3 { pragma[noinline] private predicate parameterFlow( - ParameterNodeExt p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config + ParamNode p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config ) { revFlow(p, true, apSome(ap0), ap, config) and c = getNodeEnclosingCallable(p) } - predicate parameterMayFlowThrough( - ParameterNodeExt p, DataFlowCallable c, Ap ap, Configuration config - ) { + predicate parameterMayFlowThrough(ParamNode p, DataFlowCallable c, Ap ap, Configuration config) { exists(ReturnNodeExt ret, Ap ap0, ReturnKindExt kind, int pos | parameterFlow(p, ap, ap0, c, config) and c = getNodeEnclosingCallable(ret) and @@ -2160,8 +2148,7 @@ private module Stage4 { pragma[nomagic] private predicate flowIntoCall( - DataFlowCall call, ArgumentNodeExt node1, ParameterNodeExt node2, boolean allowsFieldFlow, - Configuration config + DataFlowCall call, ArgNode node1, ParamNode node2, boolean allowsFieldFlow, Configuration config ) { flowIntoCallNodeCand2(call, node1, node2, allowsFieldFlow, config) and PrevStage::revFlow(node2, _, _, _, pragma[only_bind_into](config)) and @@ -2305,10 +2292,10 @@ private module Stage4 { pragma[nomagic] private predicate fwdFlowIn( - DataFlowCall call, ParameterNodeExt p, Cc outercc, Cc innercc, ApOption argAp, Ap ap, + DataFlowCall call, ParamNode p, Cc outercc, Cc innercc, ApOption argAp, Ap ap, Configuration config ) { - exists(ArgumentNodeExt arg, boolean allowsFieldFlow | + exists(ArgNode arg, boolean allowsFieldFlow | fwdFlow(arg, outercc, argAp, ap, config) and flowIntoCall(call, arg, p, allowsFieldFlow, config) and innercc = getCallContextCall(call, getNodeEnclosingCallable(p), outercc) @@ -2353,7 +2340,7 @@ private module Stage4 { private predicate fwdFlowIsEntered( DataFlowCall call, Cc cc, ApOption argAp, Ap ap, Configuration config ) { - exists(ParameterNodeExt p | + exists(ParamNode p | fwdFlowIn(call, p, cc, _, argAp, ap, config) and PrevStage::parameterMayFlowThrough(p, _, unbindApa(getApprox(ap)), config) ) @@ -2494,10 +2481,9 @@ private module Stage4 { pragma[nomagic] private predicate revFlowIn( - DataFlowCall call, ArgumentNodeExt arg, boolean toReturn, ApOption returnAp, Ap ap, - Configuration config + DataFlowCall call, ArgNode arg, boolean toReturn, ApOption returnAp, Ap ap, Configuration config ) { - exists(ParameterNodeExt p, boolean allowsFieldFlow | + exists(ParamNode p, boolean allowsFieldFlow | revFlow(p, toReturn, returnAp, ap, config) and flowIntoCall(call, arg, p, allowsFieldFlow, config) | @@ -2507,7 +2493,7 @@ private module Stage4 { pragma[nomagic] private predicate revFlowInToReturn( - DataFlowCall call, ArgumentNodeExt arg, Ap returnAp, Ap ap, Configuration config + DataFlowCall call, ArgNode arg, Ap returnAp, Ap ap, Configuration config ) { revFlowIn(call, arg, true, apSome(returnAp), ap, config) } @@ -2560,15 +2546,13 @@ private module Stage4 { pragma[noinline] private predicate parameterFlow( - ParameterNodeExt p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config + ParamNode p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config ) { revFlow(p, true, apSome(ap0), ap, config) and c = getNodeEnclosingCallable(p) } - predicate parameterMayFlowThrough( - ParameterNodeExt p, DataFlowCallable c, Ap ap, Configuration config - ) { + predicate parameterMayFlowThrough(ParamNode p, DataFlowCallable c, Ap ap, Configuration config) { exists(ReturnNodeExt ret, Ap ap0, ReturnKindExt kind, int pos | parameterFlow(p, ap, ap0, c, config) and c = getNodeEnclosingCallable(ret) and @@ -2613,7 +2597,7 @@ private predicate nodeMayUseSummary(Node n, AccessPathApprox apa, Configuration private newtype TSummaryCtx = TSummaryCtxNone() or - TSummaryCtxSome(ParameterNodeExt p, AccessPath ap) { + TSummaryCtxSome(ParamNode p, AccessPath ap) { Stage4::parameterMayFlowThrough(p, _, ap.getApprox(), _) } @@ -2634,7 +2618,7 @@ private class SummaryCtxNone extends SummaryCtx, TSummaryCtxNone { /** A summary context from which a flow summary can be generated. */ private class SummaryCtxSome extends SummaryCtx, TSummaryCtxSome { - private ParameterNodeExt p; + private ParamNode p; private AccessPath ap; SummaryCtxSome() { this = TSummaryCtxSome(p, ap) } @@ -3242,7 +3226,7 @@ pragma[noinline] private predicate pathIntoArg( PathNodeMid mid, int i, CallContext cc, DataFlowCall call, AccessPath ap, AccessPathApprox apa ) { - exists(ArgumentNodeExt arg | + exists(ArgNode arg | arg = mid.getNode() and cc = mid.getCallContext() and arg.argumentOf(call, i) and @@ -3255,7 +3239,7 @@ pragma[noinline] private predicate parameterCand( DataFlowCallable callable, int i, AccessPathApprox apa, Configuration config ) { - exists(ParameterNodeExt p | + exists(ParamNode p | Stage4::revFlow(p, _, _, apa, config) and p.isParameterOf(callable, i) ) @@ -3279,7 +3263,7 @@ private predicate pathIntoCallable0( * respectively. */ private predicate pathIntoCallable( - PathNodeMid mid, ParameterNodeExt p, CallContext outercc, CallContextCall innercc, SummaryCtx sc, + PathNodeMid mid, ParamNode p, CallContext outercc, CallContextCall innercc, SummaryCtx sc, DataFlowCall call ) { exists(int i, DataFlowCallable callable, AccessPath ap | @@ -3575,7 +3559,7 @@ private module FlowExploration { private newtype TSummaryCtx1 = TSummaryCtx1None() or - TSummaryCtx1Param(ParameterNodeExt p) + TSummaryCtx1Param(ParamNode p) private newtype TSummaryCtx2 = TSummaryCtx2None() or @@ -3931,7 +3915,7 @@ private module FlowExploration { PartialPathNodeFwd mid, int i, CallContext cc, DataFlowCall call, PartialAccessPath ap, Configuration config ) { - exists(ArgumentNodeExt arg | + exists(ArgNode arg | arg = mid.getNode() and cc = mid.getCallContext() and arg.argumentOf(call, i) and @@ -3950,7 +3934,7 @@ private module FlowExploration { } private predicate partialPathIntoCallable( - PartialPathNodeFwd mid, ParameterNodeExt p, CallContext outercc, CallContextCall innercc, + PartialPathNodeFwd mid, ParamNode p, CallContext outercc, CallContextCall innercc, TSummaryCtx1 sc1, TSummaryCtx2 sc2, DataFlowCall call, PartialAccessPath ap, Configuration config ) { @@ -3987,7 +3971,7 @@ private module FlowExploration { DataFlowCall call, PartialPathNodeFwd mid, ReturnKindExt kind, CallContext cc, PartialAccessPath ap, Configuration config ) { - exists(ParameterNodeExt p, CallContext innercc, TSummaryCtx1 sc1, TSummaryCtx2 sc2 | + exists(ParamNode p, CallContext innercc, TSummaryCtx1 sc1, TSummaryCtx2 sc2 | partialPathIntoCallable(mid, p, cc, innercc, sc1, sc2, call, _, config) and paramFlowsThroughInPartialPath(kind, innercc, sc1, sc2, ap, config) ) @@ -4044,7 +4028,7 @@ private module FlowExploration { apConsRev(ap, c, ap0, config) ) or - exists(ParameterNodeExt p | + exists(ParamNode p | mid.getNode() = p and viableParamArg(_, p, node) and sc1 = mid.getSummaryCtx1() and @@ -4122,7 +4106,7 @@ private module FlowExploration { int pos, TRevSummaryCtx1Some sc1, TRevSummaryCtx2Some sc2, RevPartialAccessPath ap, Configuration config ) { - exists(PartialPathNodeRev mid, ParameterNodeExt p | + exists(PartialPathNodeRev mid, ParamNode p | mid.getNode() = p and p.isParameterOf(_, pos) and sc1 = mid.getSummaryCtx1() and @@ -4145,7 +4129,7 @@ private module FlowExploration { pragma[nomagic] private predicate revPartialPathThroughCallable( - PartialPathNodeRev mid, ArgumentNodeExt node, RevPartialAccessPath ap, Configuration config + PartialPathNodeRev mid, ArgNode node, RevPartialAccessPath ap, Configuration config ) { exists(DataFlowCall call, int pos | revPartialPathThroughCallable0(call, mid, pos, ap, config) and 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 cf83530bcbd..058d66b1496 100644 --- a/cpp/ql/src/semmle/code/cpp/dataflow/internal/DataFlowImpl2.qll +++ b/cpp/ql/src/semmle/code/cpp/dataflow/internal/DataFlowImpl2.qll @@ -385,7 +385,7 @@ private module Stage1 { */ pragma[nomagic] private predicate fwdFlowIsEntered(DataFlowCall call, Cc cc, Configuration config) { - exists(ArgumentNodeExt arg | + exists(ArgNode arg | fwdFlow(arg, cc, config) and viableParamArg(call, _, arg) ) @@ -512,24 +512,22 @@ private module Stage1 { pragma[nomagic] predicate viableParamArgNodeCandFwd1( - DataFlowCall call, ParameterNodeExt p, ArgumentNodeExt arg, Configuration config + DataFlowCall call, ParamNode p, ArgNode arg, Configuration config ) { viableParamArg(call, p, arg) and fwdFlow(arg, config) } pragma[nomagic] - private predicate revFlowIn( - DataFlowCall call, ArgumentNodeExt arg, boolean toReturn, Configuration config - ) { - exists(ParameterNodeExt p | + private predicate revFlowIn(DataFlowCall call, ArgNode arg, boolean toReturn, Configuration config) { + exists(ParamNode p | revFlow(p, toReturn, config) and viableParamArgNodeCandFwd1(call, p, arg, config) ) } pragma[nomagic] - private predicate revFlowInToReturn(DataFlowCall call, ArgumentNodeExt arg, Configuration config) { + private predicate revFlowInToReturn(DataFlowCall call, ArgNode arg, Configuration config) { revFlowIn(call, arg, true, config) } @@ -594,9 +592,7 @@ private module Stage1 { * Holds if flow may enter through `p` and reach a return node making `p` a * candidate for the origin of a summary. */ - predicate parameterMayFlowThrough( - ParameterNodeExt p, DataFlowCallable c, Ap ap, Configuration config - ) { + predicate parameterMayFlowThrough(ParamNode p, DataFlowCallable c, Ap ap, Configuration config) { exists(ReturnKindExt kind | throughFlowNodeCand(p, config) and returnFlowCallableNodeCand(c, kind, config) and @@ -662,7 +658,7 @@ private predicate flowOutOfCallNodeCand1( pragma[nomagic] private predicate viableParamArgNodeCand1( - DataFlowCall call, ParameterNodeExt p, ArgumentNodeExt arg, Configuration config + DataFlowCall call, ParamNode p, ArgNode arg, Configuration config ) { Stage1::viableParamArgNodeCandFwd1(call, p, arg, config) and Stage1::revFlow(arg, config) @@ -674,7 +670,7 @@ private predicate viableParamArgNodeCand1( */ pragma[nomagic] private predicate flowIntoCallNodeCand1( - DataFlowCall call, ArgumentNodeExt arg, ParameterNodeExt p, Configuration config + DataFlowCall call, ArgNode arg, ParamNode p, Configuration config ) { viableParamArgNodeCand1(call, p, arg, config) and Stage1::revFlow(p, config) and @@ -734,8 +730,7 @@ private predicate flowOutOfCallNodeCand1( */ pragma[nomagic] private predicate flowIntoCallNodeCand1( - DataFlowCall call, ArgumentNodeExt arg, ParameterNodeExt p, boolean allowsFieldFlow, - Configuration config + DataFlowCall call, ArgNode arg, ParamNode p, boolean allowsFieldFlow, Configuration config ) { flowIntoCallNodeCand1(call, arg, p, config) and exists(int b, int j | @@ -943,10 +938,10 @@ private module Stage2 { pragma[nomagic] private predicate fwdFlowIn( - DataFlowCall call, ParameterNodeExt p, Cc outercc, Cc innercc, ApOption argAp, Ap ap, + DataFlowCall call, ParamNode p, Cc outercc, Cc innercc, ApOption argAp, Ap ap, Configuration config ) { - exists(ArgumentNodeExt arg, boolean allowsFieldFlow | + exists(ArgNode arg, boolean allowsFieldFlow | fwdFlow(arg, outercc, argAp, ap, config) and flowIntoCall(call, arg, p, allowsFieldFlow, config) and innercc = getCallContextCall(call, getNodeEnclosingCallable(p), outercc) @@ -991,7 +986,7 @@ private module Stage2 { private predicate fwdFlowIsEntered( DataFlowCall call, Cc cc, ApOption argAp, Ap ap, Configuration config ) { - exists(ParameterNodeExt p | + exists(ParamNode p | fwdFlowIn(call, p, cc, _, argAp, ap, config) and PrevStage::parameterMayFlowThrough(p, _, getApprox(ap), config) ) @@ -1132,10 +1127,9 @@ private module Stage2 { pragma[nomagic] private predicate revFlowIn( - DataFlowCall call, ArgumentNodeExt arg, boolean toReturn, ApOption returnAp, Ap ap, - Configuration config + DataFlowCall call, ArgNode arg, boolean toReturn, ApOption returnAp, Ap ap, Configuration config ) { - exists(ParameterNodeExt p, boolean allowsFieldFlow | + exists(ParamNode p, boolean allowsFieldFlow | revFlow(p, toReturn, returnAp, ap, config) and flowIntoCall(call, arg, p, allowsFieldFlow, config) | @@ -1145,7 +1139,7 @@ private module Stage2 { pragma[nomagic] private predicate revFlowInToReturn( - DataFlowCall call, ArgumentNodeExt arg, Ap returnAp, Ap ap, Configuration config + DataFlowCall call, ArgNode arg, Ap returnAp, Ap ap, Configuration config ) { revFlowIn(call, arg, true, apSome(returnAp), ap, config) } @@ -1198,15 +1192,13 @@ private module Stage2 { pragma[noinline] private predicate parameterFlow( - ParameterNodeExt p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config + ParamNode p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config ) { revFlow(p, true, apSome(ap0), ap, config) and c = getNodeEnclosingCallable(p) } - predicate parameterMayFlowThrough( - ParameterNodeExt p, DataFlowCallable c, Ap ap, Configuration config - ) { + predicate parameterMayFlowThrough(ParamNode p, DataFlowCallable c, Ap ap, Configuration config) { exists(ReturnNodeExt ret, Ap ap0, ReturnKindExt kind, int pos | parameterFlow(p, ap, ap0, c, config) and c = getNodeEnclosingCallable(ret) and @@ -1246,8 +1238,7 @@ private predicate flowOutOfCallNodeCand2( pragma[nomagic] private predicate flowIntoCallNodeCand2( - DataFlowCall call, ArgumentNodeExt node1, ParameterNodeExt node2, boolean allowsFieldFlow, - Configuration config + DataFlowCall call, ArgNode node1, ParamNode node2, boolean allowsFieldFlow, Configuration config ) { flowIntoCallNodeCand1(call, node1, node2, allowsFieldFlow, config) and Stage2::revFlow(node2, pragma[only_bind_into](config)) and @@ -1276,7 +1267,7 @@ private module LocalFlowBigStep { config.isSource(node) or jumpStep(_, node, config) or additionalJumpStep(_, node, config) or - node instanceof ParameterNodeExt or + node instanceof ParamNode or node instanceof OutNodeExt or store(_, _, node, _) or read(_, _, node) or @@ -1586,10 +1577,10 @@ private module Stage3 { pragma[nomagic] private predicate fwdFlowIn( - DataFlowCall call, ParameterNodeExt p, Cc outercc, Cc innercc, ApOption argAp, Ap ap, + DataFlowCall call, ParamNode p, Cc outercc, Cc innercc, ApOption argAp, Ap ap, Configuration config ) { - exists(ArgumentNodeExt arg, boolean allowsFieldFlow | + exists(ArgNode arg, boolean allowsFieldFlow | fwdFlow(arg, outercc, argAp, ap, config) and flowIntoCall(call, arg, p, allowsFieldFlow, config) and innercc = getCallContextCall(call, getNodeEnclosingCallable(p), outercc) @@ -1634,7 +1625,7 @@ private module Stage3 { private predicate fwdFlowIsEntered( DataFlowCall call, Cc cc, ApOption argAp, Ap ap, Configuration config ) { - exists(ParameterNodeExt p | + exists(ParamNode p | fwdFlowIn(call, p, cc, _, argAp, ap, config) and PrevStage::parameterMayFlowThrough(p, _, unbindApa(getApprox(ap)), config) ) @@ -1775,10 +1766,9 @@ private module Stage3 { pragma[nomagic] private predicate revFlowIn( - DataFlowCall call, ArgumentNodeExt arg, boolean toReturn, ApOption returnAp, Ap ap, - Configuration config + DataFlowCall call, ArgNode arg, boolean toReturn, ApOption returnAp, Ap ap, Configuration config ) { - exists(ParameterNodeExt p, boolean allowsFieldFlow | + exists(ParamNode p, boolean allowsFieldFlow | revFlow(p, toReturn, returnAp, ap, config) and flowIntoCall(call, arg, p, allowsFieldFlow, config) | @@ -1788,7 +1778,7 @@ private module Stage3 { pragma[nomagic] private predicate revFlowInToReturn( - DataFlowCall call, ArgumentNodeExt arg, Ap returnAp, Ap ap, Configuration config + DataFlowCall call, ArgNode arg, Ap returnAp, Ap ap, Configuration config ) { revFlowIn(call, arg, true, apSome(returnAp), ap, config) } @@ -1841,15 +1831,13 @@ private module Stage3 { pragma[noinline] private predicate parameterFlow( - ParameterNodeExt p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config + ParamNode p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config ) { revFlow(p, true, apSome(ap0), ap, config) and c = getNodeEnclosingCallable(p) } - predicate parameterMayFlowThrough( - ParameterNodeExt p, DataFlowCallable c, Ap ap, Configuration config - ) { + predicate parameterMayFlowThrough(ParamNode p, DataFlowCallable c, Ap ap, Configuration config) { exists(ReturnNodeExt ret, Ap ap0, ReturnKindExt kind, int pos | parameterFlow(p, ap, ap0, c, config) and c = getNodeEnclosingCallable(ret) and @@ -2160,8 +2148,7 @@ private module Stage4 { pragma[nomagic] private predicate flowIntoCall( - DataFlowCall call, ArgumentNodeExt node1, ParameterNodeExt node2, boolean allowsFieldFlow, - Configuration config + DataFlowCall call, ArgNode node1, ParamNode node2, boolean allowsFieldFlow, Configuration config ) { flowIntoCallNodeCand2(call, node1, node2, allowsFieldFlow, config) and PrevStage::revFlow(node2, _, _, _, pragma[only_bind_into](config)) and @@ -2305,10 +2292,10 @@ private module Stage4 { pragma[nomagic] private predicate fwdFlowIn( - DataFlowCall call, ParameterNodeExt p, Cc outercc, Cc innercc, ApOption argAp, Ap ap, + DataFlowCall call, ParamNode p, Cc outercc, Cc innercc, ApOption argAp, Ap ap, Configuration config ) { - exists(ArgumentNodeExt arg, boolean allowsFieldFlow | + exists(ArgNode arg, boolean allowsFieldFlow | fwdFlow(arg, outercc, argAp, ap, config) and flowIntoCall(call, arg, p, allowsFieldFlow, config) and innercc = getCallContextCall(call, getNodeEnclosingCallable(p), outercc) @@ -2353,7 +2340,7 @@ private module Stage4 { private predicate fwdFlowIsEntered( DataFlowCall call, Cc cc, ApOption argAp, Ap ap, Configuration config ) { - exists(ParameterNodeExt p | + exists(ParamNode p | fwdFlowIn(call, p, cc, _, argAp, ap, config) and PrevStage::parameterMayFlowThrough(p, _, unbindApa(getApprox(ap)), config) ) @@ -2494,10 +2481,9 @@ private module Stage4 { pragma[nomagic] private predicate revFlowIn( - DataFlowCall call, ArgumentNodeExt arg, boolean toReturn, ApOption returnAp, Ap ap, - Configuration config + DataFlowCall call, ArgNode arg, boolean toReturn, ApOption returnAp, Ap ap, Configuration config ) { - exists(ParameterNodeExt p, boolean allowsFieldFlow | + exists(ParamNode p, boolean allowsFieldFlow | revFlow(p, toReturn, returnAp, ap, config) and flowIntoCall(call, arg, p, allowsFieldFlow, config) | @@ -2507,7 +2493,7 @@ private module Stage4 { pragma[nomagic] private predicate revFlowInToReturn( - DataFlowCall call, ArgumentNodeExt arg, Ap returnAp, Ap ap, Configuration config + DataFlowCall call, ArgNode arg, Ap returnAp, Ap ap, Configuration config ) { revFlowIn(call, arg, true, apSome(returnAp), ap, config) } @@ -2560,15 +2546,13 @@ private module Stage4 { pragma[noinline] private predicate parameterFlow( - ParameterNodeExt p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config + ParamNode p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config ) { revFlow(p, true, apSome(ap0), ap, config) and c = getNodeEnclosingCallable(p) } - predicate parameterMayFlowThrough( - ParameterNodeExt p, DataFlowCallable c, Ap ap, Configuration config - ) { + predicate parameterMayFlowThrough(ParamNode p, DataFlowCallable c, Ap ap, Configuration config) { exists(ReturnNodeExt ret, Ap ap0, ReturnKindExt kind, int pos | parameterFlow(p, ap, ap0, c, config) and c = getNodeEnclosingCallable(ret) and @@ -2613,7 +2597,7 @@ private predicate nodeMayUseSummary(Node n, AccessPathApprox apa, Configuration private newtype TSummaryCtx = TSummaryCtxNone() or - TSummaryCtxSome(ParameterNodeExt p, AccessPath ap) { + TSummaryCtxSome(ParamNode p, AccessPath ap) { Stage4::parameterMayFlowThrough(p, _, ap.getApprox(), _) } @@ -2634,7 +2618,7 @@ private class SummaryCtxNone extends SummaryCtx, TSummaryCtxNone { /** A summary context from which a flow summary can be generated. */ private class SummaryCtxSome extends SummaryCtx, TSummaryCtxSome { - private ParameterNodeExt p; + private ParamNode p; private AccessPath ap; SummaryCtxSome() { this = TSummaryCtxSome(p, ap) } @@ -3242,7 +3226,7 @@ pragma[noinline] private predicate pathIntoArg( PathNodeMid mid, int i, CallContext cc, DataFlowCall call, AccessPath ap, AccessPathApprox apa ) { - exists(ArgumentNodeExt arg | + exists(ArgNode arg | arg = mid.getNode() and cc = mid.getCallContext() and arg.argumentOf(call, i) and @@ -3255,7 +3239,7 @@ pragma[noinline] private predicate parameterCand( DataFlowCallable callable, int i, AccessPathApprox apa, Configuration config ) { - exists(ParameterNodeExt p | + exists(ParamNode p | Stage4::revFlow(p, _, _, apa, config) and p.isParameterOf(callable, i) ) @@ -3279,7 +3263,7 @@ private predicate pathIntoCallable0( * respectively. */ private predicate pathIntoCallable( - PathNodeMid mid, ParameterNodeExt p, CallContext outercc, CallContextCall innercc, SummaryCtx sc, + PathNodeMid mid, ParamNode p, CallContext outercc, CallContextCall innercc, SummaryCtx sc, DataFlowCall call ) { exists(int i, DataFlowCallable callable, AccessPath ap | @@ -3575,7 +3559,7 @@ private module FlowExploration { private newtype TSummaryCtx1 = TSummaryCtx1None() or - TSummaryCtx1Param(ParameterNodeExt p) + TSummaryCtx1Param(ParamNode p) private newtype TSummaryCtx2 = TSummaryCtx2None() or @@ -3931,7 +3915,7 @@ private module FlowExploration { PartialPathNodeFwd mid, int i, CallContext cc, DataFlowCall call, PartialAccessPath ap, Configuration config ) { - exists(ArgumentNodeExt arg | + exists(ArgNode arg | arg = mid.getNode() and cc = mid.getCallContext() and arg.argumentOf(call, i) and @@ -3950,7 +3934,7 @@ private module FlowExploration { } private predicate partialPathIntoCallable( - PartialPathNodeFwd mid, ParameterNodeExt p, CallContext outercc, CallContextCall innercc, + PartialPathNodeFwd mid, ParamNode p, CallContext outercc, CallContextCall innercc, TSummaryCtx1 sc1, TSummaryCtx2 sc2, DataFlowCall call, PartialAccessPath ap, Configuration config ) { @@ -3987,7 +3971,7 @@ private module FlowExploration { DataFlowCall call, PartialPathNodeFwd mid, ReturnKindExt kind, CallContext cc, PartialAccessPath ap, Configuration config ) { - exists(ParameterNodeExt p, CallContext innercc, TSummaryCtx1 sc1, TSummaryCtx2 sc2 | + exists(ParamNode p, CallContext innercc, TSummaryCtx1 sc1, TSummaryCtx2 sc2 | partialPathIntoCallable(mid, p, cc, innercc, sc1, sc2, call, _, config) and paramFlowsThroughInPartialPath(kind, innercc, sc1, sc2, ap, config) ) @@ -4044,7 +4028,7 @@ private module FlowExploration { apConsRev(ap, c, ap0, config) ) or - exists(ParameterNodeExt p | + exists(ParamNode p | mid.getNode() = p and viableParamArg(_, p, node) and sc1 = mid.getSummaryCtx1() and @@ -4122,7 +4106,7 @@ private module FlowExploration { int pos, TRevSummaryCtx1Some sc1, TRevSummaryCtx2Some sc2, RevPartialAccessPath ap, Configuration config ) { - exists(PartialPathNodeRev mid, ParameterNodeExt p | + exists(PartialPathNodeRev mid, ParamNode p | mid.getNode() = p and p.isParameterOf(_, pos) and sc1 = mid.getSummaryCtx1() and @@ -4145,7 +4129,7 @@ private module FlowExploration { pragma[nomagic] private predicate revPartialPathThroughCallable( - PartialPathNodeRev mid, ArgumentNodeExt node, RevPartialAccessPath ap, Configuration config + PartialPathNodeRev mid, ArgNode node, RevPartialAccessPath ap, Configuration config ) { exists(DataFlowCall call, int pos | revPartialPathThroughCallable0(call, mid, pos, ap, config) and 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 cf83530bcbd..058d66b1496 100644 --- a/cpp/ql/src/semmle/code/cpp/dataflow/internal/DataFlowImpl3.qll +++ b/cpp/ql/src/semmle/code/cpp/dataflow/internal/DataFlowImpl3.qll @@ -385,7 +385,7 @@ private module Stage1 { */ pragma[nomagic] private predicate fwdFlowIsEntered(DataFlowCall call, Cc cc, Configuration config) { - exists(ArgumentNodeExt arg | + exists(ArgNode arg | fwdFlow(arg, cc, config) and viableParamArg(call, _, arg) ) @@ -512,24 +512,22 @@ private module Stage1 { pragma[nomagic] predicate viableParamArgNodeCandFwd1( - DataFlowCall call, ParameterNodeExt p, ArgumentNodeExt arg, Configuration config + DataFlowCall call, ParamNode p, ArgNode arg, Configuration config ) { viableParamArg(call, p, arg) and fwdFlow(arg, config) } pragma[nomagic] - private predicate revFlowIn( - DataFlowCall call, ArgumentNodeExt arg, boolean toReturn, Configuration config - ) { - exists(ParameterNodeExt p | + private predicate revFlowIn(DataFlowCall call, ArgNode arg, boolean toReturn, Configuration config) { + exists(ParamNode p | revFlow(p, toReturn, config) and viableParamArgNodeCandFwd1(call, p, arg, config) ) } pragma[nomagic] - private predicate revFlowInToReturn(DataFlowCall call, ArgumentNodeExt arg, Configuration config) { + private predicate revFlowInToReturn(DataFlowCall call, ArgNode arg, Configuration config) { revFlowIn(call, arg, true, config) } @@ -594,9 +592,7 @@ private module Stage1 { * Holds if flow may enter through `p` and reach a return node making `p` a * candidate for the origin of a summary. */ - predicate parameterMayFlowThrough( - ParameterNodeExt p, DataFlowCallable c, Ap ap, Configuration config - ) { + predicate parameterMayFlowThrough(ParamNode p, DataFlowCallable c, Ap ap, Configuration config) { exists(ReturnKindExt kind | throughFlowNodeCand(p, config) and returnFlowCallableNodeCand(c, kind, config) and @@ -662,7 +658,7 @@ private predicate flowOutOfCallNodeCand1( pragma[nomagic] private predicate viableParamArgNodeCand1( - DataFlowCall call, ParameterNodeExt p, ArgumentNodeExt arg, Configuration config + DataFlowCall call, ParamNode p, ArgNode arg, Configuration config ) { Stage1::viableParamArgNodeCandFwd1(call, p, arg, config) and Stage1::revFlow(arg, config) @@ -674,7 +670,7 @@ private predicate viableParamArgNodeCand1( */ pragma[nomagic] private predicate flowIntoCallNodeCand1( - DataFlowCall call, ArgumentNodeExt arg, ParameterNodeExt p, Configuration config + DataFlowCall call, ArgNode arg, ParamNode p, Configuration config ) { viableParamArgNodeCand1(call, p, arg, config) and Stage1::revFlow(p, config) and @@ -734,8 +730,7 @@ private predicate flowOutOfCallNodeCand1( */ pragma[nomagic] private predicate flowIntoCallNodeCand1( - DataFlowCall call, ArgumentNodeExt arg, ParameterNodeExt p, boolean allowsFieldFlow, - Configuration config + DataFlowCall call, ArgNode arg, ParamNode p, boolean allowsFieldFlow, Configuration config ) { flowIntoCallNodeCand1(call, arg, p, config) and exists(int b, int j | @@ -943,10 +938,10 @@ private module Stage2 { pragma[nomagic] private predicate fwdFlowIn( - DataFlowCall call, ParameterNodeExt p, Cc outercc, Cc innercc, ApOption argAp, Ap ap, + DataFlowCall call, ParamNode p, Cc outercc, Cc innercc, ApOption argAp, Ap ap, Configuration config ) { - exists(ArgumentNodeExt arg, boolean allowsFieldFlow | + exists(ArgNode arg, boolean allowsFieldFlow | fwdFlow(arg, outercc, argAp, ap, config) and flowIntoCall(call, arg, p, allowsFieldFlow, config) and innercc = getCallContextCall(call, getNodeEnclosingCallable(p), outercc) @@ -991,7 +986,7 @@ private module Stage2 { private predicate fwdFlowIsEntered( DataFlowCall call, Cc cc, ApOption argAp, Ap ap, Configuration config ) { - exists(ParameterNodeExt p | + exists(ParamNode p | fwdFlowIn(call, p, cc, _, argAp, ap, config) and PrevStage::parameterMayFlowThrough(p, _, getApprox(ap), config) ) @@ -1132,10 +1127,9 @@ private module Stage2 { pragma[nomagic] private predicate revFlowIn( - DataFlowCall call, ArgumentNodeExt arg, boolean toReturn, ApOption returnAp, Ap ap, - Configuration config + DataFlowCall call, ArgNode arg, boolean toReturn, ApOption returnAp, Ap ap, Configuration config ) { - exists(ParameterNodeExt p, boolean allowsFieldFlow | + exists(ParamNode p, boolean allowsFieldFlow | revFlow(p, toReturn, returnAp, ap, config) and flowIntoCall(call, arg, p, allowsFieldFlow, config) | @@ -1145,7 +1139,7 @@ private module Stage2 { pragma[nomagic] private predicate revFlowInToReturn( - DataFlowCall call, ArgumentNodeExt arg, Ap returnAp, Ap ap, Configuration config + DataFlowCall call, ArgNode arg, Ap returnAp, Ap ap, Configuration config ) { revFlowIn(call, arg, true, apSome(returnAp), ap, config) } @@ -1198,15 +1192,13 @@ private module Stage2 { pragma[noinline] private predicate parameterFlow( - ParameterNodeExt p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config + ParamNode p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config ) { revFlow(p, true, apSome(ap0), ap, config) and c = getNodeEnclosingCallable(p) } - predicate parameterMayFlowThrough( - ParameterNodeExt p, DataFlowCallable c, Ap ap, Configuration config - ) { + predicate parameterMayFlowThrough(ParamNode p, DataFlowCallable c, Ap ap, Configuration config) { exists(ReturnNodeExt ret, Ap ap0, ReturnKindExt kind, int pos | parameterFlow(p, ap, ap0, c, config) and c = getNodeEnclosingCallable(ret) and @@ -1246,8 +1238,7 @@ private predicate flowOutOfCallNodeCand2( pragma[nomagic] private predicate flowIntoCallNodeCand2( - DataFlowCall call, ArgumentNodeExt node1, ParameterNodeExt node2, boolean allowsFieldFlow, - Configuration config + DataFlowCall call, ArgNode node1, ParamNode node2, boolean allowsFieldFlow, Configuration config ) { flowIntoCallNodeCand1(call, node1, node2, allowsFieldFlow, config) and Stage2::revFlow(node2, pragma[only_bind_into](config)) and @@ -1276,7 +1267,7 @@ private module LocalFlowBigStep { config.isSource(node) or jumpStep(_, node, config) or additionalJumpStep(_, node, config) or - node instanceof ParameterNodeExt or + node instanceof ParamNode or node instanceof OutNodeExt or store(_, _, node, _) or read(_, _, node) or @@ -1586,10 +1577,10 @@ private module Stage3 { pragma[nomagic] private predicate fwdFlowIn( - DataFlowCall call, ParameterNodeExt p, Cc outercc, Cc innercc, ApOption argAp, Ap ap, + DataFlowCall call, ParamNode p, Cc outercc, Cc innercc, ApOption argAp, Ap ap, Configuration config ) { - exists(ArgumentNodeExt arg, boolean allowsFieldFlow | + exists(ArgNode arg, boolean allowsFieldFlow | fwdFlow(arg, outercc, argAp, ap, config) and flowIntoCall(call, arg, p, allowsFieldFlow, config) and innercc = getCallContextCall(call, getNodeEnclosingCallable(p), outercc) @@ -1634,7 +1625,7 @@ private module Stage3 { private predicate fwdFlowIsEntered( DataFlowCall call, Cc cc, ApOption argAp, Ap ap, Configuration config ) { - exists(ParameterNodeExt p | + exists(ParamNode p | fwdFlowIn(call, p, cc, _, argAp, ap, config) and PrevStage::parameterMayFlowThrough(p, _, unbindApa(getApprox(ap)), config) ) @@ -1775,10 +1766,9 @@ private module Stage3 { pragma[nomagic] private predicate revFlowIn( - DataFlowCall call, ArgumentNodeExt arg, boolean toReturn, ApOption returnAp, Ap ap, - Configuration config + DataFlowCall call, ArgNode arg, boolean toReturn, ApOption returnAp, Ap ap, Configuration config ) { - exists(ParameterNodeExt p, boolean allowsFieldFlow | + exists(ParamNode p, boolean allowsFieldFlow | revFlow(p, toReturn, returnAp, ap, config) and flowIntoCall(call, arg, p, allowsFieldFlow, config) | @@ -1788,7 +1778,7 @@ private module Stage3 { pragma[nomagic] private predicate revFlowInToReturn( - DataFlowCall call, ArgumentNodeExt arg, Ap returnAp, Ap ap, Configuration config + DataFlowCall call, ArgNode arg, Ap returnAp, Ap ap, Configuration config ) { revFlowIn(call, arg, true, apSome(returnAp), ap, config) } @@ -1841,15 +1831,13 @@ private module Stage3 { pragma[noinline] private predicate parameterFlow( - ParameterNodeExt p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config + ParamNode p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config ) { revFlow(p, true, apSome(ap0), ap, config) and c = getNodeEnclosingCallable(p) } - predicate parameterMayFlowThrough( - ParameterNodeExt p, DataFlowCallable c, Ap ap, Configuration config - ) { + predicate parameterMayFlowThrough(ParamNode p, DataFlowCallable c, Ap ap, Configuration config) { exists(ReturnNodeExt ret, Ap ap0, ReturnKindExt kind, int pos | parameterFlow(p, ap, ap0, c, config) and c = getNodeEnclosingCallable(ret) and @@ -2160,8 +2148,7 @@ private module Stage4 { pragma[nomagic] private predicate flowIntoCall( - DataFlowCall call, ArgumentNodeExt node1, ParameterNodeExt node2, boolean allowsFieldFlow, - Configuration config + DataFlowCall call, ArgNode node1, ParamNode node2, boolean allowsFieldFlow, Configuration config ) { flowIntoCallNodeCand2(call, node1, node2, allowsFieldFlow, config) and PrevStage::revFlow(node2, _, _, _, pragma[only_bind_into](config)) and @@ -2305,10 +2292,10 @@ private module Stage4 { pragma[nomagic] private predicate fwdFlowIn( - DataFlowCall call, ParameterNodeExt p, Cc outercc, Cc innercc, ApOption argAp, Ap ap, + DataFlowCall call, ParamNode p, Cc outercc, Cc innercc, ApOption argAp, Ap ap, Configuration config ) { - exists(ArgumentNodeExt arg, boolean allowsFieldFlow | + exists(ArgNode arg, boolean allowsFieldFlow | fwdFlow(arg, outercc, argAp, ap, config) and flowIntoCall(call, arg, p, allowsFieldFlow, config) and innercc = getCallContextCall(call, getNodeEnclosingCallable(p), outercc) @@ -2353,7 +2340,7 @@ private module Stage4 { private predicate fwdFlowIsEntered( DataFlowCall call, Cc cc, ApOption argAp, Ap ap, Configuration config ) { - exists(ParameterNodeExt p | + exists(ParamNode p | fwdFlowIn(call, p, cc, _, argAp, ap, config) and PrevStage::parameterMayFlowThrough(p, _, unbindApa(getApprox(ap)), config) ) @@ -2494,10 +2481,9 @@ private module Stage4 { pragma[nomagic] private predicate revFlowIn( - DataFlowCall call, ArgumentNodeExt arg, boolean toReturn, ApOption returnAp, Ap ap, - Configuration config + DataFlowCall call, ArgNode arg, boolean toReturn, ApOption returnAp, Ap ap, Configuration config ) { - exists(ParameterNodeExt p, boolean allowsFieldFlow | + exists(ParamNode p, boolean allowsFieldFlow | revFlow(p, toReturn, returnAp, ap, config) and flowIntoCall(call, arg, p, allowsFieldFlow, config) | @@ -2507,7 +2493,7 @@ private module Stage4 { pragma[nomagic] private predicate revFlowInToReturn( - DataFlowCall call, ArgumentNodeExt arg, Ap returnAp, Ap ap, Configuration config + DataFlowCall call, ArgNode arg, Ap returnAp, Ap ap, Configuration config ) { revFlowIn(call, arg, true, apSome(returnAp), ap, config) } @@ -2560,15 +2546,13 @@ private module Stage4 { pragma[noinline] private predicate parameterFlow( - ParameterNodeExt p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config + ParamNode p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config ) { revFlow(p, true, apSome(ap0), ap, config) and c = getNodeEnclosingCallable(p) } - predicate parameterMayFlowThrough( - ParameterNodeExt p, DataFlowCallable c, Ap ap, Configuration config - ) { + predicate parameterMayFlowThrough(ParamNode p, DataFlowCallable c, Ap ap, Configuration config) { exists(ReturnNodeExt ret, Ap ap0, ReturnKindExt kind, int pos | parameterFlow(p, ap, ap0, c, config) and c = getNodeEnclosingCallable(ret) and @@ -2613,7 +2597,7 @@ private predicate nodeMayUseSummary(Node n, AccessPathApprox apa, Configuration private newtype TSummaryCtx = TSummaryCtxNone() or - TSummaryCtxSome(ParameterNodeExt p, AccessPath ap) { + TSummaryCtxSome(ParamNode p, AccessPath ap) { Stage4::parameterMayFlowThrough(p, _, ap.getApprox(), _) } @@ -2634,7 +2618,7 @@ private class SummaryCtxNone extends SummaryCtx, TSummaryCtxNone { /** A summary context from which a flow summary can be generated. */ private class SummaryCtxSome extends SummaryCtx, TSummaryCtxSome { - private ParameterNodeExt p; + private ParamNode p; private AccessPath ap; SummaryCtxSome() { this = TSummaryCtxSome(p, ap) } @@ -3242,7 +3226,7 @@ pragma[noinline] private predicate pathIntoArg( PathNodeMid mid, int i, CallContext cc, DataFlowCall call, AccessPath ap, AccessPathApprox apa ) { - exists(ArgumentNodeExt arg | + exists(ArgNode arg | arg = mid.getNode() and cc = mid.getCallContext() and arg.argumentOf(call, i) and @@ -3255,7 +3239,7 @@ pragma[noinline] private predicate parameterCand( DataFlowCallable callable, int i, AccessPathApprox apa, Configuration config ) { - exists(ParameterNodeExt p | + exists(ParamNode p | Stage4::revFlow(p, _, _, apa, config) and p.isParameterOf(callable, i) ) @@ -3279,7 +3263,7 @@ private predicate pathIntoCallable0( * respectively. */ private predicate pathIntoCallable( - PathNodeMid mid, ParameterNodeExt p, CallContext outercc, CallContextCall innercc, SummaryCtx sc, + PathNodeMid mid, ParamNode p, CallContext outercc, CallContextCall innercc, SummaryCtx sc, DataFlowCall call ) { exists(int i, DataFlowCallable callable, AccessPath ap | @@ -3575,7 +3559,7 @@ private module FlowExploration { private newtype TSummaryCtx1 = TSummaryCtx1None() or - TSummaryCtx1Param(ParameterNodeExt p) + TSummaryCtx1Param(ParamNode p) private newtype TSummaryCtx2 = TSummaryCtx2None() or @@ -3931,7 +3915,7 @@ private module FlowExploration { PartialPathNodeFwd mid, int i, CallContext cc, DataFlowCall call, PartialAccessPath ap, Configuration config ) { - exists(ArgumentNodeExt arg | + exists(ArgNode arg | arg = mid.getNode() and cc = mid.getCallContext() and arg.argumentOf(call, i) and @@ -3950,7 +3934,7 @@ private module FlowExploration { } private predicate partialPathIntoCallable( - PartialPathNodeFwd mid, ParameterNodeExt p, CallContext outercc, CallContextCall innercc, + PartialPathNodeFwd mid, ParamNode p, CallContext outercc, CallContextCall innercc, TSummaryCtx1 sc1, TSummaryCtx2 sc2, DataFlowCall call, PartialAccessPath ap, Configuration config ) { @@ -3987,7 +3971,7 @@ private module FlowExploration { DataFlowCall call, PartialPathNodeFwd mid, ReturnKindExt kind, CallContext cc, PartialAccessPath ap, Configuration config ) { - exists(ParameterNodeExt p, CallContext innercc, TSummaryCtx1 sc1, TSummaryCtx2 sc2 | + exists(ParamNode p, CallContext innercc, TSummaryCtx1 sc1, TSummaryCtx2 sc2 | partialPathIntoCallable(mid, p, cc, innercc, sc1, sc2, call, _, config) and paramFlowsThroughInPartialPath(kind, innercc, sc1, sc2, ap, config) ) @@ -4044,7 +4028,7 @@ private module FlowExploration { apConsRev(ap, c, ap0, config) ) or - exists(ParameterNodeExt p | + exists(ParamNode p | mid.getNode() = p and viableParamArg(_, p, node) and sc1 = mid.getSummaryCtx1() and @@ -4122,7 +4106,7 @@ private module FlowExploration { int pos, TRevSummaryCtx1Some sc1, TRevSummaryCtx2Some sc2, RevPartialAccessPath ap, Configuration config ) { - exists(PartialPathNodeRev mid, ParameterNodeExt p | + exists(PartialPathNodeRev mid, ParamNode p | mid.getNode() = p and p.isParameterOf(_, pos) and sc1 = mid.getSummaryCtx1() and @@ -4145,7 +4129,7 @@ private module FlowExploration { pragma[nomagic] private predicate revPartialPathThroughCallable( - PartialPathNodeRev mid, ArgumentNodeExt node, RevPartialAccessPath ap, Configuration config + PartialPathNodeRev mid, ArgNode node, RevPartialAccessPath ap, Configuration config ) { exists(DataFlowCall call, int pos | revPartialPathThroughCallable0(call, mid, pos, ap, config) and 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 cf83530bcbd..058d66b1496 100644 --- a/cpp/ql/src/semmle/code/cpp/dataflow/internal/DataFlowImpl4.qll +++ b/cpp/ql/src/semmle/code/cpp/dataflow/internal/DataFlowImpl4.qll @@ -385,7 +385,7 @@ private module Stage1 { */ pragma[nomagic] private predicate fwdFlowIsEntered(DataFlowCall call, Cc cc, Configuration config) { - exists(ArgumentNodeExt arg | + exists(ArgNode arg | fwdFlow(arg, cc, config) and viableParamArg(call, _, arg) ) @@ -512,24 +512,22 @@ private module Stage1 { pragma[nomagic] predicate viableParamArgNodeCandFwd1( - DataFlowCall call, ParameterNodeExt p, ArgumentNodeExt arg, Configuration config + DataFlowCall call, ParamNode p, ArgNode arg, Configuration config ) { viableParamArg(call, p, arg) and fwdFlow(arg, config) } pragma[nomagic] - private predicate revFlowIn( - DataFlowCall call, ArgumentNodeExt arg, boolean toReturn, Configuration config - ) { - exists(ParameterNodeExt p | + private predicate revFlowIn(DataFlowCall call, ArgNode arg, boolean toReturn, Configuration config) { + exists(ParamNode p | revFlow(p, toReturn, config) and viableParamArgNodeCandFwd1(call, p, arg, config) ) } pragma[nomagic] - private predicate revFlowInToReturn(DataFlowCall call, ArgumentNodeExt arg, Configuration config) { + private predicate revFlowInToReturn(DataFlowCall call, ArgNode arg, Configuration config) { revFlowIn(call, arg, true, config) } @@ -594,9 +592,7 @@ private module Stage1 { * Holds if flow may enter through `p` and reach a return node making `p` a * candidate for the origin of a summary. */ - predicate parameterMayFlowThrough( - ParameterNodeExt p, DataFlowCallable c, Ap ap, Configuration config - ) { + predicate parameterMayFlowThrough(ParamNode p, DataFlowCallable c, Ap ap, Configuration config) { exists(ReturnKindExt kind | throughFlowNodeCand(p, config) and returnFlowCallableNodeCand(c, kind, config) and @@ -662,7 +658,7 @@ private predicate flowOutOfCallNodeCand1( pragma[nomagic] private predicate viableParamArgNodeCand1( - DataFlowCall call, ParameterNodeExt p, ArgumentNodeExt arg, Configuration config + DataFlowCall call, ParamNode p, ArgNode arg, Configuration config ) { Stage1::viableParamArgNodeCandFwd1(call, p, arg, config) and Stage1::revFlow(arg, config) @@ -674,7 +670,7 @@ private predicate viableParamArgNodeCand1( */ pragma[nomagic] private predicate flowIntoCallNodeCand1( - DataFlowCall call, ArgumentNodeExt arg, ParameterNodeExt p, Configuration config + DataFlowCall call, ArgNode arg, ParamNode p, Configuration config ) { viableParamArgNodeCand1(call, p, arg, config) and Stage1::revFlow(p, config) and @@ -734,8 +730,7 @@ private predicate flowOutOfCallNodeCand1( */ pragma[nomagic] private predicate flowIntoCallNodeCand1( - DataFlowCall call, ArgumentNodeExt arg, ParameterNodeExt p, boolean allowsFieldFlow, - Configuration config + DataFlowCall call, ArgNode arg, ParamNode p, boolean allowsFieldFlow, Configuration config ) { flowIntoCallNodeCand1(call, arg, p, config) and exists(int b, int j | @@ -943,10 +938,10 @@ private module Stage2 { pragma[nomagic] private predicate fwdFlowIn( - DataFlowCall call, ParameterNodeExt p, Cc outercc, Cc innercc, ApOption argAp, Ap ap, + DataFlowCall call, ParamNode p, Cc outercc, Cc innercc, ApOption argAp, Ap ap, Configuration config ) { - exists(ArgumentNodeExt arg, boolean allowsFieldFlow | + exists(ArgNode arg, boolean allowsFieldFlow | fwdFlow(arg, outercc, argAp, ap, config) and flowIntoCall(call, arg, p, allowsFieldFlow, config) and innercc = getCallContextCall(call, getNodeEnclosingCallable(p), outercc) @@ -991,7 +986,7 @@ private module Stage2 { private predicate fwdFlowIsEntered( DataFlowCall call, Cc cc, ApOption argAp, Ap ap, Configuration config ) { - exists(ParameterNodeExt p | + exists(ParamNode p | fwdFlowIn(call, p, cc, _, argAp, ap, config) and PrevStage::parameterMayFlowThrough(p, _, getApprox(ap), config) ) @@ -1132,10 +1127,9 @@ private module Stage2 { pragma[nomagic] private predicate revFlowIn( - DataFlowCall call, ArgumentNodeExt arg, boolean toReturn, ApOption returnAp, Ap ap, - Configuration config + DataFlowCall call, ArgNode arg, boolean toReturn, ApOption returnAp, Ap ap, Configuration config ) { - exists(ParameterNodeExt p, boolean allowsFieldFlow | + exists(ParamNode p, boolean allowsFieldFlow | revFlow(p, toReturn, returnAp, ap, config) and flowIntoCall(call, arg, p, allowsFieldFlow, config) | @@ -1145,7 +1139,7 @@ private module Stage2 { pragma[nomagic] private predicate revFlowInToReturn( - DataFlowCall call, ArgumentNodeExt arg, Ap returnAp, Ap ap, Configuration config + DataFlowCall call, ArgNode arg, Ap returnAp, Ap ap, Configuration config ) { revFlowIn(call, arg, true, apSome(returnAp), ap, config) } @@ -1198,15 +1192,13 @@ private module Stage2 { pragma[noinline] private predicate parameterFlow( - ParameterNodeExt p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config + ParamNode p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config ) { revFlow(p, true, apSome(ap0), ap, config) and c = getNodeEnclosingCallable(p) } - predicate parameterMayFlowThrough( - ParameterNodeExt p, DataFlowCallable c, Ap ap, Configuration config - ) { + predicate parameterMayFlowThrough(ParamNode p, DataFlowCallable c, Ap ap, Configuration config) { exists(ReturnNodeExt ret, Ap ap0, ReturnKindExt kind, int pos | parameterFlow(p, ap, ap0, c, config) and c = getNodeEnclosingCallable(ret) and @@ -1246,8 +1238,7 @@ private predicate flowOutOfCallNodeCand2( pragma[nomagic] private predicate flowIntoCallNodeCand2( - DataFlowCall call, ArgumentNodeExt node1, ParameterNodeExt node2, boolean allowsFieldFlow, - Configuration config + DataFlowCall call, ArgNode node1, ParamNode node2, boolean allowsFieldFlow, Configuration config ) { flowIntoCallNodeCand1(call, node1, node2, allowsFieldFlow, config) and Stage2::revFlow(node2, pragma[only_bind_into](config)) and @@ -1276,7 +1267,7 @@ private module LocalFlowBigStep { config.isSource(node) or jumpStep(_, node, config) or additionalJumpStep(_, node, config) or - node instanceof ParameterNodeExt or + node instanceof ParamNode or node instanceof OutNodeExt or store(_, _, node, _) or read(_, _, node) or @@ -1586,10 +1577,10 @@ private module Stage3 { pragma[nomagic] private predicate fwdFlowIn( - DataFlowCall call, ParameterNodeExt p, Cc outercc, Cc innercc, ApOption argAp, Ap ap, + DataFlowCall call, ParamNode p, Cc outercc, Cc innercc, ApOption argAp, Ap ap, Configuration config ) { - exists(ArgumentNodeExt arg, boolean allowsFieldFlow | + exists(ArgNode arg, boolean allowsFieldFlow | fwdFlow(arg, outercc, argAp, ap, config) and flowIntoCall(call, arg, p, allowsFieldFlow, config) and innercc = getCallContextCall(call, getNodeEnclosingCallable(p), outercc) @@ -1634,7 +1625,7 @@ private module Stage3 { private predicate fwdFlowIsEntered( DataFlowCall call, Cc cc, ApOption argAp, Ap ap, Configuration config ) { - exists(ParameterNodeExt p | + exists(ParamNode p | fwdFlowIn(call, p, cc, _, argAp, ap, config) and PrevStage::parameterMayFlowThrough(p, _, unbindApa(getApprox(ap)), config) ) @@ -1775,10 +1766,9 @@ private module Stage3 { pragma[nomagic] private predicate revFlowIn( - DataFlowCall call, ArgumentNodeExt arg, boolean toReturn, ApOption returnAp, Ap ap, - Configuration config + DataFlowCall call, ArgNode arg, boolean toReturn, ApOption returnAp, Ap ap, Configuration config ) { - exists(ParameterNodeExt p, boolean allowsFieldFlow | + exists(ParamNode p, boolean allowsFieldFlow | revFlow(p, toReturn, returnAp, ap, config) and flowIntoCall(call, arg, p, allowsFieldFlow, config) | @@ -1788,7 +1778,7 @@ private module Stage3 { pragma[nomagic] private predicate revFlowInToReturn( - DataFlowCall call, ArgumentNodeExt arg, Ap returnAp, Ap ap, Configuration config + DataFlowCall call, ArgNode arg, Ap returnAp, Ap ap, Configuration config ) { revFlowIn(call, arg, true, apSome(returnAp), ap, config) } @@ -1841,15 +1831,13 @@ private module Stage3 { pragma[noinline] private predicate parameterFlow( - ParameterNodeExt p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config + ParamNode p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config ) { revFlow(p, true, apSome(ap0), ap, config) and c = getNodeEnclosingCallable(p) } - predicate parameterMayFlowThrough( - ParameterNodeExt p, DataFlowCallable c, Ap ap, Configuration config - ) { + predicate parameterMayFlowThrough(ParamNode p, DataFlowCallable c, Ap ap, Configuration config) { exists(ReturnNodeExt ret, Ap ap0, ReturnKindExt kind, int pos | parameterFlow(p, ap, ap0, c, config) and c = getNodeEnclosingCallable(ret) and @@ -2160,8 +2148,7 @@ private module Stage4 { pragma[nomagic] private predicate flowIntoCall( - DataFlowCall call, ArgumentNodeExt node1, ParameterNodeExt node2, boolean allowsFieldFlow, - Configuration config + DataFlowCall call, ArgNode node1, ParamNode node2, boolean allowsFieldFlow, Configuration config ) { flowIntoCallNodeCand2(call, node1, node2, allowsFieldFlow, config) and PrevStage::revFlow(node2, _, _, _, pragma[only_bind_into](config)) and @@ -2305,10 +2292,10 @@ private module Stage4 { pragma[nomagic] private predicate fwdFlowIn( - DataFlowCall call, ParameterNodeExt p, Cc outercc, Cc innercc, ApOption argAp, Ap ap, + DataFlowCall call, ParamNode p, Cc outercc, Cc innercc, ApOption argAp, Ap ap, Configuration config ) { - exists(ArgumentNodeExt arg, boolean allowsFieldFlow | + exists(ArgNode arg, boolean allowsFieldFlow | fwdFlow(arg, outercc, argAp, ap, config) and flowIntoCall(call, arg, p, allowsFieldFlow, config) and innercc = getCallContextCall(call, getNodeEnclosingCallable(p), outercc) @@ -2353,7 +2340,7 @@ private module Stage4 { private predicate fwdFlowIsEntered( DataFlowCall call, Cc cc, ApOption argAp, Ap ap, Configuration config ) { - exists(ParameterNodeExt p | + exists(ParamNode p | fwdFlowIn(call, p, cc, _, argAp, ap, config) and PrevStage::parameterMayFlowThrough(p, _, unbindApa(getApprox(ap)), config) ) @@ -2494,10 +2481,9 @@ private module Stage4 { pragma[nomagic] private predicate revFlowIn( - DataFlowCall call, ArgumentNodeExt arg, boolean toReturn, ApOption returnAp, Ap ap, - Configuration config + DataFlowCall call, ArgNode arg, boolean toReturn, ApOption returnAp, Ap ap, Configuration config ) { - exists(ParameterNodeExt p, boolean allowsFieldFlow | + exists(ParamNode p, boolean allowsFieldFlow | revFlow(p, toReturn, returnAp, ap, config) and flowIntoCall(call, arg, p, allowsFieldFlow, config) | @@ -2507,7 +2493,7 @@ private module Stage4 { pragma[nomagic] private predicate revFlowInToReturn( - DataFlowCall call, ArgumentNodeExt arg, Ap returnAp, Ap ap, Configuration config + DataFlowCall call, ArgNode arg, Ap returnAp, Ap ap, Configuration config ) { revFlowIn(call, arg, true, apSome(returnAp), ap, config) } @@ -2560,15 +2546,13 @@ private module Stage4 { pragma[noinline] private predicate parameterFlow( - ParameterNodeExt p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config + ParamNode p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config ) { revFlow(p, true, apSome(ap0), ap, config) and c = getNodeEnclosingCallable(p) } - predicate parameterMayFlowThrough( - ParameterNodeExt p, DataFlowCallable c, Ap ap, Configuration config - ) { + predicate parameterMayFlowThrough(ParamNode p, DataFlowCallable c, Ap ap, Configuration config) { exists(ReturnNodeExt ret, Ap ap0, ReturnKindExt kind, int pos | parameterFlow(p, ap, ap0, c, config) and c = getNodeEnclosingCallable(ret) and @@ -2613,7 +2597,7 @@ private predicate nodeMayUseSummary(Node n, AccessPathApprox apa, Configuration private newtype TSummaryCtx = TSummaryCtxNone() or - TSummaryCtxSome(ParameterNodeExt p, AccessPath ap) { + TSummaryCtxSome(ParamNode p, AccessPath ap) { Stage4::parameterMayFlowThrough(p, _, ap.getApprox(), _) } @@ -2634,7 +2618,7 @@ private class SummaryCtxNone extends SummaryCtx, TSummaryCtxNone { /** A summary context from which a flow summary can be generated. */ private class SummaryCtxSome extends SummaryCtx, TSummaryCtxSome { - private ParameterNodeExt p; + private ParamNode p; private AccessPath ap; SummaryCtxSome() { this = TSummaryCtxSome(p, ap) } @@ -3242,7 +3226,7 @@ pragma[noinline] private predicate pathIntoArg( PathNodeMid mid, int i, CallContext cc, DataFlowCall call, AccessPath ap, AccessPathApprox apa ) { - exists(ArgumentNodeExt arg | + exists(ArgNode arg | arg = mid.getNode() and cc = mid.getCallContext() and arg.argumentOf(call, i) and @@ -3255,7 +3239,7 @@ pragma[noinline] private predicate parameterCand( DataFlowCallable callable, int i, AccessPathApprox apa, Configuration config ) { - exists(ParameterNodeExt p | + exists(ParamNode p | Stage4::revFlow(p, _, _, apa, config) and p.isParameterOf(callable, i) ) @@ -3279,7 +3263,7 @@ private predicate pathIntoCallable0( * respectively. */ private predicate pathIntoCallable( - PathNodeMid mid, ParameterNodeExt p, CallContext outercc, CallContextCall innercc, SummaryCtx sc, + PathNodeMid mid, ParamNode p, CallContext outercc, CallContextCall innercc, SummaryCtx sc, DataFlowCall call ) { exists(int i, DataFlowCallable callable, AccessPath ap | @@ -3575,7 +3559,7 @@ private module FlowExploration { private newtype TSummaryCtx1 = TSummaryCtx1None() or - TSummaryCtx1Param(ParameterNodeExt p) + TSummaryCtx1Param(ParamNode p) private newtype TSummaryCtx2 = TSummaryCtx2None() or @@ -3931,7 +3915,7 @@ private module FlowExploration { PartialPathNodeFwd mid, int i, CallContext cc, DataFlowCall call, PartialAccessPath ap, Configuration config ) { - exists(ArgumentNodeExt arg | + exists(ArgNode arg | arg = mid.getNode() and cc = mid.getCallContext() and arg.argumentOf(call, i) and @@ -3950,7 +3934,7 @@ private module FlowExploration { } private predicate partialPathIntoCallable( - PartialPathNodeFwd mid, ParameterNodeExt p, CallContext outercc, CallContextCall innercc, + PartialPathNodeFwd mid, ParamNode p, CallContext outercc, CallContextCall innercc, TSummaryCtx1 sc1, TSummaryCtx2 sc2, DataFlowCall call, PartialAccessPath ap, Configuration config ) { @@ -3987,7 +3971,7 @@ private module FlowExploration { DataFlowCall call, PartialPathNodeFwd mid, ReturnKindExt kind, CallContext cc, PartialAccessPath ap, Configuration config ) { - exists(ParameterNodeExt p, CallContext innercc, TSummaryCtx1 sc1, TSummaryCtx2 sc2 | + exists(ParamNode p, CallContext innercc, TSummaryCtx1 sc1, TSummaryCtx2 sc2 | partialPathIntoCallable(mid, p, cc, innercc, sc1, sc2, call, _, config) and paramFlowsThroughInPartialPath(kind, innercc, sc1, sc2, ap, config) ) @@ -4044,7 +4028,7 @@ private module FlowExploration { apConsRev(ap, c, ap0, config) ) or - exists(ParameterNodeExt p | + exists(ParamNode p | mid.getNode() = p and viableParamArg(_, p, node) and sc1 = mid.getSummaryCtx1() and @@ -4122,7 +4106,7 @@ private module FlowExploration { int pos, TRevSummaryCtx1Some sc1, TRevSummaryCtx2Some sc2, RevPartialAccessPath ap, Configuration config ) { - exists(PartialPathNodeRev mid, ParameterNodeExt p | + exists(PartialPathNodeRev mid, ParamNode p | mid.getNode() = p and p.isParameterOf(_, pos) and sc1 = mid.getSummaryCtx1() and @@ -4145,7 +4129,7 @@ private module FlowExploration { pragma[nomagic] private predicate revPartialPathThroughCallable( - PartialPathNodeRev mid, ArgumentNodeExt node, RevPartialAccessPath ap, Configuration config + PartialPathNodeRev mid, ArgNode node, RevPartialAccessPath ap, Configuration config ) { exists(DataFlowCall call, int pos | revPartialPathThroughCallable0(call, mid, pos, ap, config) and 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 b5ceabc605d..b8e08408911 100644 --- a/cpp/ql/src/semmle/code/cpp/dataflow/internal/DataFlowImplCommon.qll +++ b/cpp/ql/src/semmle/code/cpp/dataflow/internal/DataFlowImplCommon.qll @@ -35,24 +35,22 @@ predicate accessPathCostLimits(int apLimit, int tupleLimit) { * calls. For this reason, we cannot reuse the code from `DataFlowImpl.qll` directly. */ private module LambdaFlow { - private predicate viableParamNonLambda(DataFlowCall call, int i, ParameterNodeExt p) { + private predicate viableParamNonLambda(DataFlowCall call, int i, ParamNode p) { p.isParameterOf(viableCallable(call), i) } - private predicate viableParamLambda(DataFlowCall call, int i, ParameterNodeExt p) { + private predicate viableParamLambda(DataFlowCall call, int i, ParamNode p) { p.isParameterOf(viableCallableLambda(call, _), i) } - private predicate viableParamArgNonLambda( - DataFlowCall call, ParameterNodeExt p, ArgumentNodeExt arg - ) { + private predicate viableParamArgNonLambda(DataFlowCall call, ParamNode p, ArgNode arg) { exists(int i | viableParamNonLambda(call, i, p) and arg.argumentOf(call, i) ) } - private predicate viableParamArgLambda(DataFlowCall call, ParameterNodeExt p, ArgumentNodeExt arg) { + private predicate viableParamArgLambda(DataFlowCall call, ParamNode p, ArgNode arg) { exists(int i | viableParamLambda(call, i, p) and arg.argumentOf(call, i) @@ -120,7 +118,7 @@ private module LambdaFlow { boolean toJump, DataFlowCallOption lastCall ) { revLambdaFlow0(lambdaCall, kind, node, t, toReturn, toJump, lastCall) and - if castNode(node) or node instanceof ArgumentNodeExt or node instanceof ReturnNode + if castNode(node) or node instanceof ArgNode or node instanceof ReturnNode then compatibleTypes(t, getNodeDataFlowType(node)) else any() } @@ -178,7 +176,7 @@ private module LambdaFlow { ) or // flow into a callable - exists(ParameterNodeExt p, DataFlowCallOption lastCall0, DataFlowCall call | + exists(ParamNode p, DataFlowCallOption lastCall0, DataFlowCall call | revLambdaFlowIn(lambdaCall, kind, p, t, toJump, lastCall0) and ( if lastCall0 = TDataFlowCallNone() and toJump = false @@ -229,8 +227,8 @@ private module LambdaFlow { pragma[nomagic] predicate revLambdaFlowIn( - DataFlowCall lambdaCall, LambdaCallKind kind, ParameterNodeExt p, DataFlowType t, - boolean toJump, DataFlowCallOption lastCall + DataFlowCall lambdaCall, LambdaCallKind kind, ParamNode p, DataFlowType t, boolean toJump, + DataFlowCallOption lastCall ) { revLambdaFlow(lambdaCall, kind, p, t, false, toJump, lastCall) } @@ -276,7 +274,7 @@ private module Cached { predicate outNodeExt(Node n) { n instanceof OutNode or - n.(PostUpdateNode).getPreUpdateNode() instanceof ArgumentNodeExt + n.(PostUpdateNode).getPreUpdateNode() instanceof ArgNode } cached @@ -286,7 +284,7 @@ private module Cached { OutNodeExt getAnOutNodeExt(DataFlowCall call, ReturnKindExt k) { result = getAnOutNode(call, k.(ValueReturnKind).getKind()) or - exists(ArgumentNodeExt arg | + exists(ArgNode arg | result.(PostUpdateNode).getPreUpdateNode() = arg and arg.argumentOf(call, k.(ParamUpdateReturnKind).getPosition()) ) @@ -296,7 +294,7 @@ private module Cached { predicate returnNodeExt(Node n, ReturnKindExt k) { k = TValueReturn(n.(ReturnNode).getKind()) or - exists(ParameterNodeExt p, int pos | + exists(ParamNode p, int pos | parameterValueFlowsToPreUpdate(p, n) and p.isParameterOf(_, pos) and k = TParamUpdate(pos) @@ -309,7 +307,7 @@ private module Cached { cached predicate castingNode(Node n) { castNode(n) or - n instanceof ParameterNodeExt or + n instanceof ParamNode or n instanceof OutNodeExt or // For reads, `x.f`, we want to check that the tracked type after the read (which // is obtained by popping the head of the access path stack) is compatible with @@ -346,7 +344,7 @@ private module Cached { * The instance parameter is considered to have index `-1`. */ pragma[nomagic] - private predicate viableParam(DataFlowCall call, int i, ParameterNodeExt p) { + private predicate viableParam(DataFlowCall call, int i, ParamNode p) { p.isParameterOf(viableCallableExt(call), i) } @@ -355,7 +353,7 @@ private module Cached { * dispatch into account. */ cached - predicate viableParamArg(DataFlowCall call, ParameterNodeExt p, ArgumentNodeExt arg) { + predicate viableParamArg(DataFlowCall call, ParamNode p, ArgNode arg) { exists(int i | viableParam(call, i, p) and arg.argumentOf(call, i) and @@ -397,7 +395,7 @@ private module Cached { * `read` indicates whether it is contents of `p` that can flow to `node`. */ pragma[nomagic] - private predicate parameterValueFlowCand(ParameterNodeExt p, Node node, boolean read) { + private predicate parameterValueFlowCand(ParamNode p, Node node, boolean read) { p = node and read = false or @@ -415,27 +413,25 @@ private module Cached { ) or // flow through: no prior read - exists(ArgumentNodeExt arg | + exists(ArgNode arg | parameterValueFlowArgCand(p, arg, false) and argumentValueFlowsThroughCand(arg, node, read) ) or // flow through: no read inside method - exists(ArgumentNodeExt arg | + exists(ArgNode arg | parameterValueFlowArgCand(p, arg, read) and argumentValueFlowsThroughCand(arg, node, false) ) } pragma[nomagic] - private predicate parameterValueFlowArgCand( - ParameterNodeExt p, ArgumentNodeExt arg, boolean read - ) { + private predicate parameterValueFlowArgCand(ParamNode p, ArgNode arg, boolean read) { parameterValueFlowCand(p, arg, read) } pragma[nomagic] - predicate parameterValueFlowsToPreUpdateCand(ParameterNodeExt p, PostUpdateNode n) { + predicate parameterValueFlowsToPreUpdateCand(ParamNode p, PostUpdateNode n) { parameterValueFlowCand(p, n.getPreUpdateNode(), false) } @@ -447,7 +443,7 @@ private module Cached { * `read` indicates whether it is contents of `p` that can flow to the return * node. */ - predicate parameterValueFlowReturnCand(ParameterNodeExt p, ReturnKind kind, boolean read) { + predicate parameterValueFlowReturnCand(ParamNode p, ReturnKind kind, boolean read) { exists(ReturnNode ret | parameterValueFlowCand(p, ret, read) and kind = ret.getKind() @@ -456,9 +452,9 @@ private module Cached { pragma[nomagic] private predicate argumentValueFlowsThroughCand0( - DataFlowCall call, ArgumentNodeExt arg, ReturnKind kind, boolean read + DataFlowCall call, ArgNode arg, ReturnKind kind, boolean read ) { - exists(ParameterNodeExt param | viableParamArg(call, param, arg) | + exists(ParamNode param | viableParamArg(call, param, arg) | parameterValueFlowReturnCand(param, kind, read) ) } @@ -469,14 +465,14 @@ private module Cached { * * `read` indicates whether it is contents of `arg` that can flow to `out`. */ - predicate argumentValueFlowsThroughCand(ArgumentNodeExt arg, Node out, boolean read) { + predicate argumentValueFlowsThroughCand(ArgNode arg, Node out, boolean read) { exists(DataFlowCall call, ReturnKind kind | argumentValueFlowsThroughCand0(call, arg, kind, read) and out = getAnOutNode(call, kind) ) } - predicate cand(ParameterNodeExt p, Node n) { + predicate cand(ParamNode p, Node n) { parameterValueFlowCand(p, n, _) and ( parameterValueFlowReturnCand(p, _, _) @@ -503,7 +499,7 @@ private module Cached { * If a read step was taken, then `read` captures the `Content`, the * container type, and the content type. */ - predicate parameterValueFlow(ParameterNodeExt p, Node node, ReadStepTypesOption read) { + predicate parameterValueFlow(ParamNode p, Node node, ReadStepTypesOption read) { parameterValueFlow0(p, node, read) and if node instanceof CastingNode then @@ -517,7 +513,7 @@ private module Cached { } pragma[nomagic] - private predicate parameterValueFlow0(ParameterNodeExt p, Node node, ReadStepTypesOption read) { + private predicate parameterValueFlow0(ParamNode p, Node node, ReadStepTypesOption read) { p = node and Cand::cand(p, _) and read = TReadStepTypesNone() @@ -542,34 +538,32 @@ private module Cached { pragma[nomagic] private predicate parameterValueFlow0_0( - ReadStepTypesOption mustBeNone, ParameterNodeExt p, Node node, ReadStepTypesOption read + ReadStepTypesOption mustBeNone, ParamNode p, Node node, ReadStepTypesOption read ) { // flow through: no prior read - exists(ArgumentNodeExt arg | + exists(ArgNode arg | parameterValueFlowArg(p, arg, mustBeNone) and argumentValueFlowsThrough(arg, read, node) ) or // flow through: no read inside method - exists(ArgumentNodeExt arg | + exists(ArgNode arg | parameterValueFlowArg(p, arg, read) and argumentValueFlowsThrough(arg, mustBeNone, node) ) } pragma[nomagic] - private predicate parameterValueFlowArg( - ParameterNodeExt p, ArgumentNodeExt arg, ReadStepTypesOption read - ) { + private predicate parameterValueFlowArg(ParamNode p, ArgNode arg, ReadStepTypesOption read) { parameterValueFlow(p, arg, read) and Cand::argumentValueFlowsThroughCand(arg, _, _) } pragma[nomagic] private predicate argumentValueFlowsThrough0( - DataFlowCall call, ArgumentNodeExt arg, ReturnKind kind, ReadStepTypesOption read + DataFlowCall call, ArgNode arg, ReturnKind kind, ReadStepTypesOption read ) { - exists(ParameterNodeExt param | viableParamArg(call, param, arg) | + exists(ParamNode param | viableParamArg(call, param, arg) | parameterValueFlowReturn(param, kind, read) ) } @@ -583,7 +577,7 @@ private module Cached { * container type, and the content type. */ pragma[nomagic] - predicate argumentValueFlowsThrough(ArgumentNodeExt arg, ReadStepTypesOption read, Node out) { + predicate argumentValueFlowsThrough(ArgNode arg, ReadStepTypesOption read, Node out) { exists(DataFlowCall call, ReturnKind kind | argumentValueFlowsThrough0(call, arg, kind, read) and out = getAnOutNode(call, kind) @@ -603,7 +597,7 @@ private module Cached { * value-preserving steps and a single read step, not taking call * contexts into account, thus representing a getter-step. */ - predicate getterStep(ArgumentNodeExt arg, Content c, Node out) { + predicate getterStep(ArgNode arg, Content c, Node out) { argumentValueFlowsThrough(arg, TReadStepTypesSome(_, c, _), out) } @@ -616,7 +610,7 @@ private module Cached { * container type, and the content type. */ private predicate parameterValueFlowReturn( - ParameterNodeExt p, ReturnKind kind, ReadStepTypesOption read + ParamNode p, ReturnKind kind, ReadStepTypesOption read ) { exists(ReturnNode ret | parameterValueFlow(p, ret, read) and @@ -722,7 +716,7 @@ private module Cached { * Holds if `p` can flow to the pre-update node associated with post-update * node `n`, in the same callable, using only value-preserving steps. */ - private predicate parameterValueFlowsToPreUpdate(ParameterNodeExt p, PostUpdateNode n) { + private predicate parameterValueFlowsToPreUpdate(ParamNode p, PostUpdateNode n) { parameterValueFlow(p, n.getPreUpdateNode(), TReadStepTypesNone()) } @@ -778,8 +772,8 @@ private module Cached { // Does the language-specific simpleLocalFlowStep already model flow // from function input to output? fromPre = getAnOutNode(c, _) and - toPre.(ArgumentNodeExt).argumentOf(c, _) and - simpleLocalFlowStep(toPre.(ArgumentNodeExt), fromPre) + toPre.(ArgNode).argumentOf(c, _) and + simpleLocalFlowStep(toPre.(ArgNode), fromPre) ) or argumentValueFlowsThrough(toPre, TReadStepTypesNone(), fromPre) @@ -827,7 +821,7 @@ private module Cached { cached newtype TReturnKindExt = TValueReturn(ReturnKind kind) or - TParamUpdate(int pos) { exists(ParameterNodeExt p | p.isParameterOf(_, pos)) } + TParamUpdate(int pos) { exists(ParamNode p | p.isParameterOf(_, pos)) } cached newtype TBooleanOption = @@ -942,7 +936,7 @@ class CallContextSomeCall extends CallContextCall, TSomeCall { override string toString() { result = "CcSomeCall" } override predicate relevantFor(DataFlowCallable callable) { - exists(ParameterNodeExt p | getNodeEnclosingCallable(p) = callable) + exists(ParamNode p | getNodeEnclosingCallable(p) = callable) } override predicate matchesCall(DataFlowCall call) { any() } @@ -1005,8 +999,8 @@ LocalCallContext getLocalCallContext(CallContext ctx, DataFlowCallable callable) * The value of a parameter at function entry, viewed as a node in a data * flow graph. */ -class ParameterNodeExt extends Node { - ParameterNodeExt() { parameterNode(this, _, _) } +class ParamNode extends Node { + ParamNode() { parameterNode(this, _, _) } /** * Holds if this node is the parameter of callable `c` at the specified @@ -1016,8 +1010,8 @@ class ParameterNodeExt extends Node { } /** A data-flow node that represents a call argument. */ -class ArgumentNodeExt extends Node { - ArgumentNodeExt() { argumentNode(this, _, _) } +class ArgNode extends Node { + ArgNode() { argumentNode(this, _, _) } /** Holds if this argument occurs at the given position in the given call. */ final predicate argumentOf(DataFlowCall call, int pos) { argumentNode(this, call, pos) } 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 cf83530bcbd..058d66b1496 100644 --- a/cpp/ql/src/semmle/code/cpp/dataflow/internal/DataFlowImplLocal.qll +++ b/cpp/ql/src/semmle/code/cpp/dataflow/internal/DataFlowImplLocal.qll @@ -385,7 +385,7 @@ private module Stage1 { */ pragma[nomagic] private predicate fwdFlowIsEntered(DataFlowCall call, Cc cc, Configuration config) { - exists(ArgumentNodeExt arg | + exists(ArgNode arg | fwdFlow(arg, cc, config) and viableParamArg(call, _, arg) ) @@ -512,24 +512,22 @@ private module Stage1 { pragma[nomagic] predicate viableParamArgNodeCandFwd1( - DataFlowCall call, ParameterNodeExt p, ArgumentNodeExt arg, Configuration config + DataFlowCall call, ParamNode p, ArgNode arg, Configuration config ) { viableParamArg(call, p, arg) and fwdFlow(arg, config) } pragma[nomagic] - private predicate revFlowIn( - DataFlowCall call, ArgumentNodeExt arg, boolean toReturn, Configuration config - ) { - exists(ParameterNodeExt p | + private predicate revFlowIn(DataFlowCall call, ArgNode arg, boolean toReturn, Configuration config) { + exists(ParamNode p | revFlow(p, toReturn, config) and viableParamArgNodeCandFwd1(call, p, arg, config) ) } pragma[nomagic] - private predicate revFlowInToReturn(DataFlowCall call, ArgumentNodeExt arg, Configuration config) { + private predicate revFlowInToReturn(DataFlowCall call, ArgNode arg, Configuration config) { revFlowIn(call, arg, true, config) } @@ -594,9 +592,7 @@ private module Stage1 { * Holds if flow may enter through `p` and reach a return node making `p` a * candidate for the origin of a summary. */ - predicate parameterMayFlowThrough( - ParameterNodeExt p, DataFlowCallable c, Ap ap, Configuration config - ) { + predicate parameterMayFlowThrough(ParamNode p, DataFlowCallable c, Ap ap, Configuration config) { exists(ReturnKindExt kind | throughFlowNodeCand(p, config) and returnFlowCallableNodeCand(c, kind, config) and @@ -662,7 +658,7 @@ private predicate flowOutOfCallNodeCand1( pragma[nomagic] private predicate viableParamArgNodeCand1( - DataFlowCall call, ParameterNodeExt p, ArgumentNodeExt arg, Configuration config + DataFlowCall call, ParamNode p, ArgNode arg, Configuration config ) { Stage1::viableParamArgNodeCandFwd1(call, p, arg, config) and Stage1::revFlow(arg, config) @@ -674,7 +670,7 @@ private predicate viableParamArgNodeCand1( */ pragma[nomagic] private predicate flowIntoCallNodeCand1( - DataFlowCall call, ArgumentNodeExt arg, ParameterNodeExt p, Configuration config + DataFlowCall call, ArgNode arg, ParamNode p, Configuration config ) { viableParamArgNodeCand1(call, p, arg, config) and Stage1::revFlow(p, config) and @@ -734,8 +730,7 @@ private predicate flowOutOfCallNodeCand1( */ pragma[nomagic] private predicate flowIntoCallNodeCand1( - DataFlowCall call, ArgumentNodeExt arg, ParameterNodeExt p, boolean allowsFieldFlow, - Configuration config + DataFlowCall call, ArgNode arg, ParamNode p, boolean allowsFieldFlow, Configuration config ) { flowIntoCallNodeCand1(call, arg, p, config) and exists(int b, int j | @@ -943,10 +938,10 @@ private module Stage2 { pragma[nomagic] private predicate fwdFlowIn( - DataFlowCall call, ParameterNodeExt p, Cc outercc, Cc innercc, ApOption argAp, Ap ap, + DataFlowCall call, ParamNode p, Cc outercc, Cc innercc, ApOption argAp, Ap ap, Configuration config ) { - exists(ArgumentNodeExt arg, boolean allowsFieldFlow | + exists(ArgNode arg, boolean allowsFieldFlow | fwdFlow(arg, outercc, argAp, ap, config) and flowIntoCall(call, arg, p, allowsFieldFlow, config) and innercc = getCallContextCall(call, getNodeEnclosingCallable(p), outercc) @@ -991,7 +986,7 @@ private module Stage2 { private predicate fwdFlowIsEntered( DataFlowCall call, Cc cc, ApOption argAp, Ap ap, Configuration config ) { - exists(ParameterNodeExt p | + exists(ParamNode p | fwdFlowIn(call, p, cc, _, argAp, ap, config) and PrevStage::parameterMayFlowThrough(p, _, getApprox(ap), config) ) @@ -1132,10 +1127,9 @@ private module Stage2 { pragma[nomagic] private predicate revFlowIn( - DataFlowCall call, ArgumentNodeExt arg, boolean toReturn, ApOption returnAp, Ap ap, - Configuration config + DataFlowCall call, ArgNode arg, boolean toReturn, ApOption returnAp, Ap ap, Configuration config ) { - exists(ParameterNodeExt p, boolean allowsFieldFlow | + exists(ParamNode p, boolean allowsFieldFlow | revFlow(p, toReturn, returnAp, ap, config) and flowIntoCall(call, arg, p, allowsFieldFlow, config) | @@ -1145,7 +1139,7 @@ private module Stage2 { pragma[nomagic] private predicate revFlowInToReturn( - DataFlowCall call, ArgumentNodeExt arg, Ap returnAp, Ap ap, Configuration config + DataFlowCall call, ArgNode arg, Ap returnAp, Ap ap, Configuration config ) { revFlowIn(call, arg, true, apSome(returnAp), ap, config) } @@ -1198,15 +1192,13 @@ private module Stage2 { pragma[noinline] private predicate parameterFlow( - ParameterNodeExt p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config + ParamNode p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config ) { revFlow(p, true, apSome(ap0), ap, config) and c = getNodeEnclosingCallable(p) } - predicate parameterMayFlowThrough( - ParameterNodeExt p, DataFlowCallable c, Ap ap, Configuration config - ) { + predicate parameterMayFlowThrough(ParamNode p, DataFlowCallable c, Ap ap, Configuration config) { exists(ReturnNodeExt ret, Ap ap0, ReturnKindExt kind, int pos | parameterFlow(p, ap, ap0, c, config) and c = getNodeEnclosingCallable(ret) and @@ -1246,8 +1238,7 @@ private predicate flowOutOfCallNodeCand2( pragma[nomagic] private predicate flowIntoCallNodeCand2( - DataFlowCall call, ArgumentNodeExt node1, ParameterNodeExt node2, boolean allowsFieldFlow, - Configuration config + DataFlowCall call, ArgNode node1, ParamNode node2, boolean allowsFieldFlow, Configuration config ) { flowIntoCallNodeCand1(call, node1, node2, allowsFieldFlow, config) and Stage2::revFlow(node2, pragma[only_bind_into](config)) and @@ -1276,7 +1267,7 @@ private module LocalFlowBigStep { config.isSource(node) or jumpStep(_, node, config) or additionalJumpStep(_, node, config) or - node instanceof ParameterNodeExt or + node instanceof ParamNode or node instanceof OutNodeExt or store(_, _, node, _) or read(_, _, node) or @@ -1586,10 +1577,10 @@ private module Stage3 { pragma[nomagic] private predicate fwdFlowIn( - DataFlowCall call, ParameterNodeExt p, Cc outercc, Cc innercc, ApOption argAp, Ap ap, + DataFlowCall call, ParamNode p, Cc outercc, Cc innercc, ApOption argAp, Ap ap, Configuration config ) { - exists(ArgumentNodeExt arg, boolean allowsFieldFlow | + exists(ArgNode arg, boolean allowsFieldFlow | fwdFlow(arg, outercc, argAp, ap, config) and flowIntoCall(call, arg, p, allowsFieldFlow, config) and innercc = getCallContextCall(call, getNodeEnclosingCallable(p), outercc) @@ -1634,7 +1625,7 @@ private module Stage3 { private predicate fwdFlowIsEntered( DataFlowCall call, Cc cc, ApOption argAp, Ap ap, Configuration config ) { - exists(ParameterNodeExt p | + exists(ParamNode p | fwdFlowIn(call, p, cc, _, argAp, ap, config) and PrevStage::parameterMayFlowThrough(p, _, unbindApa(getApprox(ap)), config) ) @@ -1775,10 +1766,9 @@ private module Stage3 { pragma[nomagic] private predicate revFlowIn( - DataFlowCall call, ArgumentNodeExt arg, boolean toReturn, ApOption returnAp, Ap ap, - Configuration config + DataFlowCall call, ArgNode arg, boolean toReturn, ApOption returnAp, Ap ap, Configuration config ) { - exists(ParameterNodeExt p, boolean allowsFieldFlow | + exists(ParamNode p, boolean allowsFieldFlow | revFlow(p, toReturn, returnAp, ap, config) and flowIntoCall(call, arg, p, allowsFieldFlow, config) | @@ -1788,7 +1778,7 @@ private module Stage3 { pragma[nomagic] private predicate revFlowInToReturn( - DataFlowCall call, ArgumentNodeExt arg, Ap returnAp, Ap ap, Configuration config + DataFlowCall call, ArgNode arg, Ap returnAp, Ap ap, Configuration config ) { revFlowIn(call, arg, true, apSome(returnAp), ap, config) } @@ -1841,15 +1831,13 @@ private module Stage3 { pragma[noinline] private predicate parameterFlow( - ParameterNodeExt p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config + ParamNode p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config ) { revFlow(p, true, apSome(ap0), ap, config) and c = getNodeEnclosingCallable(p) } - predicate parameterMayFlowThrough( - ParameterNodeExt p, DataFlowCallable c, Ap ap, Configuration config - ) { + predicate parameterMayFlowThrough(ParamNode p, DataFlowCallable c, Ap ap, Configuration config) { exists(ReturnNodeExt ret, Ap ap0, ReturnKindExt kind, int pos | parameterFlow(p, ap, ap0, c, config) and c = getNodeEnclosingCallable(ret) and @@ -2160,8 +2148,7 @@ private module Stage4 { pragma[nomagic] private predicate flowIntoCall( - DataFlowCall call, ArgumentNodeExt node1, ParameterNodeExt node2, boolean allowsFieldFlow, - Configuration config + DataFlowCall call, ArgNode node1, ParamNode node2, boolean allowsFieldFlow, Configuration config ) { flowIntoCallNodeCand2(call, node1, node2, allowsFieldFlow, config) and PrevStage::revFlow(node2, _, _, _, pragma[only_bind_into](config)) and @@ -2305,10 +2292,10 @@ private module Stage4 { pragma[nomagic] private predicate fwdFlowIn( - DataFlowCall call, ParameterNodeExt p, Cc outercc, Cc innercc, ApOption argAp, Ap ap, + DataFlowCall call, ParamNode p, Cc outercc, Cc innercc, ApOption argAp, Ap ap, Configuration config ) { - exists(ArgumentNodeExt arg, boolean allowsFieldFlow | + exists(ArgNode arg, boolean allowsFieldFlow | fwdFlow(arg, outercc, argAp, ap, config) and flowIntoCall(call, arg, p, allowsFieldFlow, config) and innercc = getCallContextCall(call, getNodeEnclosingCallable(p), outercc) @@ -2353,7 +2340,7 @@ private module Stage4 { private predicate fwdFlowIsEntered( DataFlowCall call, Cc cc, ApOption argAp, Ap ap, Configuration config ) { - exists(ParameterNodeExt p | + exists(ParamNode p | fwdFlowIn(call, p, cc, _, argAp, ap, config) and PrevStage::parameterMayFlowThrough(p, _, unbindApa(getApprox(ap)), config) ) @@ -2494,10 +2481,9 @@ private module Stage4 { pragma[nomagic] private predicate revFlowIn( - DataFlowCall call, ArgumentNodeExt arg, boolean toReturn, ApOption returnAp, Ap ap, - Configuration config + DataFlowCall call, ArgNode arg, boolean toReturn, ApOption returnAp, Ap ap, Configuration config ) { - exists(ParameterNodeExt p, boolean allowsFieldFlow | + exists(ParamNode p, boolean allowsFieldFlow | revFlow(p, toReturn, returnAp, ap, config) and flowIntoCall(call, arg, p, allowsFieldFlow, config) | @@ -2507,7 +2493,7 @@ private module Stage4 { pragma[nomagic] private predicate revFlowInToReturn( - DataFlowCall call, ArgumentNodeExt arg, Ap returnAp, Ap ap, Configuration config + DataFlowCall call, ArgNode arg, Ap returnAp, Ap ap, Configuration config ) { revFlowIn(call, arg, true, apSome(returnAp), ap, config) } @@ -2560,15 +2546,13 @@ private module Stage4 { pragma[noinline] private predicate parameterFlow( - ParameterNodeExt p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config + ParamNode p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config ) { revFlow(p, true, apSome(ap0), ap, config) and c = getNodeEnclosingCallable(p) } - predicate parameterMayFlowThrough( - ParameterNodeExt p, DataFlowCallable c, Ap ap, Configuration config - ) { + predicate parameterMayFlowThrough(ParamNode p, DataFlowCallable c, Ap ap, Configuration config) { exists(ReturnNodeExt ret, Ap ap0, ReturnKindExt kind, int pos | parameterFlow(p, ap, ap0, c, config) and c = getNodeEnclosingCallable(ret) and @@ -2613,7 +2597,7 @@ private predicate nodeMayUseSummary(Node n, AccessPathApprox apa, Configuration private newtype TSummaryCtx = TSummaryCtxNone() or - TSummaryCtxSome(ParameterNodeExt p, AccessPath ap) { + TSummaryCtxSome(ParamNode p, AccessPath ap) { Stage4::parameterMayFlowThrough(p, _, ap.getApprox(), _) } @@ -2634,7 +2618,7 @@ private class SummaryCtxNone extends SummaryCtx, TSummaryCtxNone { /** A summary context from which a flow summary can be generated. */ private class SummaryCtxSome extends SummaryCtx, TSummaryCtxSome { - private ParameterNodeExt p; + private ParamNode p; private AccessPath ap; SummaryCtxSome() { this = TSummaryCtxSome(p, ap) } @@ -3242,7 +3226,7 @@ pragma[noinline] private predicate pathIntoArg( PathNodeMid mid, int i, CallContext cc, DataFlowCall call, AccessPath ap, AccessPathApprox apa ) { - exists(ArgumentNodeExt arg | + exists(ArgNode arg | arg = mid.getNode() and cc = mid.getCallContext() and arg.argumentOf(call, i) and @@ -3255,7 +3239,7 @@ pragma[noinline] private predicate parameterCand( DataFlowCallable callable, int i, AccessPathApprox apa, Configuration config ) { - exists(ParameterNodeExt p | + exists(ParamNode p | Stage4::revFlow(p, _, _, apa, config) and p.isParameterOf(callable, i) ) @@ -3279,7 +3263,7 @@ private predicate pathIntoCallable0( * respectively. */ private predicate pathIntoCallable( - PathNodeMid mid, ParameterNodeExt p, CallContext outercc, CallContextCall innercc, SummaryCtx sc, + PathNodeMid mid, ParamNode p, CallContext outercc, CallContextCall innercc, SummaryCtx sc, DataFlowCall call ) { exists(int i, DataFlowCallable callable, AccessPath ap | @@ -3575,7 +3559,7 @@ private module FlowExploration { private newtype TSummaryCtx1 = TSummaryCtx1None() or - TSummaryCtx1Param(ParameterNodeExt p) + TSummaryCtx1Param(ParamNode p) private newtype TSummaryCtx2 = TSummaryCtx2None() or @@ -3931,7 +3915,7 @@ private module FlowExploration { PartialPathNodeFwd mid, int i, CallContext cc, DataFlowCall call, PartialAccessPath ap, Configuration config ) { - exists(ArgumentNodeExt arg | + exists(ArgNode arg | arg = mid.getNode() and cc = mid.getCallContext() and arg.argumentOf(call, i) and @@ -3950,7 +3934,7 @@ private module FlowExploration { } private predicate partialPathIntoCallable( - PartialPathNodeFwd mid, ParameterNodeExt p, CallContext outercc, CallContextCall innercc, + PartialPathNodeFwd mid, ParamNode p, CallContext outercc, CallContextCall innercc, TSummaryCtx1 sc1, TSummaryCtx2 sc2, DataFlowCall call, PartialAccessPath ap, Configuration config ) { @@ -3987,7 +3971,7 @@ private module FlowExploration { DataFlowCall call, PartialPathNodeFwd mid, ReturnKindExt kind, CallContext cc, PartialAccessPath ap, Configuration config ) { - exists(ParameterNodeExt p, CallContext innercc, TSummaryCtx1 sc1, TSummaryCtx2 sc2 | + exists(ParamNode p, CallContext innercc, TSummaryCtx1 sc1, TSummaryCtx2 sc2 | partialPathIntoCallable(mid, p, cc, innercc, sc1, sc2, call, _, config) and paramFlowsThroughInPartialPath(kind, innercc, sc1, sc2, ap, config) ) @@ -4044,7 +4028,7 @@ private module FlowExploration { apConsRev(ap, c, ap0, config) ) or - exists(ParameterNodeExt p | + exists(ParamNode p | mid.getNode() = p and viableParamArg(_, p, node) and sc1 = mid.getSummaryCtx1() and @@ -4122,7 +4106,7 @@ private module FlowExploration { int pos, TRevSummaryCtx1Some sc1, TRevSummaryCtx2Some sc2, RevPartialAccessPath ap, Configuration config ) { - exists(PartialPathNodeRev mid, ParameterNodeExt p | + exists(PartialPathNodeRev mid, ParamNode p | mid.getNode() = p and p.isParameterOf(_, pos) and sc1 = mid.getSummaryCtx1() and @@ -4145,7 +4129,7 @@ private module FlowExploration { pragma[nomagic] private predicate revPartialPathThroughCallable( - PartialPathNodeRev mid, ArgumentNodeExt node, RevPartialAccessPath ap, Configuration config + PartialPathNodeRev mid, ArgNode node, RevPartialAccessPath ap, Configuration config ) { exists(DataFlowCall call, int pos | revPartialPathThroughCallable0(call, mid, pos, ap, config) and 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 cf83530bcbd..058d66b1496 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 @@ -385,7 +385,7 @@ private module Stage1 { */ pragma[nomagic] private predicate fwdFlowIsEntered(DataFlowCall call, Cc cc, Configuration config) { - exists(ArgumentNodeExt arg | + exists(ArgNode arg | fwdFlow(arg, cc, config) and viableParamArg(call, _, arg) ) @@ -512,24 +512,22 @@ private module Stage1 { pragma[nomagic] predicate viableParamArgNodeCandFwd1( - DataFlowCall call, ParameterNodeExt p, ArgumentNodeExt arg, Configuration config + DataFlowCall call, ParamNode p, ArgNode arg, Configuration config ) { viableParamArg(call, p, arg) and fwdFlow(arg, config) } pragma[nomagic] - private predicate revFlowIn( - DataFlowCall call, ArgumentNodeExt arg, boolean toReturn, Configuration config - ) { - exists(ParameterNodeExt p | + private predicate revFlowIn(DataFlowCall call, ArgNode arg, boolean toReturn, Configuration config) { + exists(ParamNode p | revFlow(p, toReturn, config) and viableParamArgNodeCandFwd1(call, p, arg, config) ) } pragma[nomagic] - private predicate revFlowInToReturn(DataFlowCall call, ArgumentNodeExt arg, Configuration config) { + private predicate revFlowInToReturn(DataFlowCall call, ArgNode arg, Configuration config) { revFlowIn(call, arg, true, config) } @@ -594,9 +592,7 @@ private module Stage1 { * Holds if flow may enter through `p` and reach a return node making `p` a * candidate for the origin of a summary. */ - predicate parameterMayFlowThrough( - ParameterNodeExt p, DataFlowCallable c, Ap ap, Configuration config - ) { + predicate parameterMayFlowThrough(ParamNode p, DataFlowCallable c, Ap ap, Configuration config) { exists(ReturnKindExt kind | throughFlowNodeCand(p, config) and returnFlowCallableNodeCand(c, kind, config) and @@ -662,7 +658,7 @@ private predicate flowOutOfCallNodeCand1( pragma[nomagic] private predicate viableParamArgNodeCand1( - DataFlowCall call, ParameterNodeExt p, ArgumentNodeExt arg, Configuration config + DataFlowCall call, ParamNode p, ArgNode arg, Configuration config ) { Stage1::viableParamArgNodeCandFwd1(call, p, arg, config) and Stage1::revFlow(arg, config) @@ -674,7 +670,7 @@ private predicate viableParamArgNodeCand1( */ pragma[nomagic] private predicate flowIntoCallNodeCand1( - DataFlowCall call, ArgumentNodeExt arg, ParameterNodeExt p, Configuration config + DataFlowCall call, ArgNode arg, ParamNode p, Configuration config ) { viableParamArgNodeCand1(call, p, arg, config) and Stage1::revFlow(p, config) and @@ -734,8 +730,7 @@ private predicate flowOutOfCallNodeCand1( */ pragma[nomagic] private predicate flowIntoCallNodeCand1( - DataFlowCall call, ArgumentNodeExt arg, ParameterNodeExt p, boolean allowsFieldFlow, - Configuration config + DataFlowCall call, ArgNode arg, ParamNode p, boolean allowsFieldFlow, Configuration config ) { flowIntoCallNodeCand1(call, arg, p, config) and exists(int b, int j | @@ -943,10 +938,10 @@ private module Stage2 { pragma[nomagic] private predicate fwdFlowIn( - DataFlowCall call, ParameterNodeExt p, Cc outercc, Cc innercc, ApOption argAp, Ap ap, + DataFlowCall call, ParamNode p, Cc outercc, Cc innercc, ApOption argAp, Ap ap, Configuration config ) { - exists(ArgumentNodeExt arg, boolean allowsFieldFlow | + exists(ArgNode arg, boolean allowsFieldFlow | fwdFlow(arg, outercc, argAp, ap, config) and flowIntoCall(call, arg, p, allowsFieldFlow, config) and innercc = getCallContextCall(call, getNodeEnclosingCallable(p), outercc) @@ -991,7 +986,7 @@ private module Stage2 { private predicate fwdFlowIsEntered( DataFlowCall call, Cc cc, ApOption argAp, Ap ap, Configuration config ) { - exists(ParameterNodeExt p | + exists(ParamNode p | fwdFlowIn(call, p, cc, _, argAp, ap, config) and PrevStage::parameterMayFlowThrough(p, _, getApprox(ap), config) ) @@ -1132,10 +1127,9 @@ private module Stage2 { pragma[nomagic] private predicate revFlowIn( - DataFlowCall call, ArgumentNodeExt arg, boolean toReturn, ApOption returnAp, Ap ap, - Configuration config + DataFlowCall call, ArgNode arg, boolean toReturn, ApOption returnAp, Ap ap, Configuration config ) { - exists(ParameterNodeExt p, boolean allowsFieldFlow | + exists(ParamNode p, boolean allowsFieldFlow | revFlow(p, toReturn, returnAp, ap, config) and flowIntoCall(call, arg, p, allowsFieldFlow, config) | @@ -1145,7 +1139,7 @@ private module Stage2 { pragma[nomagic] private predicate revFlowInToReturn( - DataFlowCall call, ArgumentNodeExt arg, Ap returnAp, Ap ap, Configuration config + DataFlowCall call, ArgNode arg, Ap returnAp, Ap ap, Configuration config ) { revFlowIn(call, arg, true, apSome(returnAp), ap, config) } @@ -1198,15 +1192,13 @@ private module Stage2 { pragma[noinline] private predicate parameterFlow( - ParameterNodeExt p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config + ParamNode p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config ) { revFlow(p, true, apSome(ap0), ap, config) and c = getNodeEnclosingCallable(p) } - predicate parameterMayFlowThrough( - ParameterNodeExt p, DataFlowCallable c, Ap ap, Configuration config - ) { + predicate parameterMayFlowThrough(ParamNode p, DataFlowCallable c, Ap ap, Configuration config) { exists(ReturnNodeExt ret, Ap ap0, ReturnKindExt kind, int pos | parameterFlow(p, ap, ap0, c, config) and c = getNodeEnclosingCallable(ret) and @@ -1246,8 +1238,7 @@ private predicate flowOutOfCallNodeCand2( pragma[nomagic] private predicate flowIntoCallNodeCand2( - DataFlowCall call, ArgumentNodeExt node1, ParameterNodeExt node2, boolean allowsFieldFlow, - Configuration config + DataFlowCall call, ArgNode node1, ParamNode node2, boolean allowsFieldFlow, Configuration config ) { flowIntoCallNodeCand1(call, node1, node2, allowsFieldFlow, config) and Stage2::revFlow(node2, pragma[only_bind_into](config)) and @@ -1276,7 +1267,7 @@ private module LocalFlowBigStep { config.isSource(node) or jumpStep(_, node, config) or additionalJumpStep(_, node, config) or - node instanceof ParameterNodeExt or + node instanceof ParamNode or node instanceof OutNodeExt or store(_, _, node, _) or read(_, _, node) or @@ -1586,10 +1577,10 @@ private module Stage3 { pragma[nomagic] private predicate fwdFlowIn( - DataFlowCall call, ParameterNodeExt p, Cc outercc, Cc innercc, ApOption argAp, Ap ap, + DataFlowCall call, ParamNode p, Cc outercc, Cc innercc, ApOption argAp, Ap ap, Configuration config ) { - exists(ArgumentNodeExt arg, boolean allowsFieldFlow | + exists(ArgNode arg, boolean allowsFieldFlow | fwdFlow(arg, outercc, argAp, ap, config) and flowIntoCall(call, arg, p, allowsFieldFlow, config) and innercc = getCallContextCall(call, getNodeEnclosingCallable(p), outercc) @@ -1634,7 +1625,7 @@ private module Stage3 { private predicate fwdFlowIsEntered( DataFlowCall call, Cc cc, ApOption argAp, Ap ap, Configuration config ) { - exists(ParameterNodeExt p | + exists(ParamNode p | fwdFlowIn(call, p, cc, _, argAp, ap, config) and PrevStage::parameterMayFlowThrough(p, _, unbindApa(getApprox(ap)), config) ) @@ -1775,10 +1766,9 @@ private module Stage3 { pragma[nomagic] private predicate revFlowIn( - DataFlowCall call, ArgumentNodeExt arg, boolean toReturn, ApOption returnAp, Ap ap, - Configuration config + DataFlowCall call, ArgNode arg, boolean toReturn, ApOption returnAp, Ap ap, Configuration config ) { - exists(ParameterNodeExt p, boolean allowsFieldFlow | + exists(ParamNode p, boolean allowsFieldFlow | revFlow(p, toReturn, returnAp, ap, config) and flowIntoCall(call, arg, p, allowsFieldFlow, config) | @@ -1788,7 +1778,7 @@ private module Stage3 { pragma[nomagic] private predicate revFlowInToReturn( - DataFlowCall call, ArgumentNodeExt arg, Ap returnAp, Ap ap, Configuration config + DataFlowCall call, ArgNode arg, Ap returnAp, Ap ap, Configuration config ) { revFlowIn(call, arg, true, apSome(returnAp), ap, config) } @@ -1841,15 +1831,13 @@ private module Stage3 { pragma[noinline] private predicate parameterFlow( - ParameterNodeExt p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config + ParamNode p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config ) { revFlow(p, true, apSome(ap0), ap, config) and c = getNodeEnclosingCallable(p) } - predicate parameterMayFlowThrough( - ParameterNodeExt p, DataFlowCallable c, Ap ap, Configuration config - ) { + predicate parameterMayFlowThrough(ParamNode p, DataFlowCallable c, Ap ap, Configuration config) { exists(ReturnNodeExt ret, Ap ap0, ReturnKindExt kind, int pos | parameterFlow(p, ap, ap0, c, config) and c = getNodeEnclosingCallable(ret) and @@ -2160,8 +2148,7 @@ private module Stage4 { pragma[nomagic] private predicate flowIntoCall( - DataFlowCall call, ArgumentNodeExt node1, ParameterNodeExt node2, boolean allowsFieldFlow, - Configuration config + DataFlowCall call, ArgNode node1, ParamNode node2, boolean allowsFieldFlow, Configuration config ) { flowIntoCallNodeCand2(call, node1, node2, allowsFieldFlow, config) and PrevStage::revFlow(node2, _, _, _, pragma[only_bind_into](config)) and @@ -2305,10 +2292,10 @@ private module Stage4 { pragma[nomagic] private predicate fwdFlowIn( - DataFlowCall call, ParameterNodeExt p, Cc outercc, Cc innercc, ApOption argAp, Ap ap, + DataFlowCall call, ParamNode p, Cc outercc, Cc innercc, ApOption argAp, Ap ap, Configuration config ) { - exists(ArgumentNodeExt arg, boolean allowsFieldFlow | + exists(ArgNode arg, boolean allowsFieldFlow | fwdFlow(arg, outercc, argAp, ap, config) and flowIntoCall(call, arg, p, allowsFieldFlow, config) and innercc = getCallContextCall(call, getNodeEnclosingCallable(p), outercc) @@ -2353,7 +2340,7 @@ private module Stage4 { private predicate fwdFlowIsEntered( DataFlowCall call, Cc cc, ApOption argAp, Ap ap, Configuration config ) { - exists(ParameterNodeExt p | + exists(ParamNode p | fwdFlowIn(call, p, cc, _, argAp, ap, config) and PrevStage::parameterMayFlowThrough(p, _, unbindApa(getApprox(ap)), config) ) @@ -2494,10 +2481,9 @@ private module Stage4 { pragma[nomagic] private predicate revFlowIn( - DataFlowCall call, ArgumentNodeExt arg, boolean toReturn, ApOption returnAp, Ap ap, - Configuration config + DataFlowCall call, ArgNode arg, boolean toReturn, ApOption returnAp, Ap ap, Configuration config ) { - exists(ParameterNodeExt p, boolean allowsFieldFlow | + exists(ParamNode p, boolean allowsFieldFlow | revFlow(p, toReturn, returnAp, ap, config) and flowIntoCall(call, arg, p, allowsFieldFlow, config) | @@ -2507,7 +2493,7 @@ private module Stage4 { pragma[nomagic] private predicate revFlowInToReturn( - DataFlowCall call, ArgumentNodeExt arg, Ap returnAp, Ap ap, Configuration config + DataFlowCall call, ArgNode arg, Ap returnAp, Ap ap, Configuration config ) { revFlowIn(call, arg, true, apSome(returnAp), ap, config) } @@ -2560,15 +2546,13 @@ private module Stage4 { pragma[noinline] private predicate parameterFlow( - ParameterNodeExt p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config + ParamNode p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config ) { revFlow(p, true, apSome(ap0), ap, config) and c = getNodeEnclosingCallable(p) } - predicate parameterMayFlowThrough( - ParameterNodeExt p, DataFlowCallable c, Ap ap, Configuration config - ) { + predicate parameterMayFlowThrough(ParamNode p, DataFlowCallable c, Ap ap, Configuration config) { exists(ReturnNodeExt ret, Ap ap0, ReturnKindExt kind, int pos | parameterFlow(p, ap, ap0, c, config) and c = getNodeEnclosingCallable(ret) and @@ -2613,7 +2597,7 @@ private predicate nodeMayUseSummary(Node n, AccessPathApprox apa, Configuration private newtype TSummaryCtx = TSummaryCtxNone() or - TSummaryCtxSome(ParameterNodeExt p, AccessPath ap) { + TSummaryCtxSome(ParamNode p, AccessPath ap) { Stage4::parameterMayFlowThrough(p, _, ap.getApprox(), _) } @@ -2634,7 +2618,7 @@ private class SummaryCtxNone extends SummaryCtx, TSummaryCtxNone { /** A summary context from which a flow summary can be generated. */ private class SummaryCtxSome extends SummaryCtx, TSummaryCtxSome { - private ParameterNodeExt p; + private ParamNode p; private AccessPath ap; SummaryCtxSome() { this = TSummaryCtxSome(p, ap) } @@ -3242,7 +3226,7 @@ pragma[noinline] private predicate pathIntoArg( PathNodeMid mid, int i, CallContext cc, DataFlowCall call, AccessPath ap, AccessPathApprox apa ) { - exists(ArgumentNodeExt arg | + exists(ArgNode arg | arg = mid.getNode() and cc = mid.getCallContext() and arg.argumentOf(call, i) and @@ -3255,7 +3239,7 @@ pragma[noinline] private predicate parameterCand( DataFlowCallable callable, int i, AccessPathApprox apa, Configuration config ) { - exists(ParameterNodeExt p | + exists(ParamNode p | Stage4::revFlow(p, _, _, apa, config) and p.isParameterOf(callable, i) ) @@ -3279,7 +3263,7 @@ private predicate pathIntoCallable0( * respectively. */ private predicate pathIntoCallable( - PathNodeMid mid, ParameterNodeExt p, CallContext outercc, CallContextCall innercc, SummaryCtx sc, + PathNodeMid mid, ParamNode p, CallContext outercc, CallContextCall innercc, SummaryCtx sc, DataFlowCall call ) { exists(int i, DataFlowCallable callable, AccessPath ap | @@ -3575,7 +3559,7 @@ private module FlowExploration { private newtype TSummaryCtx1 = TSummaryCtx1None() or - TSummaryCtx1Param(ParameterNodeExt p) + TSummaryCtx1Param(ParamNode p) private newtype TSummaryCtx2 = TSummaryCtx2None() or @@ -3931,7 +3915,7 @@ private module FlowExploration { PartialPathNodeFwd mid, int i, CallContext cc, DataFlowCall call, PartialAccessPath ap, Configuration config ) { - exists(ArgumentNodeExt arg | + exists(ArgNode arg | arg = mid.getNode() and cc = mid.getCallContext() and arg.argumentOf(call, i) and @@ -3950,7 +3934,7 @@ private module FlowExploration { } private predicate partialPathIntoCallable( - PartialPathNodeFwd mid, ParameterNodeExt p, CallContext outercc, CallContextCall innercc, + PartialPathNodeFwd mid, ParamNode p, CallContext outercc, CallContextCall innercc, TSummaryCtx1 sc1, TSummaryCtx2 sc2, DataFlowCall call, PartialAccessPath ap, Configuration config ) { @@ -3987,7 +3971,7 @@ private module FlowExploration { DataFlowCall call, PartialPathNodeFwd mid, ReturnKindExt kind, CallContext cc, PartialAccessPath ap, Configuration config ) { - exists(ParameterNodeExt p, CallContext innercc, TSummaryCtx1 sc1, TSummaryCtx2 sc2 | + exists(ParamNode p, CallContext innercc, TSummaryCtx1 sc1, TSummaryCtx2 sc2 | partialPathIntoCallable(mid, p, cc, innercc, sc1, sc2, call, _, config) and paramFlowsThroughInPartialPath(kind, innercc, sc1, sc2, ap, config) ) @@ -4044,7 +4028,7 @@ private module FlowExploration { apConsRev(ap, c, ap0, config) ) or - exists(ParameterNodeExt p | + exists(ParamNode p | mid.getNode() = p and viableParamArg(_, p, node) and sc1 = mid.getSummaryCtx1() and @@ -4122,7 +4106,7 @@ private module FlowExploration { int pos, TRevSummaryCtx1Some sc1, TRevSummaryCtx2Some sc2, RevPartialAccessPath ap, Configuration config ) { - exists(PartialPathNodeRev mid, ParameterNodeExt p | + exists(PartialPathNodeRev mid, ParamNode p | mid.getNode() = p and p.isParameterOf(_, pos) and sc1 = mid.getSummaryCtx1() and @@ -4145,7 +4129,7 @@ private module FlowExploration { pragma[nomagic] private predicate revPartialPathThroughCallable( - PartialPathNodeRev mid, ArgumentNodeExt node, RevPartialAccessPath ap, Configuration config + PartialPathNodeRev mid, ArgNode node, RevPartialAccessPath ap, Configuration config ) { exists(DataFlowCall call, int pos | revPartialPathThroughCallable0(call, mid, pos, ap, config) and 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 cf83530bcbd..058d66b1496 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 @@ -385,7 +385,7 @@ private module Stage1 { */ pragma[nomagic] private predicate fwdFlowIsEntered(DataFlowCall call, Cc cc, Configuration config) { - exists(ArgumentNodeExt arg | + exists(ArgNode arg | fwdFlow(arg, cc, config) and viableParamArg(call, _, arg) ) @@ -512,24 +512,22 @@ private module Stage1 { pragma[nomagic] predicate viableParamArgNodeCandFwd1( - DataFlowCall call, ParameterNodeExt p, ArgumentNodeExt arg, Configuration config + DataFlowCall call, ParamNode p, ArgNode arg, Configuration config ) { viableParamArg(call, p, arg) and fwdFlow(arg, config) } pragma[nomagic] - private predicate revFlowIn( - DataFlowCall call, ArgumentNodeExt arg, boolean toReturn, Configuration config - ) { - exists(ParameterNodeExt p | + private predicate revFlowIn(DataFlowCall call, ArgNode arg, boolean toReturn, Configuration config) { + exists(ParamNode p | revFlow(p, toReturn, config) and viableParamArgNodeCandFwd1(call, p, arg, config) ) } pragma[nomagic] - private predicate revFlowInToReturn(DataFlowCall call, ArgumentNodeExt arg, Configuration config) { + private predicate revFlowInToReturn(DataFlowCall call, ArgNode arg, Configuration config) { revFlowIn(call, arg, true, config) } @@ -594,9 +592,7 @@ private module Stage1 { * Holds if flow may enter through `p` and reach a return node making `p` a * candidate for the origin of a summary. */ - predicate parameterMayFlowThrough( - ParameterNodeExt p, DataFlowCallable c, Ap ap, Configuration config - ) { + predicate parameterMayFlowThrough(ParamNode p, DataFlowCallable c, Ap ap, Configuration config) { exists(ReturnKindExt kind | throughFlowNodeCand(p, config) and returnFlowCallableNodeCand(c, kind, config) and @@ -662,7 +658,7 @@ private predicate flowOutOfCallNodeCand1( pragma[nomagic] private predicate viableParamArgNodeCand1( - DataFlowCall call, ParameterNodeExt p, ArgumentNodeExt arg, Configuration config + DataFlowCall call, ParamNode p, ArgNode arg, Configuration config ) { Stage1::viableParamArgNodeCandFwd1(call, p, arg, config) and Stage1::revFlow(arg, config) @@ -674,7 +670,7 @@ private predicate viableParamArgNodeCand1( */ pragma[nomagic] private predicate flowIntoCallNodeCand1( - DataFlowCall call, ArgumentNodeExt arg, ParameterNodeExt p, Configuration config + DataFlowCall call, ArgNode arg, ParamNode p, Configuration config ) { viableParamArgNodeCand1(call, p, arg, config) and Stage1::revFlow(p, config) and @@ -734,8 +730,7 @@ private predicate flowOutOfCallNodeCand1( */ pragma[nomagic] private predicate flowIntoCallNodeCand1( - DataFlowCall call, ArgumentNodeExt arg, ParameterNodeExt p, boolean allowsFieldFlow, - Configuration config + DataFlowCall call, ArgNode arg, ParamNode p, boolean allowsFieldFlow, Configuration config ) { flowIntoCallNodeCand1(call, arg, p, config) and exists(int b, int j | @@ -943,10 +938,10 @@ private module Stage2 { pragma[nomagic] private predicate fwdFlowIn( - DataFlowCall call, ParameterNodeExt p, Cc outercc, Cc innercc, ApOption argAp, Ap ap, + DataFlowCall call, ParamNode p, Cc outercc, Cc innercc, ApOption argAp, Ap ap, Configuration config ) { - exists(ArgumentNodeExt arg, boolean allowsFieldFlow | + exists(ArgNode arg, boolean allowsFieldFlow | fwdFlow(arg, outercc, argAp, ap, config) and flowIntoCall(call, arg, p, allowsFieldFlow, config) and innercc = getCallContextCall(call, getNodeEnclosingCallable(p), outercc) @@ -991,7 +986,7 @@ private module Stage2 { private predicate fwdFlowIsEntered( DataFlowCall call, Cc cc, ApOption argAp, Ap ap, Configuration config ) { - exists(ParameterNodeExt p | + exists(ParamNode p | fwdFlowIn(call, p, cc, _, argAp, ap, config) and PrevStage::parameterMayFlowThrough(p, _, getApprox(ap), config) ) @@ -1132,10 +1127,9 @@ private module Stage2 { pragma[nomagic] private predicate revFlowIn( - DataFlowCall call, ArgumentNodeExt arg, boolean toReturn, ApOption returnAp, Ap ap, - Configuration config + DataFlowCall call, ArgNode arg, boolean toReturn, ApOption returnAp, Ap ap, Configuration config ) { - exists(ParameterNodeExt p, boolean allowsFieldFlow | + exists(ParamNode p, boolean allowsFieldFlow | revFlow(p, toReturn, returnAp, ap, config) and flowIntoCall(call, arg, p, allowsFieldFlow, config) | @@ -1145,7 +1139,7 @@ private module Stage2 { pragma[nomagic] private predicate revFlowInToReturn( - DataFlowCall call, ArgumentNodeExt arg, Ap returnAp, Ap ap, Configuration config + DataFlowCall call, ArgNode arg, Ap returnAp, Ap ap, Configuration config ) { revFlowIn(call, arg, true, apSome(returnAp), ap, config) } @@ -1198,15 +1192,13 @@ private module Stage2 { pragma[noinline] private predicate parameterFlow( - ParameterNodeExt p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config + ParamNode p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config ) { revFlow(p, true, apSome(ap0), ap, config) and c = getNodeEnclosingCallable(p) } - predicate parameterMayFlowThrough( - ParameterNodeExt p, DataFlowCallable c, Ap ap, Configuration config - ) { + predicate parameterMayFlowThrough(ParamNode p, DataFlowCallable c, Ap ap, Configuration config) { exists(ReturnNodeExt ret, Ap ap0, ReturnKindExt kind, int pos | parameterFlow(p, ap, ap0, c, config) and c = getNodeEnclosingCallable(ret) and @@ -1246,8 +1238,7 @@ private predicate flowOutOfCallNodeCand2( pragma[nomagic] private predicate flowIntoCallNodeCand2( - DataFlowCall call, ArgumentNodeExt node1, ParameterNodeExt node2, boolean allowsFieldFlow, - Configuration config + DataFlowCall call, ArgNode node1, ParamNode node2, boolean allowsFieldFlow, Configuration config ) { flowIntoCallNodeCand1(call, node1, node2, allowsFieldFlow, config) and Stage2::revFlow(node2, pragma[only_bind_into](config)) and @@ -1276,7 +1267,7 @@ private module LocalFlowBigStep { config.isSource(node) or jumpStep(_, node, config) or additionalJumpStep(_, node, config) or - node instanceof ParameterNodeExt or + node instanceof ParamNode or node instanceof OutNodeExt or store(_, _, node, _) or read(_, _, node) or @@ -1586,10 +1577,10 @@ private module Stage3 { pragma[nomagic] private predicate fwdFlowIn( - DataFlowCall call, ParameterNodeExt p, Cc outercc, Cc innercc, ApOption argAp, Ap ap, + DataFlowCall call, ParamNode p, Cc outercc, Cc innercc, ApOption argAp, Ap ap, Configuration config ) { - exists(ArgumentNodeExt arg, boolean allowsFieldFlow | + exists(ArgNode arg, boolean allowsFieldFlow | fwdFlow(arg, outercc, argAp, ap, config) and flowIntoCall(call, arg, p, allowsFieldFlow, config) and innercc = getCallContextCall(call, getNodeEnclosingCallable(p), outercc) @@ -1634,7 +1625,7 @@ private module Stage3 { private predicate fwdFlowIsEntered( DataFlowCall call, Cc cc, ApOption argAp, Ap ap, Configuration config ) { - exists(ParameterNodeExt p | + exists(ParamNode p | fwdFlowIn(call, p, cc, _, argAp, ap, config) and PrevStage::parameterMayFlowThrough(p, _, unbindApa(getApprox(ap)), config) ) @@ -1775,10 +1766,9 @@ private module Stage3 { pragma[nomagic] private predicate revFlowIn( - DataFlowCall call, ArgumentNodeExt arg, boolean toReturn, ApOption returnAp, Ap ap, - Configuration config + DataFlowCall call, ArgNode arg, boolean toReturn, ApOption returnAp, Ap ap, Configuration config ) { - exists(ParameterNodeExt p, boolean allowsFieldFlow | + exists(ParamNode p, boolean allowsFieldFlow | revFlow(p, toReturn, returnAp, ap, config) and flowIntoCall(call, arg, p, allowsFieldFlow, config) | @@ -1788,7 +1778,7 @@ private module Stage3 { pragma[nomagic] private predicate revFlowInToReturn( - DataFlowCall call, ArgumentNodeExt arg, Ap returnAp, Ap ap, Configuration config + DataFlowCall call, ArgNode arg, Ap returnAp, Ap ap, Configuration config ) { revFlowIn(call, arg, true, apSome(returnAp), ap, config) } @@ -1841,15 +1831,13 @@ private module Stage3 { pragma[noinline] private predicate parameterFlow( - ParameterNodeExt p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config + ParamNode p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config ) { revFlow(p, true, apSome(ap0), ap, config) and c = getNodeEnclosingCallable(p) } - predicate parameterMayFlowThrough( - ParameterNodeExt p, DataFlowCallable c, Ap ap, Configuration config - ) { + predicate parameterMayFlowThrough(ParamNode p, DataFlowCallable c, Ap ap, Configuration config) { exists(ReturnNodeExt ret, Ap ap0, ReturnKindExt kind, int pos | parameterFlow(p, ap, ap0, c, config) and c = getNodeEnclosingCallable(ret) and @@ -2160,8 +2148,7 @@ private module Stage4 { pragma[nomagic] private predicate flowIntoCall( - DataFlowCall call, ArgumentNodeExt node1, ParameterNodeExt node2, boolean allowsFieldFlow, - Configuration config + DataFlowCall call, ArgNode node1, ParamNode node2, boolean allowsFieldFlow, Configuration config ) { flowIntoCallNodeCand2(call, node1, node2, allowsFieldFlow, config) and PrevStage::revFlow(node2, _, _, _, pragma[only_bind_into](config)) and @@ -2305,10 +2292,10 @@ private module Stage4 { pragma[nomagic] private predicate fwdFlowIn( - DataFlowCall call, ParameterNodeExt p, Cc outercc, Cc innercc, ApOption argAp, Ap ap, + DataFlowCall call, ParamNode p, Cc outercc, Cc innercc, ApOption argAp, Ap ap, Configuration config ) { - exists(ArgumentNodeExt arg, boolean allowsFieldFlow | + exists(ArgNode arg, boolean allowsFieldFlow | fwdFlow(arg, outercc, argAp, ap, config) and flowIntoCall(call, arg, p, allowsFieldFlow, config) and innercc = getCallContextCall(call, getNodeEnclosingCallable(p), outercc) @@ -2353,7 +2340,7 @@ private module Stage4 { private predicate fwdFlowIsEntered( DataFlowCall call, Cc cc, ApOption argAp, Ap ap, Configuration config ) { - exists(ParameterNodeExt p | + exists(ParamNode p | fwdFlowIn(call, p, cc, _, argAp, ap, config) and PrevStage::parameterMayFlowThrough(p, _, unbindApa(getApprox(ap)), config) ) @@ -2494,10 +2481,9 @@ private module Stage4 { pragma[nomagic] private predicate revFlowIn( - DataFlowCall call, ArgumentNodeExt arg, boolean toReturn, ApOption returnAp, Ap ap, - Configuration config + DataFlowCall call, ArgNode arg, boolean toReturn, ApOption returnAp, Ap ap, Configuration config ) { - exists(ParameterNodeExt p, boolean allowsFieldFlow | + exists(ParamNode p, boolean allowsFieldFlow | revFlow(p, toReturn, returnAp, ap, config) and flowIntoCall(call, arg, p, allowsFieldFlow, config) | @@ -2507,7 +2493,7 @@ private module Stage4 { pragma[nomagic] private predicate revFlowInToReturn( - DataFlowCall call, ArgumentNodeExt arg, Ap returnAp, Ap ap, Configuration config + DataFlowCall call, ArgNode arg, Ap returnAp, Ap ap, Configuration config ) { revFlowIn(call, arg, true, apSome(returnAp), ap, config) } @@ -2560,15 +2546,13 @@ private module Stage4 { pragma[noinline] private predicate parameterFlow( - ParameterNodeExt p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config + ParamNode p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config ) { revFlow(p, true, apSome(ap0), ap, config) and c = getNodeEnclosingCallable(p) } - predicate parameterMayFlowThrough( - ParameterNodeExt p, DataFlowCallable c, Ap ap, Configuration config - ) { + predicate parameterMayFlowThrough(ParamNode p, DataFlowCallable c, Ap ap, Configuration config) { exists(ReturnNodeExt ret, Ap ap0, ReturnKindExt kind, int pos | parameterFlow(p, ap, ap0, c, config) and c = getNodeEnclosingCallable(ret) and @@ -2613,7 +2597,7 @@ private predicate nodeMayUseSummary(Node n, AccessPathApprox apa, Configuration private newtype TSummaryCtx = TSummaryCtxNone() or - TSummaryCtxSome(ParameterNodeExt p, AccessPath ap) { + TSummaryCtxSome(ParamNode p, AccessPath ap) { Stage4::parameterMayFlowThrough(p, _, ap.getApprox(), _) } @@ -2634,7 +2618,7 @@ private class SummaryCtxNone extends SummaryCtx, TSummaryCtxNone { /** A summary context from which a flow summary can be generated. */ private class SummaryCtxSome extends SummaryCtx, TSummaryCtxSome { - private ParameterNodeExt p; + private ParamNode p; private AccessPath ap; SummaryCtxSome() { this = TSummaryCtxSome(p, ap) } @@ -3242,7 +3226,7 @@ pragma[noinline] private predicate pathIntoArg( PathNodeMid mid, int i, CallContext cc, DataFlowCall call, AccessPath ap, AccessPathApprox apa ) { - exists(ArgumentNodeExt arg | + exists(ArgNode arg | arg = mid.getNode() and cc = mid.getCallContext() and arg.argumentOf(call, i) and @@ -3255,7 +3239,7 @@ pragma[noinline] private predicate parameterCand( DataFlowCallable callable, int i, AccessPathApprox apa, Configuration config ) { - exists(ParameterNodeExt p | + exists(ParamNode p | Stage4::revFlow(p, _, _, apa, config) and p.isParameterOf(callable, i) ) @@ -3279,7 +3263,7 @@ private predicate pathIntoCallable0( * respectively. */ private predicate pathIntoCallable( - PathNodeMid mid, ParameterNodeExt p, CallContext outercc, CallContextCall innercc, SummaryCtx sc, + PathNodeMid mid, ParamNode p, CallContext outercc, CallContextCall innercc, SummaryCtx sc, DataFlowCall call ) { exists(int i, DataFlowCallable callable, AccessPath ap | @@ -3575,7 +3559,7 @@ private module FlowExploration { private newtype TSummaryCtx1 = TSummaryCtx1None() or - TSummaryCtx1Param(ParameterNodeExt p) + TSummaryCtx1Param(ParamNode p) private newtype TSummaryCtx2 = TSummaryCtx2None() or @@ -3931,7 +3915,7 @@ private module FlowExploration { PartialPathNodeFwd mid, int i, CallContext cc, DataFlowCall call, PartialAccessPath ap, Configuration config ) { - exists(ArgumentNodeExt arg | + exists(ArgNode arg | arg = mid.getNode() and cc = mid.getCallContext() and arg.argumentOf(call, i) and @@ -3950,7 +3934,7 @@ private module FlowExploration { } private predicate partialPathIntoCallable( - PartialPathNodeFwd mid, ParameterNodeExt p, CallContext outercc, CallContextCall innercc, + PartialPathNodeFwd mid, ParamNode p, CallContext outercc, CallContextCall innercc, TSummaryCtx1 sc1, TSummaryCtx2 sc2, DataFlowCall call, PartialAccessPath ap, Configuration config ) { @@ -3987,7 +3971,7 @@ private module FlowExploration { DataFlowCall call, PartialPathNodeFwd mid, ReturnKindExt kind, CallContext cc, PartialAccessPath ap, Configuration config ) { - exists(ParameterNodeExt p, CallContext innercc, TSummaryCtx1 sc1, TSummaryCtx2 sc2 | + exists(ParamNode p, CallContext innercc, TSummaryCtx1 sc1, TSummaryCtx2 sc2 | partialPathIntoCallable(mid, p, cc, innercc, sc1, sc2, call, _, config) and paramFlowsThroughInPartialPath(kind, innercc, sc1, sc2, ap, config) ) @@ -4044,7 +4028,7 @@ private module FlowExploration { apConsRev(ap, c, ap0, config) ) or - exists(ParameterNodeExt p | + exists(ParamNode p | mid.getNode() = p and viableParamArg(_, p, node) and sc1 = mid.getSummaryCtx1() and @@ -4122,7 +4106,7 @@ private module FlowExploration { int pos, TRevSummaryCtx1Some sc1, TRevSummaryCtx2Some sc2, RevPartialAccessPath ap, Configuration config ) { - exists(PartialPathNodeRev mid, ParameterNodeExt p | + exists(PartialPathNodeRev mid, ParamNode p | mid.getNode() = p and p.isParameterOf(_, pos) and sc1 = mid.getSummaryCtx1() and @@ -4145,7 +4129,7 @@ private module FlowExploration { pragma[nomagic] private predicate revPartialPathThroughCallable( - PartialPathNodeRev mid, ArgumentNodeExt node, RevPartialAccessPath ap, Configuration config + PartialPathNodeRev mid, ArgNode node, RevPartialAccessPath ap, Configuration config ) { exists(DataFlowCall call, int pos | revPartialPathThroughCallable0(call, mid, pos, ap, config) and 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 cf83530bcbd..058d66b1496 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 @@ -385,7 +385,7 @@ private module Stage1 { */ pragma[nomagic] private predicate fwdFlowIsEntered(DataFlowCall call, Cc cc, Configuration config) { - exists(ArgumentNodeExt arg | + exists(ArgNode arg | fwdFlow(arg, cc, config) and viableParamArg(call, _, arg) ) @@ -512,24 +512,22 @@ private module Stage1 { pragma[nomagic] predicate viableParamArgNodeCandFwd1( - DataFlowCall call, ParameterNodeExt p, ArgumentNodeExt arg, Configuration config + DataFlowCall call, ParamNode p, ArgNode arg, Configuration config ) { viableParamArg(call, p, arg) and fwdFlow(arg, config) } pragma[nomagic] - private predicate revFlowIn( - DataFlowCall call, ArgumentNodeExt arg, boolean toReturn, Configuration config - ) { - exists(ParameterNodeExt p | + private predicate revFlowIn(DataFlowCall call, ArgNode arg, boolean toReturn, Configuration config) { + exists(ParamNode p | revFlow(p, toReturn, config) and viableParamArgNodeCandFwd1(call, p, arg, config) ) } pragma[nomagic] - private predicate revFlowInToReturn(DataFlowCall call, ArgumentNodeExt arg, Configuration config) { + private predicate revFlowInToReturn(DataFlowCall call, ArgNode arg, Configuration config) { revFlowIn(call, arg, true, config) } @@ -594,9 +592,7 @@ private module Stage1 { * Holds if flow may enter through `p` and reach a return node making `p` a * candidate for the origin of a summary. */ - predicate parameterMayFlowThrough( - ParameterNodeExt p, DataFlowCallable c, Ap ap, Configuration config - ) { + predicate parameterMayFlowThrough(ParamNode p, DataFlowCallable c, Ap ap, Configuration config) { exists(ReturnKindExt kind | throughFlowNodeCand(p, config) and returnFlowCallableNodeCand(c, kind, config) and @@ -662,7 +658,7 @@ private predicate flowOutOfCallNodeCand1( pragma[nomagic] private predicate viableParamArgNodeCand1( - DataFlowCall call, ParameterNodeExt p, ArgumentNodeExt arg, Configuration config + DataFlowCall call, ParamNode p, ArgNode arg, Configuration config ) { Stage1::viableParamArgNodeCandFwd1(call, p, arg, config) and Stage1::revFlow(arg, config) @@ -674,7 +670,7 @@ private predicate viableParamArgNodeCand1( */ pragma[nomagic] private predicate flowIntoCallNodeCand1( - DataFlowCall call, ArgumentNodeExt arg, ParameterNodeExt p, Configuration config + DataFlowCall call, ArgNode arg, ParamNode p, Configuration config ) { viableParamArgNodeCand1(call, p, arg, config) and Stage1::revFlow(p, config) and @@ -734,8 +730,7 @@ private predicate flowOutOfCallNodeCand1( */ pragma[nomagic] private predicate flowIntoCallNodeCand1( - DataFlowCall call, ArgumentNodeExt arg, ParameterNodeExt p, boolean allowsFieldFlow, - Configuration config + DataFlowCall call, ArgNode arg, ParamNode p, boolean allowsFieldFlow, Configuration config ) { flowIntoCallNodeCand1(call, arg, p, config) and exists(int b, int j | @@ -943,10 +938,10 @@ private module Stage2 { pragma[nomagic] private predicate fwdFlowIn( - DataFlowCall call, ParameterNodeExt p, Cc outercc, Cc innercc, ApOption argAp, Ap ap, + DataFlowCall call, ParamNode p, Cc outercc, Cc innercc, ApOption argAp, Ap ap, Configuration config ) { - exists(ArgumentNodeExt arg, boolean allowsFieldFlow | + exists(ArgNode arg, boolean allowsFieldFlow | fwdFlow(arg, outercc, argAp, ap, config) and flowIntoCall(call, arg, p, allowsFieldFlow, config) and innercc = getCallContextCall(call, getNodeEnclosingCallable(p), outercc) @@ -991,7 +986,7 @@ private module Stage2 { private predicate fwdFlowIsEntered( DataFlowCall call, Cc cc, ApOption argAp, Ap ap, Configuration config ) { - exists(ParameterNodeExt p | + exists(ParamNode p | fwdFlowIn(call, p, cc, _, argAp, ap, config) and PrevStage::parameterMayFlowThrough(p, _, getApprox(ap), config) ) @@ -1132,10 +1127,9 @@ private module Stage2 { pragma[nomagic] private predicate revFlowIn( - DataFlowCall call, ArgumentNodeExt arg, boolean toReturn, ApOption returnAp, Ap ap, - Configuration config + DataFlowCall call, ArgNode arg, boolean toReturn, ApOption returnAp, Ap ap, Configuration config ) { - exists(ParameterNodeExt p, boolean allowsFieldFlow | + exists(ParamNode p, boolean allowsFieldFlow | revFlow(p, toReturn, returnAp, ap, config) and flowIntoCall(call, arg, p, allowsFieldFlow, config) | @@ -1145,7 +1139,7 @@ private module Stage2 { pragma[nomagic] private predicate revFlowInToReturn( - DataFlowCall call, ArgumentNodeExt arg, Ap returnAp, Ap ap, Configuration config + DataFlowCall call, ArgNode arg, Ap returnAp, Ap ap, Configuration config ) { revFlowIn(call, arg, true, apSome(returnAp), ap, config) } @@ -1198,15 +1192,13 @@ private module Stage2 { pragma[noinline] private predicate parameterFlow( - ParameterNodeExt p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config + ParamNode p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config ) { revFlow(p, true, apSome(ap0), ap, config) and c = getNodeEnclosingCallable(p) } - predicate parameterMayFlowThrough( - ParameterNodeExt p, DataFlowCallable c, Ap ap, Configuration config - ) { + predicate parameterMayFlowThrough(ParamNode p, DataFlowCallable c, Ap ap, Configuration config) { exists(ReturnNodeExt ret, Ap ap0, ReturnKindExt kind, int pos | parameterFlow(p, ap, ap0, c, config) and c = getNodeEnclosingCallable(ret) and @@ -1246,8 +1238,7 @@ private predicate flowOutOfCallNodeCand2( pragma[nomagic] private predicate flowIntoCallNodeCand2( - DataFlowCall call, ArgumentNodeExt node1, ParameterNodeExt node2, boolean allowsFieldFlow, - Configuration config + DataFlowCall call, ArgNode node1, ParamNode node2, boolean allowsFieldFlow, Configuration config ) { flowIntoCallNodeCand1(call, node1, node2, allowsFieldFlow, config) and Stage2::revFlow(node2, pragma[only_bind_into](config)) and @@ -1276,7 +1267,7 @@ private module LocalFlowBigStep { config.isSource(node) or jumpStep(_, node, config) or additionalJumpStep(_, node, config) or - node instanceof ParameterNodeExt or + node instanceof ParamNode or node instanceof OutNodeExt or store(_, _, node, _) or read(_, _, node) or @@ -1586,10 +1577,10 @@ private module Stage3 { pragma[nomagic] private predicate fwdFlowIn( - DataFlowCall call, ParameterNodeExt p, Cc outercc, Cc innercc, ApOption argAp, Ap ap, + DataFlowCall call, ParamNode p, Cc outercc, Cc innercc, ApOption argAp, Ap ap, Configuration config ) { - exists(ArgumentNodeExt arg, boolean allowsFieldFlow | + exists(ArgNode arg, boolean allowsFieldFlow | fwdFlow(arg, outercc, argAp, ap, config) and flowIntoCall(call, arg, p, allowsFieldFlow, config) and innercc = getCallContextCall(call, getNodeEnclosingCallable(p), outercc) @@ -1634,7 +1625,7 @@ private module Stage3 { private predicate fwdFlowIsEntered( DataFlowCall call, Cc cc, ApOption argAp, Ap ap, Configuration config ) { - exists(ParameterNodeExt p | + exists(ParamNode p | fwdFlowIn(call, p, cc, _, argAp, ap, config) and PrevStage::parameterMayFlowThrough(p, _, unbindApa(getApprox(ap)), config) ) @@ -1775,10 +1766,9 @@ private module Stage3 { pragma[nomagic] private predicate revFlowIn( - DataFlowCall call, ArgumentNodeExt arg, boolean toReturn, ApOption returnAp, Ap ap, - Configuration config + DataFlowCall call, ArgNode arg, boolean toReturn, ApOption returnAp, Ap ap, Configuration config ) { - exists(ParameterNodeExt p, boolean allowsFieldFlow | + exists(ParamNode p, boolean allowsFieldFlow | revFlow(p, toReturn, returnAp, ap, config) and flowIntoCall(call, arg, p, allowsFieldFlow, config) | @@ -1788,7 +1778,7 @@ private module Stage3 { pragma[nomagic] private predicate revFlowInToReturn( - DataFlowCall call, ArgumentNodeExt arg, Ap returnAp, Ap ap, Configuration config + DataFlowCall call, ArgNode arg, Ap returnAp, Ap ap, Configuration config ) { revFlowIn(call, arg, true, apSome(returnAp), ap, config) } @@ -1841,15 +1831,13 @@ private module Stage3 { pragma[noinline] private predicate parameterFlow( - ParameterNodeExt p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config + ParamNode p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config ) { revFlow(p, true, apSome(ap0), ap, config) and c = getNodeEnclosingCallable(p) } - predicate parameterMayFlowThrough( - ParameterNodeExt p, DataFlowCallable c, Ap ap, Configuration config - ) { + predicate parameterMayFlowThrough(ParamNode p, DataFlowCallable c, Ap ap, Configuration config) { exists(ReturnNodeExt ret, Ap ap0, ReturnKindExt kind, int pos | parameterFlow(p, ap, ap0, c, config) and c = getNodeEnclosingCallable(ret) and @@ -2160,8 +2148,7 @@ private module Stage4 { pragma[nomagic] private predicate flowIntoCall( - DataFlowCall call, ArgumentNodeExt node1, ParameterNodeExt node2, boolean allowsFieldFlow, - Configuration config + DataFlowCall call, ArgNode node1, ParamNode node2, boolean allowsFieldFlow, Configuration config ) { flowIntoCallNodeCand2(call, node1, node2, allowsFieldFlow, config) and PrevStage::revFlow(node2, _, _, _, pragma[only_bind_into](config)) and @@ -2305,10 +2292,10 @@ private module Stage4 { pragma[nomagic] private predicate fwdFlowIn( - DataFlowCall call, ParameterNodeExt p, Cc outercc, Cc innercc, ApOption argAp, Ap ap, + DataFlowCall call, ParamNode p, Cc outercc, Cc innercc, ApOption argAp, Ap ap, Configuration config ) { - exists(ArgumentNodeExt arg, boolean allowsFieldFlow | + exists(ArgNode arg, boolean allowsFieldFlow | fwdFlow(arg, outercc, argAp, ap, config) and flowIntoCall(call, arg, p, allowsFieldFlow, config) and innercc = getCallContextCall(call, getNodeEnclosingCallable(p), outercc) @@ -2353,7 +2340,7 @@ private module Stage4 { private predicate fwdFlowIsEntered( DataFlowCall call, Cc cc, ApOption argAp, Ap ap, Configuration config ) { - exists(ParameterNodeExt p | + exists(ParamNode p | fwdFlowIn(call, p, cc, _, argAp, ap, config) and PrevStage::parameterMayFlowThrough(p, _, unbindApa(getApprox(ap)), config) ) @@ -2494,10 +2481,9 @@ private module Stage4 { pragma[nomagic] private predicate revFlowIn( - DataFlowCall call, ArgumentNodeExt arg, boolean toReturn, ApOption returnAp, Ap ap, - Configuration config + DataFlowCall call, ArgNode arg, boolean toReturn, ApOption returnAp, Ap ap, Configuration config ) { - exists(ParameterNodeExt p, boolean allowsFieldFlow | + exists(ParamNode p, boolean allowsFieldFlow | revFlow(p, toReturn, returnAp, ap, config) and flowIntoCall(call, arg, p, allowsFieldFlow, config) | @@ -2507,7 +2493,7 @@ private module Stage4 { pragma[nomagic] private predicate revFlowInToReturn( - DataFlowCall call, ArgumentNodeExt arg, Ap returnAp, Ap ap, Configuration config + DataFlowCall call, ArgNode arg, Ap returnAp, Ap ap, Configuration config ) { revFlowIn(call, arg, true, apSome(returnAp), ap, config) } @@ -2560,15 +2546,13 @@ private module Stage4 { pragma[noinline] private predicate parameterFlow( - ParameterNodeExt p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config + ParamNode p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config ) { revFlow(p, true, apSome(ap0), ap, config) and c = getNodeEnclosingCallable(p) } - predicate parameterMayFlowThrough( - ParameterNodeExt p, DataFlowCallable c, Ap ap, Configuration config - ) { + predicate parameterMayFlowThrough(ParamNode p, DataFlowCallable c, Ap ap, Configuration config) { exists(ReturnNodeExt ret, Ap ap0, ReturnKindExt kind, int pos | parameterFlow(p, ap, ap0, c, config) and c = getNodeEnclosingCallable(ret) and @@ -2613,7 +2597,7 @@ private predicate nodeMayUseSummary(Node n, AccessPathApprox apa, Configuration private newtype TSummaryCtx = TSummaryCtxNone() or - TSummaryCtxSome(ParameterNodeExt p, AccessPath ap) { + TSummaryCtxSome(ParamNode p, AccessPath ap) { Stage4::parameterMayFlowThrough(p, _, ap.getApprox(), _) } @@ -2634,7 +2618,7 @@ private class SummaryCtxNone extends SummaryCtx, TSummaryCtxNone { /** A summary context from which a flow summary can be generated. */ private class SummaryCtxSome extends SummaryCtx, TSummaryCtxSome { - private ParameterNodeExt p; + private ParamNode p; private AccessPath ap; SummaryCtxSome() { this = TSummaryCtxSome(p, ap) } @@ -3242,7 +3226,7 @@ pragma[noinline] private predicate pathIntoArg( PathNodeMid mid, int i, CallContext cc, DataFlowCall call, AccessPath ap, AccessPathApprox apa ) { - exists(ArgumentNodeExt arg | + exists(ArgNode arg | arg = mid.getNode() and cc = mid.getCallContext() and arg.argumentOf(call, i) and @@ -3255,7 +3239,7 @@ pragma[noinline] private predicate parameterCand( DataFlowCallable callable, int i, AccessPathApprox apa, Configuration config ) { - exists(ParameterNodeExt p | + exists(ParamNode p | Stage4::revFlow(p, _, _, apa, config) and p.isParameterOf(callable, i) ) @@ -3279,7 +3263,7 @@ private predicate pathIntoCallable0( * respectively. */ private predicate pathIntoCallable( - PathNodeMid mid, ParameterNodeExt p, CallContext outercc, CallContextCall innercc, SummaryCtx sc, + PathNodeMid mid, ParamNode p, CallContext outercc, CallContextCall innercc, SummaryCtx sc, DataFlowCall call ) { exists(int i, DataFlowCallable callable, AccessPath ap | @@ -3575,7 +3559,7 @@ private module FlowExploration { private newtype TSummaryCtx1 = TSummaryCtx1None() or - TSummaryCtx1Param(ParameterNodeExt p) + TSummaryCtx1Param(ParamNode p) private newtype TSummaryCtx2 = TSummaryCtx2None() or @@ -3931,7 +3915,7 @@ private module FlowExploration { PartialPathNodeFwd mid, int i, CallContext cc, DataFlowCall call, PartialAccessPath ap, Configuration config ) { - exists(ArgumentNodeExt arg | + exists(ArgNode arg | arg = mid.getNode() and cc = mid.getCallContext() and arg.argumentOf(call, i) and @@ -3950,7 +3934,7 @@ private module FlowExploration { } private predicate partialPathIntoCallable( - PartialPathNodeFwd mid, ParameterNodeExt p, CallContext outercc, CallContextCall innercc, + PartialPathNodeFwd mid, ParamNode p, CallContext outercc, CallContextCall innercc, TSummaryCtx1 sc1, TSummaryCtx2 sc2, DataFlowCall call, PartialAccessPath ap, Configuration config ) { @@ -3987,7 +3971,7 @@ private module FlowExploration { DataFlowCall call, PartialPathNodeFwd mid, ReturnKindExt kind, CallContext cc, PartialAccessPath ap, Configuration config ) { - exists(ParameterNodeExt p, CallContext innercc, TSummaryCtx1 sc1, TSummaryCtx2 sc2 | + exists(ParamNode p, CallContext innercc, TSummaryCtx1 sc1, TSummaryCtx2 sc2 | partialPathIntoCallable(mid, p, cc, innercc, sc1, sc2, call, _, config) and paramFlowsThroughInPartialPath(kind, innercc, sc1, sc2, ap, config) ) @@ -4044,7 +4028,7 @@ private module FlowExploration { apConsRev(ap, c, ap0, config) ) or - exists(ParameterNodeExt p | + exists(ParamNode p | mid.getNode() = p and viableParamArg(_, p, node) and sc1 = mid.getSummaryCtx1() and @@ -4122,7 +4106,7 @@ private module FlowExploration { int pos, TRevSummaryCtx1Some sc1, TRevSummaryCtx2Some sc2, RevPartialAccessPath ap, Configuration config ) { - exists(PartialPathNodeRev mid, ParameterNodeExt p | + exists(PartialPathNodeRev mid, ParamNode p | mid.getNode() = p and p.isParameterOf(_, pos) and sc1 = mid.getSummaryCtx1() and @@ -4145,7 +4129,7 @@ private module FlowExploration { pragma[nomagic] private predicate revPartialPathThroughCallable( - PartialPathNodeRev mid, ArgumentNodeExt node, RevPartialAccessPath ap, Configuration config + PartialPathNodeRev mid, ArgNode node, RevPartialAccessPath ap, Configuration config ) { exists(DataFlowCall call, int pos | revPartialPathThroughCallable0(call, mid, pos, ap, config) and 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 cf83530bcbd..058d66b1496 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 @@ -385,7 +385,7 @@ private module Stage1 { */ pragma[nomagic] private predicate fwdFlowIsEntered(DataFlowCall call, Cc cc, Configuration config) { - exists(ArgumentNodeExt arg | + exists(ArgNode arg | fwdFlow(arg, cc, config) and viableParamArg(call, _, arg) ) @@ -512,24 +512,22 @@ private module Stage1 { pragma[nomagic] predicate viableParamArgNodeCandFwd1( - DataFlowCall call, ParameterNodeExt p, ArgumentNodeExt arg, Configuration config + DataFlowCall call, ParamNode p, ArgNode arg, Configuration config ) { viableParamArg(call, p, arg) and fwdFlow(arg, config) } pragma[nomagic] - private predicate revFlowIn( - DataFlowCall call, ArgumentNodeExt arg, boolean toReturn, Configuration config - ) { - exists(ParameterNodeExt p | + private predicate revFlowIn(DataFlowCall call, ArgNode arg, boolean toReturn, Configuration config) { + exists(ParamNode p | revFlow(p, toReturn, config) and viableParamArgNodeCandFwd1(call, p, arg, config) ) } pragma[nomagic] - private predicate revFlowInToReturn(DataFlowCall call, ArgumentNodeExt arg, Configuration config) { + private predicate revFlowInToReturn(DataFlowCall call, ArgNode arg, Configuration config) { revFlowIn(call, arg, true, config) } @@ -594,9 +592,7 @@ private module Stage1 { * Holds if flow may enter through `p` and reach a return node making `p` a * candidate for the origin of a summary. */ - predicate parameterMayFlowThrough( - ParameterNodeExt p, DataFlowCallable c, Ap ap, Configuration config - ) { + predicate parameterMayFlowThrough(ParamNode p, DataFlowCallable c, Ap ap, Configuration config) { exists(ReturnKindExt kind | throughFlowNodeCand(p, config) and returnFlowCallableNodeCand(c, kind, config) and @@ -662,7 +658,7 @@ private predicate flowOutOfCallNodeCand1( pragma[nomagic] private predicate viableParamArgNodeCand1( - DataFlowCall call, ParameterNodeExt p, ArgumentNodeExt arg, Configuration config + DataFlowCall call, ParamNode p, ArgNode arg, Configuration config ) { Stage1::viableParamArgNodeCandFwd1(call, p, arg, config) and Stage1::revFlow(arg, config) @@ -674,7 +670,7 @@ private predicate viableParamArgNodeCand1( */ pragma[nomagic] private predicate flowIntoCallNodeCand1( - DataFlowCall call, ArgumentNodeExt arg, ParameterNodeExt p, Configuration config + DataFlowCall call, ArgNode arg, ParamNode p, Configuration config ) { viableParamArgNodeCand1(call, p, arg, config) and Stage1::revFlow(p, config) and @@ -734,8 +730,7 @@ private predicate flowOutOfCallNodeCand1( */ pragma[nomagic] private predicate flowIntoCallNodeCand1( - DataFlowCall call, ArgumentNodeExt arg, ParameterNodeExt p, boolean allowsFieldFlow, - Configuration config + DataFlowCall call, ArgNode arg, ParamNode p, boolean allowsFieldFlow, Configuration config ) { flowIntoCallNodeCand1(call, arg, p, config) and exists(int b, int j | @@ -943,10 +938,10 @@ private module Stage2 { pragma[nomagic] private predicate fwdFlowIn( - DataFlowCall call, ParameterNodeExt p, Cc outercc, Cc innercc, ApOption argAp, Ap ap, + DataFlowCall call, ParamNode p, Cc outercc, Cc innercc, ApOption argAp, Ap ap, Configuration config ) { - exists(ArgumentNodeExt arg, boolean allowsFieldFlow | + exists(ArgNode arg, boolean allowsFieldFlow | fwdFlow(arg, outercc, argAp, ap, config) and flowIntoCall(call, arg, p, allowsFieldFlow, config) and innercc = getCallContextCall(call, getNodeEnclosingCallable(p), outercc) @@ -991,7 +986,7 @@ private module Stage2 { private predicate fwdFlowIsEntered( DataFlowCall call, Cc cc, ApOption argAp, Ap ap, Configuration config ) { - exists(ParameterNodeExt p | + exists(ParamNode p | fwdFlowIn(call, p, cc, _, argAp, ap, config) and PrevStage::parameterMayFlowThrough(p, _, getApprox(ap), config) ) @@ -1132,10 +1127,9 @@ private module Stage2 { pragma[nomagic] private predicate revFlowIn( - DataFlowCall call, ArgumentNodeExt arg, boolean toReturn, ApOption returnAp, Ap ap, - Configuration config + DataFlowCall call, ArgNode arg, boolean toReturn, ApOption returnAp, Ap ap, Configuration config ) { - exists(ParameterNodeExt p, boolean allowsFieldFlow | + exists(ParamNode p, boolean allowsFieldFlow | revFlow(p, toReturn, returnAp, ap, config) and flowIntoCall(call, arg, p, allowsFieldFlow, config) | @@ -1145,7 +1139,7 @@ private module Stage2 { pragma[nomagic] private predicate revFlowInToReturn( - DataFlowCall call, ArgumentNodeExt arg, Ap returnAp, Ap ap, Configuration config + DataFlowCall call, ArgNode arg, Ap returnAp, Ap ap, Configuration config ) { revFlowIn(call, arg, true, apSome(returnAp), ap, config) } @@ -1198,15 +1192,13 @@ private module Stage2 { pragma[noinline] private predicate parameterFlow( - ParameterNodeExt p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config + ParamNode p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config ) { revFlow(p, true, apSome(ap0), ap, config) and c = getNodeEnclosingCallable(p) } - predicate parameterMayFlowThrough( - ParameterNodeExt p, DataFlowCallable c, Ap ap, Configuration config - ) { + predicate parameterMayFlowThrough(ParamNode p, DataFlowCallable c, Ap ap, Configuration config) { exists(ReturnNodeExt ret, Ap ap0, ReturnKindExt kind, int pos | parameterFlow(p, ap, ap0, c, config) and c = getNodeEnclosingCallable(ret) and @@ -1246,8 +1238,7 @@ private predicate flowOutOfCallNodeCand2( pragma[nomagic] private predicate flowIntoCallNodeCand2( - DataFlowCall call, ArgumentNodeExt node1, ParameterNodeExt node2, boolean allowsFieldFlow, - Configuration config + DataFlowCall call, ArgNode node1, ParamNode node2, boolean allowsFieldFlow, Configuration config ) { flowIntoCallNodeCand1(call, node1, node2, allowsFieldFlow, config) and Stage2::revFlow(node2, pragma[only_bind_into](config)) and @@ -1276,7 +1267,7 @@ private module LocalFlowBigStep { config.isSource(node) or jumpStep(_, node, config) or additionalJumpStep(_, node, config) or - node instanceof ParameterNodeExt or + node instanceof ParamNode or node instanceof OutNodeExt or store(_, _, node, _) or read(_, _, node) or @@ -1586,10 +1577,10 @@ private module Stage3 { pragma[nomagic] private predicate fwdFlowIn( - DataFlowCall call, ParameterNodeExt p, Cc outercc, Cc innercc, ApOption argAp, Ap ap, + DataFlowCall call, ParamNode p, Cc outercc, Cc innercc, ApOption argAp, Ap ap, Configuration config ) { - exists(ArgumentNodeExt arg, boolean allowsFieldFlow | + exists(ArgNode arg, boolean allowsFieldFlow | fwdFlow(arg, outercc, argAp, ap, config) and flowIntoCall(call, arg, p, allowsFieldFlow, config) and innercc = getCallContextCall(call, getNodeEnclosingCallable(p), outercc) @@ -1634,7 +1625,7 @@ private module Stage3 { private predicate fwdFlowIsEntered( DataFlowCall call, Cc cc, ApOption argAp, Ap ap, Configuration config ) { - exists(ParameterNodeExt p | + exists(ParamNode p | fwdFlowIn(call, p, cc, _, argAp, ap, config) and PrevStage::parameterMayFlowThrough(p, _, unbindApa(getApprox(ap)), config) ) @@ -1775,10 +1766,9 @@ private module Stage3 { pragma[nomagic] private predicate revFlowIn( - DataFlowCall call, ArgumentNodeExt arg, boolean toReturn, ApOption returnAp, Ap ap, - Configuration config + DataFlowCall call, ArgNode arg, boolean toReturn, ApOption returnAp, Ap ap, Configuration config ) { - exists(ParameterNodeExt p, boolean allowsFieldFlow | + exists(ParamNode p, boolean allowsFieldFlow | revFlow(p, toReturn, returnAp, ap, config) and flowIntoCall(call, arg, p, allowsFieldFlow, config) | @@ -1788,7 +1778,7 @@ private module Stage3 { pragma[nomagic] private predicate revFlowInToReturn( - DataFlowCall call, ArgumentNodeExt arg, Ap returnAp, Ap ap, Configuration config + DataFlowCall call, ArgNode arg, Ap returnAp, Ap ap, Configuration config ) { revFlowIn(call, arg, true, apSome(returnAp), ap, config) } @@ -1841,15 +1831,13 @@ private module Stage3 { pragma[noinline] private predicate parameterFlow( - ParameterNodeExt p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config + ParamNode p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config ) { revFlow(p, true, apSome(ap0), ap, config) and c = getNodeEnclosingCallable(p) } - predicate parameterMayFlowThrough( - ParameterNodeExt p, DataFlowCallable c, Ap ap, Configuration config - ) { + predicate parameterMayFlowThrough(ParamNode p, DataFlowCallable c, Ap ap, Configuration config) { exists(ReturnNodeExt ret, Ap ap0, ReturnKindExt kind, int pos | parameterFlow(p, ap, ap0, c, config) and c = getNodeEnclosingCallable(ret) and @@ -2160,8 +2148,7 @@ private module Stage4 { pragma[nomagic] private predicate flowIntoCall( - DataFlowCall call, ArgumentNodeExt node1, ParameterNodeExt node2, boolean allowsFieldFlow, - Configuration config + DataFlowCall call, ArgNode node1, ParamNode node2, boolean allowsFieldFlow, Configuration config ) { flowIntoCallNodeCand2(call, node1, node2, allowsFieldFlow, config) and PrevStage::revFlow(node2, _, _, _, pragma[only_bind_into](config)) and @@ -2305,10 +2292,10 @@ private module Stage4 { pragma[nomagic] private predicate fwdFlowIn( - DataFlowCall call, ParameterNodeExt p, Cc outercc, Cc innercc, ApOption argAp, Ap ap, + DataFlowCall call, ParamNode p, Cc outercc, Cc innercc, ApOption argAp, Ap ap, Configuration config ) { - exists(ArgumentNodeExt arg, boolean allowsFieldFlow | + exists(ArgNode arg, boolean allowsFieldFlow | fwdFlow(arg, outercc, argAp, ap, config) and flowIntoCall(call, arg, p, allowsFieldFlow, config) and innercc = getCallContextCall(call, getNodeEnclosingCallable(p), outercc) @@ -2353,7 +2340,7 @@ private module Stage4 { private predicate fwdFlowIsEntered( DataFlowCall call, Cc cc, ApOption argAp, Ap ap, Configuration config ) { - exists(ParameterNodeExt p | + exists(ParamNode p | fwdFlowIn(call, p, cc, _, argAp, ap, config) and PrevStage::parameterMayFlowThrough(p, _, unbindApa(getApprox(ap)), config) ) @@ -2494,10 +2481,9 @@ private module Stage4 { pragma[nomagic] private predicate revFlowIn( - DataFlowCall call, ArgumentNodeExt arg, boolean toReturn, ApOption returnAp, Ap ap, - Configuration config + DataFlowCall call, ArgNode arg, boolean toReturn, ApOption returnAp, Ap ap, Configuration config ) { - exists(ParameterNodeExt p, boolean allowsFieldFlow | + exists(ParamNode p, boolean allowsFieldFlow | revFlow(p, toReturn, returnAp, ap, config) and flowIntoCall(call, arg, p, allowsFieldFlow, config) | @@ -2507,7 +2493,7 @@ private module Stage4 { pragma[nomagic] private predicate revFlowInToReturn( - DataFlowCall call, ArgumentNodeExt arg, Ap returnAp, Ap ap, Configuration config + DataFlowCall call, ArgNode arg, Ap returnAp, Ap ap, Configuration config ) { revFlowIn(call, arg, true, apSome(returnAp), ap, config) } @@ -2560,15 +2546,13 @@ private module Stage4 { pragma[noinline] private predicate parameterFlow( - ParameterNodeExt p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config + ParamNode p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config ) { revFlow(p, true, apSome(ap0), ap, config) and c = getNodeEnclosingCallable(p) } - predicate parameterMayFlowThrough( - ParameterNodeExt p, DataFlowCallable c, Ap ap, Configuration config - ) { + predicate parameterMayFlowThrough(ParamNode p, DataFlowCallable c, Ap ap, Configuration config) { exists(ReturnNodeExt ret, Ap ap0, ReturnKindExt kind, int pos | parameterFlow(p, ap, ap0, c, config) and c = getNodeEnclosingCallable(ret) and @@ -2613,7 +2597,7 @@ private predicate nodeMayUseSummary(Node n, AccessPathApprox apa, Configuration private newtype TSummaryCtx = TSummaryCtxNone() or - TSummaryCtxSome(ParameterNodeExt p, AccessPath ap) { + TSummaryCtxSome(ParamNode p, AccessPath ap) { Stage4::parameterMayFlowThrough(p, _, ap.getApprox(), _) } @@ -2634,7 +2618,7 @@ private class SummaryCtxNone extends SummaryCtx, TSummaryCtxNone { /** A summary context from which a flow summary can be generated. */ private class SummaryCtxSome extends SummaryCtx, TSummaryCtxSome { - private ParameterNodeExt p; + private ParamNode p; private AccessPath ap; SummaryCtxSome() { this = TSummaryCtxSome(p, ap) } @@ -3242,7 +3226,7 @@ pragma[noinline] private predicate pathIntoArg( PathNodeMid mid, int i, CallContext cc, DataFlowCall call, AccessPath ap, AccessPathApprox apa ) { - exists(ArgumentNodeExt arg | + exists(ArgNode arg | arg = mid.getNode() and cc = mid.getCallContext() and arg.argumentOf(call, i) and @@ -3255,7 +3239,7 @@ pragma[noinline] private predicate parameterCand( DataFlowCallable callable, int i, AccessPathApprox apa, Configuration config ) { - exists(ParameterNodeExt p | + exists(ParamNode p | Stage4::revFlow(p, _, _, apa, config) and p.isParameterOf(callable, i) ) @@ -3279,7 +3263,7 @@ private predicate pathIntoCallable0( * respectively. */ private predicate pathIntoCallable( - PathNodeMid mid, ParameterNodeExt p, CallContext outercc, CallContextCall innercc, SummaryCtx sc, + PathNodeMid mid, ParamNode p, CallContext outercc, CallContextCall innercc, SummaryCtx sc, DataFlowCall call ) { exists(int i, DataFlowCallable callable, AccessPath ap | @@ -3575,7 +3559,7 @@ private module FlowExploration { private newtype TSummaryCtx1 = TSummaryCtx1None() or - TSummaryCtx1Param(ParameterNodeExt p) + TSummaryCtx1Param(ParamNode p) private newtype TSummaryCtx2 = TSummaryCtx2None() or @@ -3931,7 +3915,7 @@ private module FlowExploration { PartialPathNodeFwd mid, int i, CallContext cc, DataFlowCall call, PartialAccessPath ap, Configuration config ) { - exists(ArgumentNodeExt arg | + exists(ArgNode arg | arg = mid.getNode() and cc = mid.getCallContext() and arg.argumentOf(call, i) and @@ -3950,7 +3934,7 @@ private module FlowExploration { } private predicate partialPathIntoCallable( - PartialPathNodeFwd mid, ParameterNodeExt p, CallContext outercc, CallContextCall innercc, + PartialPathNodeFwd mid, ParamNode p, CallContext outercc, CallContextCall innercc, TSummaryCtx1 sc1, TSummaryCtx2 sc2, DataFlowCall call, PartialAccessPath ap, Configuration config ) { @@ -3987,7 +3971,7 @@ private module FlowExploration { DataFlowCall call, PartialPathNodeFwd mid, ReturnKindExt kind, CallContext cc, PartialAccessPath ap, Configuration config ) { - exists(ParameterNodeExt p, CallContext innercc, TSummaryCtx1 sc1, TSummaryCtx2 sc2 | + exists(ParamNode p, CallContext innercc, TSummaryCtx1 sc1, TSummaryCtx2 sc2 | partialPathIntoCallable(mid, p, cc, innercc, sc1, sc2, call, _, config) and paramFlowsThroughInPartialPath(kind, innercc, sc1, sc2, ap, config) ) @@ -4044,7 +4028,7 @@ private module FlowExploration { apConsRev(ap, c, ap0, config) ) or - exists(ParameterNodeExt p | + exists(ParamNode p | mid.getNode() = p and viableParamArg(_, p, node) and sc1 = mid.getSummaryCtx1() and @@ -4122,7 +4106,7 @@ private module FlowExploration { int pos, TRevSummaryCtx1Some sc1, TRevSummaryCtx2Some sc2, RevPartialAccessPath ap, Configuration config ) { - exists(PartialPathNodeRev mid, ParameterNodeExt p | + exists(PartialPathNodeRev mid, ParamNode p | mid.getNode() = p and p.isParameterOf(_, pos) and sc1 = mid.getSummaryCtx1() and @@ -4145,7 +4129,7 @@ private module FlowExploration { pragma[nomagic] private predicate revPartialPathThroughCallable( - PartialPathNodeRev mid, ArgumentNodeExt node, RevPartialAccessPath ap, Configuration config + PartialPathNodeRev mid, ArgNode node, RevPartialAccessPath ap, Configuration config ) { exists(DataFlowCall call, int pos | revPartialPathThroughCallable0(call, mid, pos, ap, config) and 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 b5ceabc605d..b8e08408911 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 @@ -35,24 +35,22 @@ predicate accessPathCostLimits(int apLimit, int tupleLimit) { * calls. For this reason, we cannot reuse the code from `DataFlowImpl.qll` directly. */ private module LambdaFlow { - private predicate viableParamNonLambda(DataFlowCall call, int i, ParameterNodeExt p) { + private predicate viableParamNonLambda(DataFlowCall call, int i, ParamNode p) { p.isParameterOf(viableCallable(call), i) } - private predicate viableParamLambda(DataFlowCall call, int i, ParameterNodeExt p) { + private predicate viableParamLambda(DataFlowCall call, int i, ParamNode p) { p.isParameterOf(viableCallableLambda(call, _), i) } - private predicate viableParamArgNonLambda( - DataFlowCall call, ParameterNodeExt p, ArgumentNodeExt arg - ) { + private predicate viableParamArgNonLambda(DataFlowCall call, ParamNode p, ArgNode arg) { exists(int i | viableParamNonLambda(call, i, p) and arg.argumentOf(call, i) ) } - private predicate viableParamArgLambda(DataFlowCall call, ParameterNodeExt p, ArgumentNodeExt arg) { + private predicate viableParamArgLambda(DataFlowCall call, ParamNode p, ArgNode arg) { exists(int i | viableParamLambda(call, i, p) and arg.argumentOf(call, i) @@ -120,7 +118,7 @@ private module LambdaFlow { boolean toJump, DataFlowCallOption lastCall ) { revLambdaFlow0(lambdaCall, kind, node, t, toReturn, toJump, lastCall) and - if castNode(node) or node instanceof ArgumentNodeExt or node instanceof ReturnNode + if castNode(node) or node instanceof ArgNode or node instanceof ReturnNode then compatibleTypes(t, getNodeDataFlowType(node)) else any() } @@ -178,7 +176,7 @@ private module LambdaFlow { ) or // flow into a callable - exists(ParameterNodeExt p, DataFlowCallOption lastCall0, DataFlowCall call | + exists(ParamNode p, DataFlowCallOption lastCall0, DataFlowCall call | revLambdaFlowIn(lambdaCall, kind, p, t, toJump, lastCall0) and ( if lastCall0 = TDataFlowCallNone() and toJump = false @@ -229,8 +227,8 @@ private module LambdaFlow { pragma[nomagic] predicate revLambdaFlowIn( - DataFlowCall lambdaCall, LambdaCallKind kind, ParameterNodeExt p, DataFlowType t, - boolean toJump, DataFlowCallOption lastCall + DataFlowCall lambdaCall, LambdaCallKind kind, ParamNode p, DataFlowType t, boolean toJump, + DataFlowCallOption lastCall ) { revLambdaFlow(lambdaCall, kind, p, t, false, toJump, lastCall) } @@ -276,7 +274,7 @@ private module Cached { predicate outNodeExt(Node n) { n instanceof OutNode or - n.(PostUpdateNode).getPreUpdateNode() instanceof ArgumentNodeExt + n.(PostUpdateNode).getPreUpdateNode() instanceof ArgNode } cached @@ -286,7 +284,7 @@ private module Cached { OutNodeExt getAnOutNodeExt(DataFlowCall call, ReturnKindExt k) { result = getAnOutNode(call, k.(ValueReturnKind).getKind()) or - exists(ArgumentNodeExt arg | + exists(ArgNode arg | result.(PostUpdateNode).getPreUpdateNode() = arg and arg.argumentOf(call, k.(ParamUpdateReturnKind).getPosition()) ) @@ -296,7 +294,7 @@ private module Cached { predicate returnNodeExt(Node n, ReturnKindExt k) { k = TValueReturn(n.(ReturnNode).getKind()) or - exists(ParameterNodeExt p, int pos | + exists(ParamNode p, int pos | parameterValueFlowsToPreUpdate(p, n) and p.isParameterOf(_, pos) and k = TParamUpdate(pos) @@ -309,7 +307,7 @@ private module Cached { cached predicate castingNode(Node n) { castNode(n) or - n instanceof ParameterNodeExt or + n instanceof ParamNode or n instanceof OutNodeExt or // For reads, `x.f`, we want to check that the tracked type after the read (which // is obtained by popping the head of the access path stack) is compatible with @@ -346,7 +344,7 @@ private module Cached { * The instance parameter is considered to have index `-1`. */ pragma[nomagic] - private predicate viableParam(DataFlowCall call, int i, ParameterNodeExt p) { + private predicate viableParam(DataFlowCall call, int i, ParamNode p) { p.isParameterOf(viableCallableExt(call), i) } @@ -355,7 +353,7 @@ private module Cached { * dispatch into account. */ cached - predicate viableParamArg(DataFlowCall call, ParameterNodeExt p, ArgumentNodeExt arg) { + predicate viableParamArg(DataFlowCall call, ParamNode p, ArgNode arg) { exists(int i | viableParam(call, i, p) and arg.argumentOf(call, i) and @@ -397,7 +395,7 @@ private module Cached { * `read` indicates whether it is contents of `p` that can flow to `node`. */ pragma[nomagic] - private predicate parameterValueFlowCand(ParameterNodeExt p, Node node, boolean read) { + private predicate parameterValueFlowCand(ParamNode p, Node node, boolean read) { p = node and read = false or @@ -415,27 +413,25 @@ private module Cached { ) or // flow through: no prior read - exists(ArgumentNodeExt arg | + exists(ArgNode arg | parameterValueFlowArgCand(p, arg, false) and argumentValueFlowsThroughCand(arg, node, read) ) or // flow through: no read inside method - exists(ArgumentNodeExt arg | + exists(ArgNode arg | parameterValueFlowArgCand(p, arg, read) and argumentValueFlowsThroughCand(arg, node, false) ) } pragma[nomagic] - private predicate parameterValueFlowArgCand( - ParameterNodeExt p, ArgumentNodeExt arg, boolean read - ) { + private predicate parameterValueFlowArgCand(ParamNode p, ArgNode arg, boolean read) { parameterValueFlowCand(p, arg, read) } pragma[nomagic] - predicate parameterValueFlowsToPreUpdateCand(ParameterNodeExt p, PostUpdateNode n) { + predicate parameterValueFlowsToPreUpdateCand(ParamNode p, PostUpdateNode n) { parameterValueFlowCand(p, n.getPreUpdateNode(), false) } @@ -447,7 +443,7 @@ private module Cached { * `read` indicates whether it is contents of `p` that can flow to the return * node. */ - predicate parameterValueFlowReturnCand(ParameterNodeExt p, ReturnKind kind, boolean read) { + predicate parameterValueFlowReturnCand(ParamNode p, ReturnKind kind, boolean read) { exists(ReturnNode ret | parameterValueFlowCand(p, ret, read) and kind = ret.getKind() @@ -456,9 +452,9 @@ private module Cached { pragma[nomagic] private predicate argumentValueFlowsThroughCand0( - DataFlowCall call, ArgumentNodeExt arg, ReturnKind kind, boolean read + DataFlowCall call, ArgNode arg, ReturnKind kind, boolean read ) { - exists(ParameterNodeExt param | viableParamArg(call, param, arg) | + exists(ParamNode param | viableParamArg(call, param, arg) | parameterValueFlowReturnCand(param, kind, read) ) } @@ -469,14 +465,14 @@ private module Cached { * * `read` indicates whether it is contents of `arg` that can flow to `out`. */ - predicate argumentValueFlowsThroughCand(ArgumentNodeExt arg, Node out, boolean read) { + predicate argumentValueFlowsThroughCand(ArgNode arg, Node out, boolean read) { exists(DataFlowCall call, ReturnKind kind | argumentValueFlowsThroughCand0(call, arg, kind, read) and out = getAnOutNode(call, kind) ) } - predicate cand(ParameterNodeExt p, Node n) { + predicate cand(ParamNode p, Node n) { parameterValueFlowCand(p, n, _) and ( parameterValueFlowReturnCand(p, _, _) @@ -503,7 +499,7 @@ private module Cached { * If a read step was taken, then `read` captures the `Content`, the * container type, and the content type. */ - predicate parameterValueFlow(ParameterNodeExt p, Node node, ReadStepTypesOption read) { + predicate parameterValueFlow(ParamNode p, Node node, ReadStepTypesOption read) { parameterValueFlow0(p, node, read) and if node instanceof CastingNode then @@ -517,7 +513,7 @@ private module Cached { } pragma[nomagic] - private predicate parameterValueFlow0(ParameterNodeExt p, Node node, ReadStepTypesOption read) { + private predicate parameterValueFlow0(ParamNode p, Node node, ReadStepTypesOption read) { p = node and Cand::cand(p, _) and read = TReadStepTypesNone() @@ -542,34 +538,32 @@ private module Cached { pragma[nomagic] private predicate parameterValueFlow0_0( - ReadStepTypesOption mustBeNone, ParameterNodeExt p, Node node, ReadStepTypesOption read + ReadStepTypesOption mustBeNone, ParamNode p, Node node, ReadStepTypesOption read ) { // flow through: no prior read - exists(ArgumentNodeExt arg | + exists(ArgNode arg | parameterValueFlowArg(p, arg, mustBeNone) and argumentValueFlowsThrough(arg, read, node) ) or // flow through: no read inside method - exists(ArgumentNodeExt arg | + exists(ArgNode arg | parameterValueFlowArg(p, arg, read) and argumentValueFlowsThrough(arg, mustBeNone, node) ) } pragma[nomagic] - private predicate parameterValueFlowArg( - ParameterNodeExt p, ArgumentNodeExt arg, ReadStepTypesOption read - ) { + private predicate parameterValueFlowArg(ParamNode p, ArgNode arg, ReadStepTypesOption read) { parameterValueFlow(p, arg, read) and Cand::argumentValueFlowsThroughCand(arg, _, _) } pragma[nomagic] private predicate argumentValueFlowsThrough0( - DataFlowCall call, ArgumentNodeExt arg, ReturnKind kind, ReadStepTypesOption read + DataFlowCall call, ArgNode arg, ReturnKind kind, ReadStepTypesOption read ) { - exists(ParameterNodeExt param | viableParamArg(call, param, arg) | + exists(ParamNode param | viableParamArg(call, param, arg) | parameterValueFlowReturn(param, kind, read) ) } @@ -583,7 +577,7 @@ private module Cached { * container type, and the content type. */ pragma[nomagic] - predicate argumentValueFlowsThrough(ArgumentNodeExt arg, ReadStepTypesOption read, Node out) { + predicate argumentValueFlowsThrough(ArgNode arg, ReadStepTypesOption read, Node out) { exists(DataFlowCall call, ReturnKind kind | argumentValueFlowsThrough0(call, arg, kind, read) and out = getAnOutNode(call, kind) @@ -603,7 +597,7 @@ private module Cached { * value-preserving steps and a single read step, not taking call * contexts into account, thus representing a getter-step. */ - predicate getterStep(ArgumentNodeExt arg, Content c, Node out) { + predicate getterStep(ArgNode arg, Content c, Node out) { argumentValueFlowsThrough(arg, TReadStepTypesSome(_, c, _), out) } @@ -616,7 +610,7 @@ private module Cached { * container type, and the content type. */ private predicate parameterValueFlowReturn( - ParameterNodeExt p, ReturnKind kind, ReadStepTypesOption read + ParamNode p, ReturnKind kind, ReadStepTypesOption read ) { exists(ReturnNode ret | parameterValueFlow(p, ret, read) and @@ -722,7 +716,7 @@ private module Cached { * Holds if `p` can flow to the pre-update node associated with post-update * node `n`, in the same callable, using only value-preserving steps. */ - private predicate parameterValueFlowsToPreUpdate(ParameterNodeExt p, PostUpdateNode n) { + private predicate parameterValueFlowsToPreUpdate(ParamNode p, PostUpdateNode n) { parameterValueFlow(p, n.getPreUpdateNode(), TReadStepTypesNone()) } @@ -778,8 +772,8 @@ private module Cached { // Does the language-specific simpleLocalFlowStep already model flow // from function input to output? fromPre = getAnOutNode(c, _) and - toPre.(ArgumentNodeExt).argumentOf(c, _) and - simpleLocalFlowStep(toPre.(ArgumentNodeExt), fromPre) + toPre.(ArgNode).argumentOf(c, _) and + simpleLocalFlowStep(toPre.(ArgNode), fromPre) ) or argumentValueFlowsThrough(toPre, TReadStepTypesNone(), fromPre) @@ -827,7 +821,7 @@ private module Cached { cached newtype TReturnKindExt = TValueReturn(ReturnKind kind) or - TParamUpdate(int pos) { exists(ParameterNodeExt p | p.isParameterOf(_, pos)) } + TParamUpdate(int pos) { exists(ParamNode p | p.isParameterOf(_, pos)) } cached newtype TBooleanOption = @@ -942,7 +936,7 @@ class CallContextSomeCall extends CallContextCall, TSomeCall { override string toString() { result = "CcSomeCall" } override predicate relevantFor(DataFlowCallable callable) { - exists(ParameterNodeExt p | getNodeEnclosingCallable(p) = callable) + exists(ParamNode p | getNodeEnclosingCallable(p) = callable) } override predicate matchesCall(DataFlowCall call) { any() } @@ -1005,8 +999,8 @@ LocalCallContext getLocalCallContext(CallContext ctx, DataFlowCallable callable) * The value of a parameter at function entry, viewed as a node in a data * flow graph. */ -class ParameterNodeExt extends Node { - ParameterNodeExt() { parameterNode(this, _, _) } +class ParamNode extends Node { + ParamNode() { parameterNode(this, _, _) } /** * Holds if this node is the parameter of callable `c` at the specified @@ -1016,8 +1010,8 @@ class ParameterNodeExt extends Node { } /** A data-flow node that represents a call argument. */ -class ArgumentNodeExt extends Node { - ArgumentNodeExt() { argumentNode(this, _, _) } +class ArgNode extends Node { + ArgNode() { argumentNode(this, _, _) } /** Holds if this argument occurs at the given position in the given call. */ final predicate argumentOf(DataFlowCall call, int pos) { argumentNode(this, call, pos) } 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 cf83530bcbd..058d66b1496 100644 --- a/csharp/ql/src/semmle/code/csharp/dataflow/internal/DataFlowImpl.qll +++ b/csharp/ql/src/semmle/code/csharp/dataflow/internal/DataFlowImpl.qll @@ -385,7 +385,7 @@ private module Stage1 { */ pragma[nomagic] private predicate fwdFlowIsEntered(DataFlowCall call, Cc cc, Configuration config) { - exists(ArgumentNodeExt arg | + exists(ArgNode arg | fwdFlow(arg, cc, config) and viableParamArg(call, _, arg) ) @@ -512,24 +512,22 @@ private module Stage1 { pragma[nomagic] predicate viableParamArgNodeCandFwd1( - DataFlowCall call, ParameterNodeExt p, ArgumentNodeExt arg, Configuration config + DataFlowCall call, ParamNode p, ArgNode arg, Configuration config ) { viableParamArg(call, p, arg) and fwdFlow(arg, config) } pragma[nomagic] - private predicate revFlowIn( - DataFlowCall call, ArgumentNodeExt arg, boolean toReturn, Configuration config - ) { - exists(ParameterNodeExt p | + private predicate revFlowIn(DataFlowCall call, ArgNode arg, boolean toReturn, Configuration config) { + exists(ParamNode p | revFlow(p, toReturn, config) and viableParamArgNodeCandFwd1(call, p, arg, config) ) } pragma[nomagic] - private predicate revFlowInToReturn(DataFlowCall call, ArgumentNodeExt arg, Configuration config) { + private predicate revFlowInToReturn(DataFlowCall call, ArgNode arg, Configuration config) { revFlowIn(call, arg, true, config) } @@ -594,9 +592,7 @@ private module Stage1 { * Holds if flow may enter through `p` and reach a return node making `p` a * candidate for the origin of a summary. */ - predicate parameterMayFlowThrough( - ParameterNodeExt p, DataFlowCallable c, Ap ap, Configuration config - ) { + predicate parameterMayFlowThrough(ParamNode p, DataFlowCallable c, Ap ap, Configuration config) { exists(ReturnKindExt kind | throughFlowNodeCand(p, config) and returnFlowCallableNodeCand(c, kind, config) and @@ -662,7 +658,7 @@ private predicate flowOutOfCallNodeCand1( pragma[nomagic] private predicate viableParamArgNodeCand1( - DataFlowCall call, ParameterNodeExt p, ArgumentNodeExt arg, Configuration config + DataFlowCall call, ParamNode p, ArgNode arg, Configuration config ) { Stage1::viableParamArgNodeCandFwd1(call, p, arg, config) and Stage1::revFlow(arg, config) @@ -674,7 +670,7 @@ private predicate viableParamArgNodeCand1( */ pragma[nomagic] private predicate flowIntoCallNodeCand1( - DataFlowCall call, ArgumentNodeExt arg, ParameterNodeExt p, Configuration config + DataFlowCall call, ArgNode arg, ParamNode p, Configuration config ) { viableParamArgNodeCand1(call, p, arg, config) and Stage1::revFlow(p, config) and @@ -734,8 +730,7 @@ private predicate flowOutOfCallNodeCand1( */ pragma[nomagic] private predicate flowIntoCallNodeCand1( - DataFlowCall call, ArgumentNodeExt arg, ParameterNodeExt p, boolean allowsFieldFlow, - Configuration config + DataFlowCall call, ArgNode arg, ParamNode p, boolean allowsFieldFlow, Configuration config ) { flowIntoCallNodeCand1(call, arg, p, config) and exists(int b, int j | @@ -943,10 +938,10 @@ private module Stage2 { pragma[nomagic] private predicate fwdFlowIn( - DataFlowCall call, ParameterNodeExt p, Cc outercc, Cc innercc, ApOption argAp, Ap ap, + DataFlowCall call, ParamNode p, Cc outercc, Cc innercc, ApOption argAp, Ap ap, Configuration config ) { - exists(ArgumentNodeExt arg, boolean allowsFieldFlow | + exists(ArgNode arg, boolean allowsFieldFlow | fwdFlow(arg, outercc, argAp, ap, config) and flowIntoCall(call, arg, p, allowsFieldFlow, config) and innercc = getCallContextCall(call, getNodeEnclosingCallable(p), outercc) @@ -991,7 +986,7 @@ private module Stage2 { private predicate fwdFlowIsEntered( DataFlowCall call, Cc cc, ApOption argAp, Ap ap, Configuration config ) { - exists(ParameterNodeExt p | + exists(ParamNode p | fwdFlowIn(call, p, cc, _, argAp, ap, config) and PrevStage::parameterMayFlowThrough(p, _, getApprox(ap), config) ) @@ -1132,10 +1127,9 @@ private module Stage2 { pragma[nomagic] private predicate revFlowIn( - DataFlowCall call, ArgumentNodeExt arg, boolean toReturn, ApOption returnAp, Ap ap, - Configuration config + DataFlowCall call, ArgNode arg, boolean toReturn, ApOption returnAp, Ap ap, Configuration config ) { - exists(ParameterNodeExt p, boolean allowsFieldFlow | + exists(ParamNode p, boolean allowsFieldFlow | revFlow(p, toReturn, returnAp, ap, config) and flowIntoCall(call, arg, p, allowsFieldFlow, config) | @@ -1145,7 +1139,7 @@ private module Stage2 { pragma[nomagic] private predicate revFlowInToReturn( - DataFlowCall call, ArgumentNodeExt arg, Ap returnAp, Ap ap, Configuration config + DataFlowCall call, ArgNode arg, Ap returnAp, Ap ap, Configuration config ) { revFlowIn(call, arg, true, apSome(returnAp), ap, config) } @@ -1198,15 +1192,13 @@ private module Stage2 { pragma[noinline] private predicate parameterFlow( - ParameterNodeExt p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config + ParamNode p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config ) { revFlow(p, true, apSome(ap0), ap, config) and c = getNodeEnclosingCallable(p) } - predicate parameterMayFlowThrough( - ParameterNodeExt p, DataFlowCallable c, Ap ap, Configuration config - ) { + predicate parameterMayFlowThrough(ParamNode p, DataFlowCallable c, Ap ap, Configuration config) { exists(ReturnNodeExt ret, Ap ap0, ReturnKindExt kind, int pos | parameterFlow(p, ap, ap0, c, config) and c = getNodeEnclosingCallable(ret) and @@ -1246,8 +1238,7 @@ private predicate flowOutOfCallNodeCand2( pragma[nomagic] private predicate flowIntoCallNodeCand2( - DataFlowCall call, ArgumentNodeExt node1, ParameterNodeExt node2, boolean allowsFieldFlow, - Configuration config + DataFlowCall call, ArgNode node1, ParamNode node2, boolean allowsFieldFlow, Configuration config ) { flowIntoCallNodeCand1(call, node1, node2, allowsFieldFlow, config) and Stage2::revFlow(node2, pragma[only_bind_into](config)) and @@ -1276,7 +1267,7 @@ private module LocalFlowBigStep { config.isSource(node) or jumpStep(_, node, config) or additionalJumpStep(_, node, config) or - node instanceof ParameterNodeExt or + node instanceof ParamNode or node instanceof OutNodeExt or store(_, _, node, _) or read(_, _, node) or @@ -1586,10 +1577,10 @@ private module Stage3 { pragma[nomagic] private predicate fwdFlowIn( - DataFlowCall call, ParameterNodeExt p, Cc outercc, Cc innercc, ApOption argAp, Ap ap, + DataFlowCall call, ParamNode p, Cc outercc, Cc innercc, ApOption argAp, Ap ap, Configuration config ) { - exists(ArgumentNodeExt arg, boolean allowsFieldFlow | + exists(ArgNode arg, boolean allowsFieldFlow | fwdFlow(arg, outercc, argAp, ap, config) and flowIntoCall(call, arg, p, allowsFieldFlow, config) and innercc = getCallContextCall(call, getNodeEnclosingCallable(p), outercc) @@ -1634,7 +1625,7 @@ private module Stage3 { private predicate fwdFlowIsEntered( DataFlowCall call, Cc cc, ApOption argAp, Ap ap, Configuration config ) { - exists(ParameterNodeExt p | + exists(ParamNode p | fwdFlowIn(call, p, cc, _, argAp, ap, config) and PrevStage::parameterMayFlowThrough(p, _, unbindApa(getApprox(ap)), config) ) @@ -1775,10 +1766,9 @@ private module Stage3 { pragma[nomagic] private predicate revFlowIn( - DataFlowCall call, ArgumentNodeExt arg, boolean toReturn, ApOption returnAp, Ap ap, - Configuration config + DataFlowCall call, ArgNode arg, boolean toReturn, ApOption returnAp, Ap ap, Configuration config ) { - exists(ParameterNodeExt p, boolean allowsFieldFlow | + exists(ParamNode p, boolean allowsFieldFlow | revFlow(p, toReturn, returnAp, ap, config) and flowIntoCall(call, arg, p, allowsFieldFlow, config) | @@ -1788,7 +1778,7 @@ private module Stage3 { pragma[nomagic] private predicate revFlowInToReturn( - DataFlowCall call, ArgumentNodeExt arg, Ap returnAp, Ap ap, Configuration config + DataFlowCall call, ArgNode arg, Ap returnAp, Ap ap, Configuration config ) { revFlowIn(call, arg, true, apSome(returnAp), ap, config) } @@ -1841,15 +1831,13 @@ private module Stage3 { pragma[noinline] private predicate parameterFlow( - ParameterNodeExt p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config + ParamNode p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config ) { revFlow(p, true, apSome(ap0), ap, config) and c = getNodeEnclosingCallable(p) } - predicate parameterMayFlowThrough( - ParameterNodeExt p, DataFlowCallable c, Ap ap, Configuration config - ) { + predicate parameterMayFlowThrough(ParamNode p, DataFlowCallable c, Ap ap, Configuration config) { exists(ReturnNodeExt ret, Ap ap0, ReturnKindExt kind, int pos | parameterFlow(p, ap, ap0, c, config) and c = getNodeEnclosingCallable(ret) and @@ -2160,8 +2148,7 @@ private module Stage4 { pragma[nomagic] private predicate flowIntoCall( - DataFlowCall call, ArgumentNodeExt node1, ParameterNodeExt node2, boolean allowsFieldFlow, - Configuration config + DataFlowCall call, ArgNode node1, ParamNode node2, boolean allowsFieldFlow, Configuration config ) { flowIntoCallNodeCand2(call, node1, node2, allowsFieldFlow, config) and PrevStage::revFlow(node2, _, _, _, pragma[only_bind_into](config)) and @@ -2305,10 +2292,10 @@ private module Stage4 { pragma[nomagic] private predicate fwdFlowIn( - DataFlowCall call, ParameterNodeExt p, Cc outercc, Cc innercc, ApOption argAp, Ap ap, + DataFlowCall call, ParamNode p, Cc outercc, Cc innercc, ApOption argAp, Ap ap, Configuration config ) { - exists(ArgumentNodeExt arg, boolean allowsFieldFlow | + exists(ArgNode arg, boolean allowsFieldFlow | fwdFlow(arg, outercc, argAp, ap, config) and flowIntoCall(call, arg, p, allowsFieldFlow, config) and innercc = getCallContextCall(call, getNodeEnclosingCallable(p), outercc) @@ -2353,7 +2340,7 @@ private module Stage4 { private predicate fwdFlowIsEntered( DataFlowCall call, Cc cc, ApOption argAp, Ap ap, Configuration config ) { - exists(ParameterNodeExt p | + exists(ParamNode p | fwdFlowIn(call, p, cc, _, argAp, ap, config) and PrevStage::parameterMayFlowThrough(p, _, unbindApa(getApprox(ap)), config) ) @@ -2494,10 +2481,9 @@ private module Stage4 { pragma[nomagic] private predicate revFlowIn( - DataFlowCall call, ArgumentNodeExt arg, boolean toReturn, ApOption returnAp, Ap ap, - Configuration config + DataFlowCall call, ArgNode arg, boolean toReturn, ApOption returnAp, Ap ap, Configuration config ) { - exists(ParameterNodeExt p, boolean allowsFieldFlow | + exists(ParamNode p, boolean allowsFieldFlow | revFlow(p, toReturn, returnAp, ap, config) and flowIntoCall(call, arg, p, allowsFieldFlow, config) | @@ -2507,7 +2493,7 @@ private module Stage4 { pragma[nomagic] private predicate revFlowInToReturn( - DataFlowCall call, ArgumentNodeExt arg, Ap returnAp, Ap ap, Configuration config + DataFlowCall call, ArgNode arg, Ap returnAp, Ap ap, Configuration config ) { revFlowIn(call, arg, true, apSome(returnAp), ap, config) } @@ -2560,15 +2546,13 @@ private module Stage4 { pragma[noinline] private predicate parameterFlow( - ParameterNodeExt p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config + ParamNode p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config ) { revFlow(p, true, apSome(ap0), ap, config) and c = getNodeEnclosingCallable(p) } - predicate parameterMayFlowThrough( - ParameterNodeExt p, DataFlowCallable c, Ap ap, Configuration config - ) { + predicate parameterMayFlowThrough(ParamNode p, DataFlowCallable c, Ap ap, Configuration config) { exists(ReturnNodeExt ret, Ap ap0, ReturnKindExt kind, int pos | parameterFlow(p, ap, ap0, c, config) and c = getNodeEnclosingCallable(ret) and @@ -2613,7 +2597,7 @@ private predicate nodeMayUseSummary(Node n, AccessPathApprox apa, Configuration private newtype TSummaryCtx = TSummaryCtxNone() or - TSummaryCtxSome(ParameterNodeExt p, AccessPath ap) { + TSummaryCtxSome(ParamNode p, AccessPath ap) { Stage4::parameterMayFlowThrough(p, _, ap.getApprox(), _) } @@ -2634,7 +2618,7 @@ private class SummaryCtxNone extends SummaryCtx, TSummaryCtxNone { /** A summary context from which a flow summary can be generated. */ private class SummaryCtxSome extends SummaryCtx, TSummaryCtxSome { - private ParameterNodeExt p; + private ParamNode p; private AccessPath ap; SummaryCtxSome() { this = TSummaryCtxSome(p, ap) } @@ -3242,7 +3226,7 @@ pragma[noinline] private predicate pathIntoArg( PathNodeMid mid, int i, CallContext cc, DataFlowCall call, AccessPath ap, AccessPathApprox apa ) { - exists(ArgumentNodeExt arg | + exists(ArgNode arg | arg = mid.getNode() and cc = mid.getCallContext() and arg.argumentOf(call, i) and @@ -3255,7 +3239,7 @@ pragma[noinline] private predicate parameterCand( DataFlowCallable callable, int i, AccessPathApprox apa, Configuration config ) { - exists(ParameterNodeExt p | + exists(ParamNode p | Stage4::revFlow(p, _, _, apa, config) and p.isParameterOf(callable, i) ) @@ -3279,7 +3263,7 @@ private predicate pathIntoCallable0( * respectively. */ private predicate pathIntoCallable( - PathNodeMid mid, ParameterNodeExt p, CallContext outercc, CallContextCall innercc, SummaryCtx sc, + PathNodeMid mid, ParamNode p, CallContext outercc, CallContextCall innercc, SummaryCtx sc, DataFlowCall call ) { exists(int i, DataFlowCallable callable, AccessPath ap | @@ -3575,7 +3559,7 @@ private module FlowExploration { private newtype TSummaryCtx1 = TSummaryCtx1None() or - TSummaryCtx1Param(ParameterNodeExt p) + TSummaryCtx1Param(ParamNode p) private newtype TSummaryCtx2 = TSummaryCtx2None() or @@ -3931,7 +3915,7 @@ private module FlowExploration { PartialPathNodeFwd mid, int i, CallContext cc, DataFlowCall call, PartialAccessPath ap, Configuration config ) { - exists(ArgumentNodeExt arg | + exists(ArgNode arg | arg = mid.getNode() and cc = mid.getCallContext() and arg.argumentOf(call, i) and @@ -3950,7 +3934,7 @@ private module FlowExploration { } private predicate partialPathIntoCallable( - PartialPathNodeFwd mid, ParameterNodeExt p, CallContext outercc, CallContextCall innercc, + PartialPathNodeFwd mid, ParamNode p, CallContext outercc, CallContextCall innercc, TSummaryCtx1 sc1, TSummaryCtx2 sc2, DataFlowCall call, PartialAccessPath ap, Configuration config ) { @@ -3987,7 +3971,7 @@ private module FlowExploration { DataFlowCall call, PartialPathNodeFwd mid, ReturnKindExt kind, CallContext cc, PartialAccessPath ap, Configuration config ) { - exists(ParameterNodeExt p, CallContext innercc, TSummaryCtx1 sc1, TSummaryCtx2 sc2 | + exists(ParamNode p, CallContext innercc, TSummaryCtx1 sc1, TSummaryCtx2 sc2 | partialPathIntoCallable(mid, p, cc, innercc, sc1, sc2, call, _, config) and paramFlowsThroughInPartialPath(kind, innercc, sc1, sc2, ap, config) ) @@ -4044,7 +4028,7 @@ private module FlowExploration { apConsRev(ap, c, ap0, config) ) or - exists(ParameterNodeExt p | + exists(ParamNode p | mid.getNode() = p and viableParamArg(_, p, node) and sc1 = mid.getSummaryCtx1() and @@ -4122,7 +4106,7 @@ private module FlowExploration { int pos, TRevSummaryCtx1Some sc1, TRevSummaryCtx2Some sc2, RevPartialAccessPath ap, Configuration config ) { - exists(PartialPathNodeRev mid, ParameterNodeExt p | + exists(PartialPathNodeRev mid, ParamNode p | mid.getNode() = p and p.isParameterOf(_, pos) and sc1 = mid.getSummaryCtx1() and @@ -4145,7 +4129,7 @@ private module FlowExploration { pragma[nomagic] private predicate revPartialPathThroughCallable( - PartialPathNodeRev mid, ArgumentNodeExt node, RevPartialAccessPath ap, Configuration config + PartialPathNodeRev mid, ArgNode node, RevPartialAccessPath ap, Configuration config ) { exists(DataFlowCall call, int pos | revPartialPathThroughCallable0(call, mid, pos, ap, config) and 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 cf83530bcbd..058d66b1496 100644 --- a/csharp/ql/src/semmle/code/csharp/dataflow/internal/DataFlowImpl2.qll +++ b/csharp/ql/src/semmle/code/csharp/dataflow/internal/DataFlowImpl2.qll @@ -385,7 +385,7 @@ private module Stage1 { */ pragma[nomagic] private predicate fwdFlowIsEntered(DataFlowCall call, Cc cc, Configuration config) { - exists(ArgumentNodeExt arg | + exists(ArgNode arg | fwdFlow(arg, cc, config) and viableParamArg(call, _, arg) ) @@ -512,24 +512,22 @@ private module Stage1 { pragma[nomagic] predicate viableParamArgNodeCandFwd1( - DataFlowCall call, ParameterNodeExt p, ArgumentNodeExt arg, Configuration config + DataFlowCall call, ParamNode p, ArgNode arg, Configuration config ) { viableParamArg(call, p, arg) and fwdFlow(arg, config) } pragma[nomagic] - private predicate revFlowIn( - DataFlowCall call, ArgumentNodeExt arg, boolean toReturn, Configuration config - ) { - exists(ParameterNodeExt p | + private predicate revFlowIn(DataFlowCall call, ArgNode arg, boolean toReturn, Configuration config) { + exists(ParamNode p | revFlow(p, toReturn, config) and viableParamArgNodeCandFwd1(call, p, arg, config) ) } pragma[nomagic] - private predicate revFlowInToReturn(DataFlowCall call, ArgumentNodeExt arg, Configuration config) { + private predicate revFlowInToReturn(DataFlowCall call, ArgNode arg, Configuration config) { revFlowIn(call, arg, true, config) } @@ -594,9 +592,7 @@ private module Stage1 { * Holds if flow may enter through `p` and reach a return node making `p` a * candidate for the origin of a summary. */ - predicate parameterMayFlowThrough( - ParameterNodeExt p, DataFlowCallable c, Ap ap, Configuration config - ) { + predicate parameterMayFlowThrough(ParamNode p, DataFlowCallable c, Ap ap, Configuration config) { exists(ReturnKindExt kind | throughFlowNodeCand(p, config) and returnFlowCallableNodeCand(c, kind, config) and @@ -662,7 +658,7 @@ private predicate flowOutOfCallNodeCand1( pragma[nomagic] private predicate viableParamArgNodeCand1( - DataFlowCall call, ParameterNodeExt p, ArgumentNodeExt arg, Configuration config + DataFlowCall call, ParamNode p, ArgNode arg, Configuration config ) { Stage1::viableParamArgNodeCandFwd1(call, p, arg, config) and Stage1::revFlow(arg, config) @@ -674,7 +670,7 @@ private predicate viableParamArgNodeCand1( */ pragma[nomagic] private predicate flowIntoCallNodeCand1( - DataFlowCall call, ArgumentNodeExt arg, ParameterNodeExt p, Configuration config + DataFlowCall call, ArgNode arg, ParamNode p, Configuration config ) { viableParamArgNodeCand1(call, p, arg, config) and Stage1::revFlow(p, config) and @@ -734,8 +730,7 @@ private predicate flowOutOfCallNodeCand1( */ pragma[nomagic] private predicate flowIntoCallNodeCand1( - DataFlowCall call, ArgumentNodeExt arg, ParameterNodeExt p, boolean allowsFieldFlow, - Configuration config + DataFlowCall call, ArgNode arg, ParamNode p, boolean allowsFieldFlow, Configuration config ) { flowIntoCallNodeCand1(call, arg, p, config) and exists(int b, int j | @@ -943,10 +938,10 @@ private module Stage2 { pragma[nomagic] private predicate fwdFlowIn( - DataFlowCall call, ParameterNodeExt p, Cc outercc, Cc innercc, ApOption argAp, Ap ap, + DataFlowCall call, ParamNode p, Cc outercc, Cc innercc, ApOption argAp, Ap ap, Configuration config ) { - exists(ArgumentNodeExt arg, boolean allowsFieldFlow | + exists(ArgNode arg, boolean allowsFieldFlow | fwdFlow(arg, outercc, argAp, ap, config) and flowIntoCall(call, arg, p, allowsFieldFlow, config) and innercc = getCallContextCall(call, getNodeEnclosingCallable(p), outercc) @@ -991,7 +986,7 @@ private module Stage2 { private predicate fwdFlowIsEntered( DataFlowCall call, Cc cc, ApOption argAp, Ap ap, Configuration config ) { - exists(ParameterNodeExt p | + exists(ParamNode p | fwdFlowIn(call, p, cc, _, argAp, ap, config) and PrevStage::parameterMayFlowThrough(p, _, getApprox(ap), config) ) @@ -1132,10 +1127,9 @@ private module Stage2 { pragma[nomagic] private predicate revFlowIn( - DataFlowCall call, ArgumentNodeExt arg, boolean toReturn, ApOption returnAp, Ap ap, - Configuration config + DataFlowCall call, ArgNode arg, boolean toReturn, ApOption returnAp, Ap ap, Configuration config ) { - exists(ParameterNodeExt p, boolean allowsFieldFlow | + exists(ParamNode p, boolean allowsFieldFlow | revFlow(p, toReturn, returnAp, ap, config) and flowIntoCall(call, arg, p, allowsFieldFlow, config) | @@ -1145,7 +1139,7 @@ private module Stage2 { pragma[nomagic] private predicate revFlowInToReturn( - DataFlowCall call, ArgumentNodeExt arg, Ap returnAp, Ap ap, Configuration config + DataFlowCall call, ArgNode arg, Ap returnAp, Ap ap, Configuration config ) { revFlowIn(call, arg, true, apSome(returnAp), ap, config) } @@ -1198,15 +1192,13 @@ private module Stage2 { pragma[noinline] private predicate parameterFlow( - ParameterNodeExt p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config + ParamNode p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config ) { revFlow(p, true, apSome(ap0), ap, config) and c = getNodeEnclosingCallable(p) } - predicate parameterMayFlowThrough( - ParameterNodeExt p, DataFlowCallable c, Ap ap, Configuration config - ) { + predicate parameterMayFlowThrough(ParamNode p, DataFlowCallable c, Ap ap, Configuration config) { exists(ReturnNodeExt ret, Ap ap0, ReturnKindExt kind, int pos | parameterFlow(p, ap, ap0, c, config) and c = getNodeEnclosingCallable(ret) and @@ -1246,8 +1238,7 @@ private predicate flowOutOfCallNodeCand2( pragma[nomagic] private predicate flowIntoCallNodeCand2( - DataFlowCall call, ArgumentNodeExt node1, ParameterNodeExt node2, boolean allowsFieldFlow, - Configuration config + DataFlowCall call, ArgNode node1, ParamNode node2, boolean allowsFieldFlow, Configuration config ) { flowIntoCallNodeCand1(call, node1, node2, allowsFieldFlow, config) and Stage2::revFlow(node2, pragma[only_bind_into](config)) and @@ -1276,7 +1267,7 @@ private module LocalFlowBigStep { config.isSource(node) or jumpStep(_, node, config) or additionalJumpStep(_, node, config) or - node instanceof ParameterNodeExt or + node instanceof ParamNode or node instanceof OutNodeExt or store(_, _, node, _) or read(_, _, node) or @@ -1586,10 +1577,10 @@ private module Stage3 { pragma[nomagic] private predicate fwdFlowIn( - DataFlowCall call, ParameterNodeExt p, Cc outercc, Cc innercc, ApOption argAp, Ap ap, + DataFlowCall call, ParamNode p, Cc outercc, Cc innercc, ApOption argAp, Ap ap, Configuration config ) { - exists(ArgumentNodeExt arg, boolean allowsFieldFlow | + exists(ArgNode arg, boolean allowsFieldFlow | fwdFlow(arg, outercc, argAp, ap, config) and flowIntoCall(call, arg, p, allowsFieldFlow, config) and innercc = getCallContextCall(call, getNodeEnclosingCallable(p), outercc) @@ -1634,7 +1625,7 @@ private module Stage3 { private predicate fwdFlowIsEntered( DataFlowCall call, Cc cc, ApOption argAp, Ap ap, Configuration config ) { - exists(ParameterNodeExt p | + exists(ParamNode p | fwdFlowIn(call, p, cc, _, argAp, ap, config) and PrevStage::parameterMayFlowThrough(p, _, unbindApa(getApprox(ap)), config) ) @@ -1775,10 +1766,9 @@ private module Stage3 { pragma[nomagic] private predicate revFlowIn( - DataFlowCall call, ArgumentNodeExt arg, boolean toReturn, ApOption returnAp, Ap ap, - Configuration config + DataFlowCall call, ArgNode arg, boolean toReturn, ApOption returnAp, Ap ap, Configuration config ) { - exists(ParameterNodeExt p, boolean allowsFieldFlow | + exists(ParamNode p, boolean allowsFieldFlow | revFlow(p, toReturn, returnAp, ap, config) and flowIntoCall(call, arg, p, allowsFieldFlow, config) | @@ -1788,7 +1778,7 @@ private module Stage3 { pragma[nomagic] private predicate revFlowInToReturn( - DataFlowCall call, ArgumentNodeExt arg, Ap returnAp, Ap ap, Configuration config + DataFlowCall call, ArgNode arg, Ap returnAp, Ap ap, Configuration config ) { revFlowIn(call, arg, true, apSome(returnAp), ap, config) } @@ -1841,15 +1831,13 @@ private module Stage3 { pragma[noinline] private predicate parameterFlow( - ParameterNodeExt p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config + ParamNode p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config ) { revFlow(p, true, apSome(ap0), ap, config) and c = getNodeEnclosingCallable(p) } - predicate parameterMayFlowThrough( - ParameterNodeExt p, DataFlowCallable c, Ap ap, Configuration config - ) { + predicate parameterMayFlowThrough(ParamNode p, DataFlowCallable c, Ap ap, Configuration config) { exists(ReturnNodeExt ret, Ap ap0, ReturnKindExt kind, int pos | parameterFlow(p, ap, ap0, c, config) and c = getNodeEnclosingCallable(ret) and @@ -2160,8 +2148,7 @@ private module Stage4 { pragma[nomagic] private predicate flowIntoCall( - DataFlowCall call, ArgumentNodeExt node1, ParameterNodeExt node2, boolean allowsFieldFlow, - Configuration config + DataFlowCall call, ArgNode node1, ParamNode node2, boolean allowsFieldFlow, Configuration config ) { flowIntoCallNodeCand2(call, node1, node2, allowsFieldFlow, config) and PrevStage::revFlow(node2, _, _, _, pragma[only_bind_into](config)) and @@ -2305,10 +2292,10 @@ private module Stage4 { pragma[nomagic] private predicate fwdFlowIn( - DataFlowCall call, ParameterNodeExt p, Cc outercc, Cc innercc, ApOption argAp, Ap ap, + DataFlowCall call, ParamNode p, Cc outercc, Cc innercc, ApOption argAp, Ap ap, Configuration config ) { - exists(ArgumentNodeExt arg, boolean allowsFieldFlow | + exists(ArgNode arg, boolean allowsFieldFlow | fwdFlow(arg, outercc, argAp, ap, config) and flowIntoCall(call, arg, p, allowsFieldFlow, config) and innercc = getCallContextCall(call, getNodeEnclosingCallable(p), outercc) @@ -2353,7 +2340,7 @@ private module Stage4 { private predicate fwdFlowIsEntered( DataFlowCall call, Cc cc, ApOption argAp, Ap ap, Configuration config ) { - exists(ParameterNodeExt p | + exists(ParamNode p | fwdFlowIn(call, p, cc, _, argAp, ap, config) and PrevStage::parameterMayFlowThrough(p, _, unbindApa(getApprox(ap)), config) ) @@ -2494,10 +2481,9 @@ private module Stage4 { pragma[nomagic] private predicate revFlowIn( - DataFlowCall call, ArgumentNodeExt arg, boolean toReturn, ApOption returnAp, Ap ap, - Configuration config + DataFlowCall call, ArgNode arg, boolean toReturn, ApOption returnAp, Ap ap, Configuration config ) { - exists(ParameterNodeExt p, boolean allowsFieldFlow | + exists(ParamNode p, boolean allowsFieldFlow | revFlow(p, toReturn, returnAp, ap, config) and flowIntoCall(call, arg, p, allowsFieldFlow, config) | @@ -2507,7 +2493,7 @@ private module Stage4 { pragma[nomagic] private predicate revFlowInToReturn( - DataFlowCall call, ArgumentNodeExt arg, Ap returnAp, Ap ap, Configuration config + DataFlowCall call, ArgNode arg, Ap returnAp, Ap ap, Configuration config ) { revFlowIn(call, arg, true, apSome(returnAp), ap, config) } @@ -2560,15 +2546,13 @@ private module Stage4 { pragma[noinline] private predicate parameterFlow( - ParameterNodeExt p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config + ParamNode p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config ) { revFlow(p, true, apSome(ap0), ap, config) and c = getNodeEnclosingCallable(p) } - predicate parameterMayFlowThrough( - ParameterNodeExt p, DataFlowCallable c, Ap ap, Configuration config - ) { + predicate parameterMayFlowThrough(ParamNode p, DataFlowCallable c, Ap ap, Configuration config) { exists(ReturnNodeExt ret, Ap ap0, ReturnKindExt kind, int pos | parameterFlow(p, ap, ap0, c, config) and c = getNodeEnclosingCallable(ret) and @@ -2613,7 +2597,7 @@ private predicate nodeMayUseSummary(Node n, AccessPathApprox apa, Configuration private newtype TSummaryCtx = TSummaryCtxNone() or - TSummaryCtxSome(ParameterNodeExt p, AccessPath ap) { + TSummaryCtxSome(ParamNode p, AccessPath ap) { Stage4::parameterMayFlowThrough(p, _, ap.getApprox(), _) } @@ -2634,7 +2618,7 @@ private class SummaryCtxNone extends SummaryCtx, TSummaryCtxNone { /** A summary context from which a flow summary can be generated. */ private class SummaryCtxSome extends SummaryCtx, TSummaryCtxSome { - private ParameterNodeExt p; + private ParamNode p; private AccessPath ap; SummaryCtxSome() { this = TSummaryCtxSome(p, ap) } @@ -3242,7 +3226,7 @@ pragma[noinline] private predicate pathIntoArg( PathNodeMid mid, int i, CallContext cc, DataFlowCall call, AccessPath ap, AccessPathApprox apa ) { - exists(ArgumentNodeExt arg | + exists(ArgNode arg | arg = mid.getNode() and cc = mid.getCallContext() and arg.argumentOf(call, i) and @@ -3255,7 +3239,7 @@ pragma[noinline] private predicate parameterCand( DataFlowCallable callable, int i, AccessPathApprox apa, Configuration config ) { - exists(ParameterNodeExt p | + exists(ParamNode p | Stage4::revFlow(p, _, _, apa, config) and p.isParameterOf(callable, i) ) @@ -3279,7 +3263,7 @@ private predicate pathIntoCallable0( * respectively. */ private predicate pathIntoCallable( - PathNodeMid mid, ParameterNodeExt p, CallContext outercc, CallContextCall innercc, SummaryCtx sc, + PathNodeMid mid, ParamNode p, CallContext outercc, CallContextCall innercc, SummaryCtx sc, DataFlowCall call ) { exists(int i, DataFlowCallable callable, AccessPath ap | @@ -3575,7 +3559,7 @@ private module FlowExploration { private newtype TSummaryCtx1 = TSummaryCtx1None() or - TSummaryCtx1Param(ParameterNodeExt p) + TSummaryCtx1Param(ParamNode p) private newtype TSummaryCtx2 = TSummaryCtx2None() or @@ -3931,7 +3915,7 @@ private module FlowExploration { PartialPathNodeFwd mid, int i, CallContext cc, DataFlowCall call, PartialAccessPath ap, Configuration config ) { - exists(ArgumentNodeExt arg | + exists(ArgNode arg | arg = mid.getNode() and cc = mid.getCallContext() and arg.argumentOf(call, i) and @@ -3950,7 +3934,7 @@ private module FlowExploration { } private predicate partialPathIntoCallable( - PartialPathNodeFwd mid, ParameterNodeExt p, CallContext outercc, CallContextCall innercc, + PartialPathNodeFwd mid, ParamNode p, CallContext outercc, CallContextCall innercc, TSummaryCtx1 sc1, TSummaryCtx2 sc2, DataFlowCall call, PartialAccessPath ap, Configuration config ) { @@ -3987,7 +3971,7 @@ private module FlowExploration { DataFlowCall call, PartialPathNodeFwd mid, ReturnKindExt kind, CallContext cc, PartialAccessPath ap, Configuration config ) { - exists(ParameterNodeExt p, CallContext innercc, TSummaryCtx1 sc1, TSummaryCtx2 sc2 | + exists(ParamNode p, CallContext innercc, TSummaryCtx1 sc1, TSummaryCtx2 sc2 | partialPathIntoCallable(mid, p, cc, innercc, sc1, sc2, call, _, config) and paramFlowsThroughInPartialPath(kind, innercc, sc1, sc2, ap, config) ) @@ -4044,7 +4028,7 @@ private module FlowExploration { apConsRev(ap, c, ap0, config) ) or - exists(ParameterNodeExt p | + exists(ParamNode p | mid.getNode() = p and viableParamArg(_, p, node) and sc1 = mid.getSummaryCtx1() and @@ -4122,7 +4106,7 @@ private module FlowExploration { int pos, TRevSummaryCtx1Some sc1, TRevSummaryCtx2Some sc2, RevPartialAccessPath ap, Configuration config ) { - exists(PartialPathNodeRev mid, ParameterNodeExt p | + exists(PartialPathNodeRev mid, ParamNode p | mid.getNode() = p and p.isParameterOf(_, pos) and sc1 = mid.getSummaryCtx1() and @@ -4145,7 +4129,7 @@ private module FlowExploration { pragma[nomagic] private predicate revPartialPathThroughCallable( - PartialPathNodeRev mid, ArgumentNodeExt node, RevPartialAccessPath ap, Configuration config + PartialPathNodeRev mid, ArgNode node, RevPartialAccessPath ap, Configuration config ) { exists(DataFlowCall call, int pos | revPartialPathThroughCallable0(call, mid, pos, ap, config) and 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 cf83530bcbd..058d66b1496 100644 --- a/csharp/ql/src/semmle/code/csharp/dataflow/internal/DataFlowImpl3.qll +++ b/csharp/ql/src/semmle/code/csharp/dataflow/internal/DataFlowImpl3.qll @@ -385,7 +385,7 @@ private module Stage1 { */ pragma[nomagic] private predicate fwdFlowIsEntered(DataFlowCall call, Cc cc, Configuration config) { - exists(ArgumentNodeExt arg | + exists(ArgNode arg | fwdFlow(arg, cc, config) and viableParamArg(call, _, arg) ) @@ -512,24 +512,22 @@ private module Stage1 { pragma[nomagic] predicate viableParamArgNodeCandFwd1( - DataFlowCall call, ParameterNodeExt p, ArgumentNodeExt arg, Configuration config + DataFlowCall call, ParamNode p, ArgNode arg, Configuration config ) { viableParamArg(call, p, arg) and fwdFlow(arg, config) } pragma[nomagic] - private predicate revFlowIn( - DataFlowCall call, ArgumentNodeExt arg, boolean toReturn, Configuration config - ) { - exists(ParameterNodeExt p | + private predicate revFlowIn(DataFlowCall call, ArgNode arg, boolean toReturn, Configuration config) { + exists(ParamNode p | revFlow(p, toReturn, config) and viableParamArgNodeCandFwd1(call, p, arg, config) ) } pragma[nomagic] - private predicate revFlowInToReturn(DataFlowCall call, ArgumentNodeExt arg, Configuration config) { + private predicate revFlowInToReturn(DataFlowCall call, ArgNode arg, Configuration config) { revFlowIn(call, arg, true, config) } @@ -594,9 +592,7 @@ private module Stage1 { * Holds if flow may enter through `p` and reach a return node making `p` a * candidate for the origin of a summary. */ - predicate parameterMayFlowThrough( - ParameterNodeExt p, DataFlowCallable c, Ap ap, Configuration config - ) { + predicate parameterMayFlowThrough(ParamNode p, DataFlowCallable c, Ap ap, Configuration config) { exists(ReturnKindExt kind | throughFlowNodeCand(p, config) and returnFlowCallableNodeCand(c, kind, config) and @@ -662,7 +658,7 @@ private predicate flowOutOfCallNodeCand1( pragma[nomagic] private predicate viableParamArgNodeCand1( - DataFlowCall call, ParameterNodeExt p, ArgumentNodeExt arg, Configuration config + DataFlowCall call, ParamNode p, ArgNode arg, Configuration config ) { Stage1::viableParamArgNodeCandFwd1(call, p, arg, config) and Stage1::revFlow(arg, config) @@ -674,7 +670,7 @@ private predicate viableParamArgNodeCand1( */ pragma[nomagic] private predicate flowIntoCallNodeCand1( - DataFlowCall call, ArgumentNodeExt arg, ParameterNodeExt p, Configuration config + DataFlowCall call, ArgNode arg, ParamNode p, Configuration config ) { viableParamArgNodeCand1(call, p, arg, config) and Stage1::revFlow(p, config) and @@ -734,8 +730,7 @@ private predicate flowOutOfCallNodeCand1( */ pragma[nomagic] private predicate flowIntoCallNodeCand1( - DataFlowCall call, ArgumentNodeExt arg, ParameterNodeExt p, boolean allowsFieldFlow, - Configuration config + DataFlowCall call, ArgNode arg, ParamNode p, boolean allowsFieldFlow, Configuration config ) { flowIntoCallNodeCand1(call, arg, p, config) and exists(int b, int j | @@ -943,10 +938,10 @@ private module Stage2 { pragma[nomagic] private predicate fwdFlowIn( - DataFlowCall call, ParameterNodeExt p, Cc outercc, Cc innercc, ApOption argAp, Ap ap, + DataFlowCall call, ParamNode p, Cc outercc, Cc innercc, ApOption argAp, Ap ap, Configuration config ) { - exists(ArgumentNodeExt arg, boolean allowsFieldFlow | + exists(ArgNode arg, boolean allowsFieldFlow | fwdFlow(arg, outercc, argAp, ap, config) and flowIntoCall(call, arg, p, allowsFieldFlow, config) and innercc = getCallContextCall(call, getNodeEnclosingCallable(p), outercc) @@ -991,7 +986,7 @@ private module Stage2 { private predicate fwdFlowIsEntered( DataFlowCall call, Cc cc, ApOption argAp, Ap ap, Configuration config ) { - exists(ParameterNodeExt p | + exists(ParamNode p | fwdFlowIn(call, p, cc, _, argAp, ap, config) and PrevStage::parameterMayFlowThrough(p, _, getApprox(ap), config) ) @@ -1132,10 +1127,9 @@ private module Stage2 { pragma[nomagic] private predicate revFlowIn( - DataFlowCall call, ArgumentNodeExt arg, boolean toReturn, ApOption returnAp, Ap ap, - Configuration config + DataFlowCall call, ArgNode arg, boolean toReturn, ApOption returnAp, Ap ap, Configuration config ) { - exists(ParameterNodeExt p, boolean allowsFieldFlow | + exists(ParamNode p, boolean allowsFieldFlow | revFlow(p, toReturn, returnAp, ap, config) and flowIntoCall(call, arg, p, allowsFieldFlow, config) | @@ -1145,7 +1139,7 @@ private module Stage2 { pragma[nomagic] private predicate revFlowInToReturn( - DataFlowCall call, ArgumentNodeExt arg, Ap returnAp, Ap ap, Configuration config + DataFlowCall call, ArgNode arg, Ap returnAp, Ap ap, Configuration config ) { revFlowIn(call, arg, true, apSome(returnAp), ap, config) } @@ -1198,15 +1192,13 @@ private module Stage2 { pragma[noinline] private predicate parameterFlow( - ParameterNodeExt p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config + ParamNode p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config ) { revFlow(p, true, apSome(ap0), ap, config) and c = getNodeEnclosingCallable(p) } - predicate parameterMayFlowThrough( - ParameterNodeExt p, DataFlowCallable c, Ap ap, Configuration config - ) { + predicate parameterMayFlowThrough(ParamNode p, DataFlowCallable c, Ap ap, Configuration config) { exists(ReturnNodeExt ret, Ap ap0, ReturnKindExt kind, int pos | parameterFlow(p, ap, ap0, c, config) and c = getNodeEnclosingCallable(ret) and @@ -1246,8 +1238,7 @@ private predicate flowOutOfCallNodeCand2( pragma[nomagic] private predicate flowIntoCallNodeCand2( - DataFlowCall call, ArgumentNodeExt node1, ParameterNodeExt node2, boolean allowsFieldFlow, - Configuration config + DataFlowCall call, ArgNode node1, ParamNode node2, boolean allowsFieldFlow, Configuration config ) { flowIntoCallNodeCand1(call, node1, node2, allowsFieldFlow, config) and Stage2::revFlow(node2, pragma[only_bind_into](config)) and @@ -1276,7 +1267,7 @@ private module LocalFlowBigStep { config.isSource(node) or jumpStep(_, node, config) or additionalJumpStep(_, node, config) or - node instanceof ParameterNodeExt or + node instanceof ParamNode or node instanceof OutNodeExt or store(_, _, node, _) or read(_, _, node) or @@ -1586,10 +1577,10 @@ private module Stage3 { pragma[nomagic] private predicate fwdFlowIn( - DataFlowCall call, ParameterNodeExt p, Cc outercc, Cc innercc, ApOption argAp, Ap ap, + DataFlowCall call, ParamNode p, Cc outercc, Cc innercc, ApOption argAp, Ap ap, Configuration config ) { - exists(ArgumentNodeExt arg, boolean allowsFieldFlow | + exists(ArgNode arg, boolean allowsFieldFlow | fwdFlow(arg, outercc, argAp, ap, config) and flowIntoCall(call, arg, p, allowsFieldFlow, config) and innercc = getCallContextCall(call, getNodeEnclosingCallable(p), outercc) @@ -1634,7 +1625,7 @@ private module Stage3 { private predicate fwdFlowIsEntered( DataFlowCall call, Cc cc, ApOption argAp, Ap ap, Configuration config ) { - exists(ParameterNodeExt p | + exists(ParamNode p | fwdFlowIn(call, p, cc, _, argAp, ap, config) and PrevStage::parameterMayFlowThrough(p, _, unbindApa(getApprox(ap)), config) ) @@ -1775,10 +1766,9 @@ private module Stage3 { pragma[nomagic] private predicate revFlowIn( - DataFlowCall call, ArgumentNodeExt arg, boolean toReturn, ApOption returnAp, Ap ap, - Configuration config + DataFlowCall call, ArgNode arg, boolean toReturn, ApOption returnAp, Ap ap, Configuration config ) { - exists(ParameterNodeExt p, boolean allowsFieldFlow | + exists(ParamNode p, boolean allowsFieldFlow | revFlow(p, toReturn, returnAp, ap, config) and flowIntoCall(call, arg, p, allowsFieldFlow, config) | @@ -1788,7 +1778,7 @@ private module Stage3 { pragma[nomagic] private predicate revFlowInToReturn( - DataFlowCall call, ArgumentNodeExt arg, Ap returnAp, Ap ap, Configuration config + DataFlowCall call, ArgNode arg, Ap returnAp, Ap ap, Configuration config ) { revFlowIn(call, arg, true, apSome(returnAp), ap, config) } @@ -1841,15 +1831,13 @@ private module Stage3 { pragma[noinline] private predicate parameterFlow( - ParameterNodeExt p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config + ParamNode p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config ) { revFlow(p, true, apSome(ap0), ap, config) and c = getNodeEnclosingCallable(p) } - predicate parameterMayFlowThrough( - ParameterNodeExt p, DataFlowCallable c, Ap ap, Configuration config - ) { + predicate parameterMayFlowThrough(ParamNode p, DataFlowCallable c, Ap ap, Configuration config) { exists(ReturnNodeExt ret, Ap ap0, ReturnKindExt kind, int pos | parameterFlow(p, ap, ap0, c, config) and c = getNodeEnclosingCallable(ret) and @@ -2160,8 +2148,7 @@ private module Stage4 { pragma[nomagic] private predicate flowIntoCall( - DataFlowCall call, ArgumentNodeExt node1, ParameterNodeExt node2, boolean allowsFieldFlow, - Configuration config + DataFlowCall call, ArgNode node1, ParamNode node2, boolean allowsFieldFlow, Configuration config ) { flowIntoCallNodeCand2(call, node1, node2, allowsFieldFlow, config) and PrevStage::revFlow(node2, _, _, _, pragma[only_bind_into](config)) and @@ -2305,10 +2292,10 @@ private module Stage4 { pragma[nomagic] private predicate fwdFlowIn( - DataFlowCall call, ParameterNodeExt p, Cc outercc, Cc innercc, ApOption argAp, Ap ap, + DataFlowCall call, ParamNode p, Cc outercc, Cc innercc, ApOption argAp, Ap ap, Configuration config ) { - exists(ArgumentNodeExt arg, boolean allowsFieldFlow | + exists(ArgNode arg, boolean allowsFieldFlow | fwdFlow(arg, outercc, argAp, ap, config) and flowIntoCall(call, arg, p, allowsFieldFlow, config) and innercc = getCallContextCall(call, getNodeEnclosingCallable(p), outercc) @@ -2353,7 +2340,7 @@ private module Stage4 { private predicate fwdFlowIsEntered( DataFlowCall call, Cc cc, ApOption argAp, Ap ap, Configuration config ) { - exists(ParameterNodeExt p | + exists(ParamNode p | fwdFlowIn(call, p, cc, _, argAp, ap, config) and PrevStage::parameterMayFlowThrough(p, _, unbindApa(getApprox(ap)), config) ) @@ -2494,10 +2481,9 @@ private module Stage4 { pragma[nomagic] private predicate revFlowIn( - DataFlowCall call, ArgumentNodeExt arg, boolean toReturn, ApOption returnAp, Ap ap, - Configuration config + DataFlowCall call, ArgNode arg, boolean toReturn, ApOption returnAp, Ap ap, Configuration config ) { - exists(ParameterNodeExt p, boolean allowsFieldFlow | + exists(ParamNode p, boolean allowsFieldFlow | revFlow(p, toReturn, returnAp, ap, config) and flowIntoCall(call, arg, p, allowsFieldFlow, config) | @@ -2507,7 +2493,7 @@ private module Stage4 { pragma[nomagic] private predicate revFlowInToReturn( - DataFlowCall call, ArgumentNodeExt arg, Ap returnAp, Ap ap, Configuration config + DataFlowCall call, ArgNode arg, Ap returnAp, Ap ap, Configuration config ) { revFlowIn(call, arg, true, apSome(returnAp), ap, config) } @@ -2560,15 +2546,13 @@ private module Stage4 { pragma[noinline] private predicate parameterFlow( - ParameterNodeExt p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config + ParamNode p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config ) { revFlow(p, true, apSome(ap0), ap, config) and c = getNodeEnclosingCallable(p) } - predicate parameterMayFlowThrough( - ParameterNodeExt p, DataFlowCallable c, Ap ap, Configuration config - ) { + predicate parameterMayFlowThrough(ParamNode p, DataFlowCallable c, Ap ap, Configuration config) { exists(ReturnNodeExt ret, Ap ap0, ReturnKindExt kind, int pos | parameterFlow(p, ap, ap0, c, config) and c = getNodeEnclosingCallable(ret) and @@ -2613,7 +2597,7 @@ private predicate nodeMayUseSummary(Node n, AccessPathApprox apa, Configuration private newtype TSummaryCtx = TSummaryCtxNone() or - TSummaryCtxSome(ParameterNodeExt p, AccessPath ap) { + TSummaryCtxSome(ParamNode p, AccessPath ap) { Stage4::parameterMayFlowThrough(p, _, ap.getApprox(), _) } @@ -2634,7 +2618,7 @@ private class SummaryCtxNone extends SummaryCtx, TSummaryCtxNone { /** A summary context from which a flow summary can be generated. */ private class SummaryCtxSome extends SummaryCtx, TSummaryCtxSome { - private ParameterNodeExt p; + private ParamNode p; private AccessPath ap; SummaryCtxSome() { this = TSummaryCtxSome(p, ap) } @@ -3242,7 +3226,7 @@ pragma[noinline] private predicate pathIntoArg( PathNodeMid mid, int i, CallContext cc, DataFlowCall call, AccessPath ap, AccessPathApprox apa ) { - exists(ArgumentNodeExt arg | + exists(ArgNode arg | arg = mid.getNode() and cc = mid.getCallContext() and arg.argumentOf(call, i) and @@ -3255,7 +3239,7 @@ pragma[noinline] private predicate parameterCand( DataFlowCallable callable, int i, AccessPathApprox apa, Configuration config ) { - exists(ParameterNodeExt p | + exists(ParamNode p | Stage4::revFlow(p, _, _, apa, config) and p.isParameterOf(callable, i) ) @@ -3279,7 +3263,7 @@ private predicate pathIntoCallable0( * respectively. */ private predicate pathIntoCallable( - PathNodeMid mid, ParameterNodeExt p, CallContext outercc, CallContextCall innercc, SummaryCtx sc, + PathNodeMid mid, ParamNode p, CallContext outercc, CallContextCall innercc, SummaryCtx sc, DataFlowCall call ) { exists(int i, DataFlowCallable callable, AccessPath ap | @@ -3575,7 +3559,7 @@ private module FlowExploration { private newtype TSummaryCtx1 = TSummaryCtx1None() or - TSummaryCtx1Param(ParameterNodeExt p) + TSummaryCtx1Param(ParamNode p) private newtype TSummaryCtx2 = TSummaryCtx2None() or @@ -3931,7 +3915,7 @@ private module FlowExploration { PartialPathNodeFwd mid, int i, CallContext cc, DataFlowCall call, PartialAccessPath ap, Configuration config ) { - exists(ArgumentNodeExt arg | + exists(ArgNode arg | arg = mid.getNode() and cc = mid.getCallContext() and arg.argumentOf(call, i) and @@ -3950,7 +3934,7 @@ private module FlowExploration { } private predicate partialPathIntoCallable( - PartialPathNodeFwd mid, ParameterNodeExt p, CallContext outercc, CallContextCall innercc, + PartialPathNodeFwd mid, ParamNode p, CallContext outercc, CallContextCall innercc, TSummaryCtx1 sc1, TSummaryCtx2 sc2, DataFlowCall call, PartialAccessPath ap, Configuration config ) { @@ -3987,7 +3971,7 @@ private module FlowExploration { DataFlowCall call, PartialPathNodeFwd mid, ReturnKindExt kind, CallContext cc, PartialAccessPath ap, Configuration config ) { - exists(ParameterNodeExt p, CallContext innercc, TSummaryCtx1 sc1, TSummaryCtx2 sc2 | + exists(ParamNode p, CallContext innercc, TSummaryCtx1 sc1, TSummaryCtx2 sc2 | partialPathIntoCallable(mid, p, cc, innercc, sc1, sc2, call, _, config) and paramFlowsThroughInPartialPath(kind, innercc, sc1, sc2, ap, config) ) @@ -4044,7 +4028,7 @@ private module FlowExploration { apConsRev(ap, c, ap0, config) ) or - exists(ParameterNodeExt p | + exists(ParamNode p | mid.getNode() = p and viableParamArg(_, p, node) and sc1 = mid.getSummaryCtx1() and @@ -4122,7 +4106,7 @@ private module FlowExploration { int pos, TRevSummaryCtx1Some sc1, TRevSummaryCtx2Some sc2, RevPartialAccessPath ap, Configuration config ) { - exists(PartialPathNodeRev mid, ParameterNodeExt p | + exists(PartialPathNodeRev mid, ParamNode p | mid.getNode() = p and p.isParameterOf(_, pos) and sc1 = mid.getSummaryCtx1() and @@ -4145,7 +4129,7 @@ private module FlowExploration { pragma[nomagic] private predicate revPartialPathThroughCallable( - PartialPathNodeRev mid, ArgumentNodeExt node, RevPartialAccessPath ap, Configuration config + PartialPathNodeRev mid, ArgNode node, RevPartialAccessPath ap, Configuration config ) { exists(DataFlowCall call, int pos | revPartialPathThroughCallable0(call, mid, pos, ap, config) and 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 cf83530bcbd..058d66b1496 100644 --- a/csharp/ql/src/semmle/code/csharp/dataflow/internal/DataFlowImpl4.qll +++ b/csharp/ql/src/semmle/code/csharp/dataflow/internal/DataFlowImpl4.qll @@ -385,7 +385,7 @@ private module Stage1 { */ pragma[nomagic] private predicate fwdFlowIsEntered(DataFlowCall call, Cc cc, Configuration config) { - exists(ArgumentNodeExt arg | + exists(ArgNode arg | fwdFlow(arg, cc, config) and viableParamArg(call, _, arg) ) @@ -512,24 +512,22 @@ private module Stage1 { pragma[nomagic] predicate viableParamArgNodeCandFwd1( - DataFlowCall call, ParameterNodeExt p, ArgumentNodeExt arg, Configuration config + DataFlowCall call, ParamNode p, ArgNode arg, Configuration config ) { viableParamArg(call, p, arg) and fwdFlow(arg, config) } pragma[nomagic] - private predicate revFlowIn( - DataFlowCall call, ArgumentNodeExt arg, boolean toReturn, Configuration config - ) { - exists(ParameterNodeExt p | + private predicate revFlowIn(DataFlowCall call, ArgNode arg, boolean toReturn, Configuration config) { + exists(ParamNode p | revFlow(p, toReturn, config) and viableParamArgNodeCandFwd1(call, p, arg, config) ) } pragma[nomagic] - private predicate revFlowInToReturn(DataFlowCall call, ArgumentNodeExt arg, Configuration config) { + private predicate revFlowInToReturn(DataFlowCall call, ArgNode arg, Configuration config) { revFlowIn(call, arg, true, config) } @@ -594,9 +592,7 @@ private module Stage1 { * Holds if flow may enter through `p` and reach a return node making `p` a * candidate for the origin of a summary. */ - predicate parameterMayFlowThrough( - ParameterNodeExt p, DataFlowCallable c, Ap ap, Configuration config - ) { + predicate parameterMayFlowThrough(ParamNode p, DataFlowCallable c, Ap ap, Configuration config) { exists(ReturnKindExt kind | throughFlowNodeCand(p, config) and returnFlowCallableNodeCand(c, kind, config) and @@ -662,7 +658,7 @@ private predicate flowOutOfCallNodeCand1( pragma[nomagic] private predicate viableParamArgNodeCand1( - DataFlowCall call, ParameterNodeExt p, ArgumentNodeExt arg, Configuration config + DataFlowCall call, ParamNode p, ArgNode arg, Configuration config ) { Stage1::viableParamArgNodeCandFwd1(call, p, arg, config) and Stage1::revFlow(arg, config) @@ -674,7 +670,7 @@ private predicate viableParamArgNodeCand1( */ pragma[nomagic] private predicate flowIntoCallNodeCand1( - DataFlowCall call, ArgumentNodeExt arg, ParameterNodeExt p, Configuration config + DataFlowCall call, ArgNode arg, ParamNode p, Configuration config ) { viableParamArgNodeCand1(call, p, arg, config) and Stage1::revFlow(p, config) and @@ -734,8 +730,7 @@ private predicate flowOutOfCallNodeCand1( */ pragma[nomagic] private predicate flowIntoCallNodeCand1( - DataFlowCall call, ArgumentNodeExt arg, ParameterNodeExt p, boolean allowsFieldFlow, - Configuration config + DataFlowCall call, ArgNode arg, ParamNode p, boolean allowsFieldFlow, Configuration config ) { flowIntoCallNodeCand1(call, arg, p, config) and exists(int b, int j | @@ -943,10 +938,10 @@ private module Stage2 { pragma[nomagic] private predicate fwdFlowIn( - DataFlowCall call, ParameterNodeExt p, Cc outercc, Cc innercc, ApOption argAp, Ap ap, + DataFlowCall call, ParamNode p, Cc outercc, Cc innercc, ApOption argAp, Ap ap, Configuration config ) { - exists(ArgumentNodeExt arg, boolean allowsFieldFlow | + exists(ArgNode arg, boolean allowsFieldFlow | fwdFlow(arg, outercc, argAp, ap, config) and flowIntoCall(call, arg, p, allowsFieldFlow, config) and innercc = getCallContextCall(call, getNodeEnclosingCallable(p), outercc) @@ -991,7 +986,7 @@ private module Stage2 { private predicate fwdFlowIsEntered( DataFlowCall call, Cc cc, ApOption argAp, Ap ap, Configuration config ) { - exists(ParameterNodeExt p | + exists(ParamNode p | fwdFlowIn(call, p, cc, _, argAp, ap, config) and PrevStage::parameterMayFlowThrough(p, _, getApprox(ap), config) ) @@ -1132,10 +1127,9 @@ private module Stage2 { pragma[nomagic] private predicate revFlowIn( - DataFlowCall call, ArgumentNodeExt arg, boolean toReturn, ApOption returnAp, Ap ap, - Configuration config + DataFlowCall call, ArgNode arg, boolean toReturn, ApOption returnAp, Ap ap, Configuration config ) { - exists(ParameterNodeExt p, boolean allowsFieldFlow | + exists(ParamNode p, boolean allowsFieldFlow | revFlow(p, toReturn, returnAp, ap, config) and flowIntoCall(call, arg, p, allowsFieldFlow, config) | @@ -1145,7 +1139,7 @@ private module Stage2 { pragma[nomagic] private predicate revFlowInToReturn( - DataFlowCall call, ArgumentNodeExt arg, Ap returnAp, Ap ap, Configuration config + DataFlowCall call, ArgNode arg, Ap returnAp, Ap ap, Configuration config ) { revFlowIn(call, arg, true, apSome(returnAp), ap, config) } @@ -1198,15 +1192,13 @@ private module Stage2 { pragma[noinline] private predicate parameterFlow( - ParameterNodeExt p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config + ParamNode p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config ) { revFlow(p, true, apSome(ap0), ap, config) and c = getNodeEnclosingCallable(p) } - predicate parameterMayFlowThrough( - ParameterNodeExt p, DataFlowCallable c, Ap ap, Configuration config - ) { + predicate parameterMayFlowThrough(ParamNode p, DataFlowCallable c, Ap ap, Configuration config) { exists(ReturnNodeExt ret, Ap ap0, ReturnKindExt kind, int pos | parameterFlow(p, ap, ap0, c, config) and c = getNodeEnclosingCallable(ret) and @@ -1246,8 +1238,7 @@ private predicate flowOutOfCallNodeCand2( pragma[nomagic] private predicate flowIntoCallNodeCand2( - DataFlowCall call, ArgumentNodeExt node1, ParameterNodeExt node2, boolean allowsFieldFlow, - Configuration config + DataFlowCall call, ArgNode node1, ParamNode node2, boolean allowsFieldFlow, Configuration config ) { flowIntoCallNodeCand1(call, node1, node2, allowsFieldFlow, config) and Stage2::revFlow(node2, pragma[only_bind_into](config)) and @@ -1276,7 +1267,7 @@ private module LocalFlowBigStep { config.isSource(node) or jumpStep(_, node, config) or additionalJumpStep(_, node, config) or - node instanceof ParameterNodeExt or + node instanceof ParamNode or node instanceof OutNodeExt or store(_, _, node, _) or read(_, _, node) or @@ -1586,10 +1577,10 @@ private module Stage3 { pragma[nomagic] private predicate fwdFlowIn( - DataFlowCall call, ParameterNodeExt p, Cc outercc, Cc innercc, ApOption argAp, Ap ap, + DataFlowCall call, ParamNode p, Cc outercc, Cc innercc, ApOption argAp, Ap ap, Configuration config ) { - exists(ArgumentNodeExt arg, boolean allowsFieldFlow | + exists(ArgNode arg, boolean allowsFieldFlow | fwdFlow(arg, outercc, argAp, ap, config) and flowIntoCall(call, arg, p, allowsFieldFlow, config) and innercc = getCallContextCall(call, getNodeEnclosingCallable(p), outercc) @@ -1634,7 +1625,7 @@ private module Stage3 { private predicate fwdFlowIsEntered( DataFlowCall call, Cc cc, ApOption argAp, Ap ap, Configuration config ) { - exists(ParameterNodeExt p | + exists(ParamNode p | fwdFlowIn(call, p, cc, _, argAp, ap, config) and PrevStage::parameterMayFlowThrough(p, _, unbindApa(getApprox(ap)), config) ) @@ -1775,10 +1766,9 @@ private module Stage3 { pragma[nomagic] private predicate revFlowIn( - DataFlowCall call, ArgumentNodeExt arg, boolean toReturn, ApOption returnAp, Ap ap, - Configuration config + DataFlowCall call, ArgNode arg, boolean toReturn, ApOption returnAp, Ap ap, Configuration config ) { - exists(ParameterNodeExt p, boolean allowsFieldFlow | + exists(ParamNode p, boolean allowsFieldFlow | revFlow(p, toReturn, returnAp, ap, config) and flowIntoCall(call, arg, p, allowsFieldFlow, config) | @@ -1788,7 +1778,7 @@ private module Stage3 { pragma[nomagic] private predicate revFlowInToReturn( - DataFlowCall call, ArgumentNodeExt arg, Ap returnAp, Ap ap, Configuration config + DataFlowCall call, ArgNode arg, Ap returnAp, Ap ap, Configuration config ) { revFlowIn(call, arg, true, apSome(returnAp), ap, config) } @@ -1841,15 +1831,13 @@ private module Stage3 { pragma[noinline] private predicate parameterFlow( - ParameterNodeExt p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config + ParamNode p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config ) { revFlow(p, true, apSome(ap0), ap, config) and c = getNodeEnclosingCallable(p) } - predicate parameterMayFlowThrough( - ParameterNodeExt p, DataFlowCallable c, Ap ap, Configuration config - ) { + predicate parameterMayFlowThrough(ParamNode p, DataFlowCallable c, Ap ap, Configuration config) { exists(ReturnNodeExt ret, Ap ap0, ReturnKindExt kind, int pos | parameterFlow(p, ap, ap0, c, config) and c = getNodeEnclosingCallable(ret) and @@ -2160,8 +2148,7 @@ private module Stage4 { pragma[nomagic] private predicate flowIntoCall( - DataFlowCall call, ArgumentNodeExt node1, ParameterNodeExt node2, boolean allowsFieldFlow, - Configuration config + DataFlowCall call, ArgNode node1, ParamNode node2, boolean allowsFieldFlow, Configuration config ) { flowIntoCallNodeCand2(call, node1, node2, allowsFieldFlow, config) and PrevStage::revFlow(node2, _, _, _, pragma[only_bind_into](config)) and @@ -2305,10 +2292,10 @@ private module Stage4 { pragma[nomagic] private predicate fwdFlowIn( - DataFlowCall call, ParameterNodeExt p, Cc outercc, Cc innercc, ApOption argAp, Ap ap, + DataFlowCall call, ParamNode p, Cc outercc, Cc innercc, ApOption argAp, Ap ap, Configuration config ) { - exists(ArgumentNodeExt arg, boolean allowsFieldFlow | + exists(ArgNode arg, boolean allowsFieldFlow | fwdFlow(arg, outercc, argAp, ap, config) and flowIntoCall(call, arg, p, allowsFieldFlow, config) and innercc = getCallContextCall(call, getNodeEnclosingCallable(p), outercc) @@ -2353,7 +2340,7 @@ private module Stage4 { private predicate fwdFlowIsEntered( DataFlowCall call, Cc cc, ApOption argAp, Ap ap, Configuration config ) { - exists(ParameterNodeExt p | + exists(ParamNode p | fwdFlowIn(call, p, cc, _, argAp, ap, config) and PrevStage::parameterMayFlowThrough(p, _, unbindApa(getApprox(ap)), config) ) @@ -2494,10 +2481,9 @@ private module Stage4 { pragma[nomagic] private predicate revFlowIn( - DataFlowCall call, ArgumentNodeExt arg, boolean toReturn, ApOption returnAp, Ap ap, - Configuration config + DataFlowCall call, ArgNode arg, boolean toReturn, ApOption returnAp, Ap ap, Configuration config ) { - exists(ParameterNodeExt p, boolean allowsFieldFlow | + exists(ParamNode p, boolean allowsFieldFlow | revFlow(p, toReturn, returnAp, ap, config) and flowIntoCall(call, arg, p, allowsFieldFlow, config) | @@ -2507,7 +2493,7 @@ private module Stage4 { pragma[nomagic] private predicate revFlowInToReturn( - DataFlowCall call, ArgumentNodeExt arg, Ap returnAp, Ap ap, Configuration config + DataFlowCall call, ArgNode arg, Ap returnAp, Ap ap, Configuration config ) { revFlowIn(call, arg, true, apSome(returnAp), ap, config) } @@ -2560,15 +2546,13 @@ private module Stage4 { pragma[noinline] private predicate parameterFlow( - ParameterNodeExt p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config + ParamNode p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config ) { revFlow(p, true, apSome(ap0), ap, config) and c = getNodeEnclosingCallable(p) } - predicate parameterMayFlowThrough( - ParameterNodeExt p, DataFlowCallable c, Ap ap, Configuration config - ) { + predicate parameterMayFlowThrough(ParamNode p, DataFlowCallable c, Ap ap, Configuration config) { exists(ReturnNodeExt ret, Ap ap0, ReturnKindExt kind, int pos | parameterFlow(p, ap, ap0, c, config) and c = getNodeEnclosingCallable(ret) and @@ -2613,7 +2597,7 @@ private predicate nodeMayUseSummary(Node n, AccessPathApprox apa, Configuration private newtype TSummaryCtx = TSummaryCtxNone() or - TSummaryCtxSome(ParameterNodeExt p, AccessPath ap) { + TSummaryCtxSome(ParamNode p, AccessPath ap) { Stage4::parameterMayFlowThrough(p, _, ap.getApprox(), _) } @@ -2634,7 +2618,7 @@ private class SummaryCtxNone extends SummaryCtx, TSummaryCtxNone { /** A summary context from which a flow summary can be generated. */ private class SummaryCtxSome extends SummaryCtx, TSummaryCtxSome { - private ParameterNodeExt p; + private ParamNode p; private AccessPath ap; SummaryCtxSome() { this = TSummaryCtxSome(p, ap) } @@ -3242,7 +3226,7 @@ pragma[noinline] private predicate pathIntoArg( PathNodeMid mid, int i, CallContext cc, DataFlowCall call, AccessPath ap, AccessPathApprox apa ) { - exists(ArgumentNodeExt arg | + exists(ArgNode arg | arg = mid.getNode() and cc = mid.getCallContext() and arg.argumentOf(call, i) and @@ -3255,7 +3239,7 @@ pragma[noinline] private predicate parameterCand( DataFlowCallable callable, int i, AccessPathApprox apa, Configuration config ) { - exists(ParameterNodeExt p | + exists(ParamNode p | Stage4::revFlow(p, _, _, apa, config) and p.isParameterOf(callable, i) ) @@ -3279,7 +3263,7 @@ private predicate pathIntoCallable0( * respectively. */ private predicate pathIntoCallable( - PathNodeMid mid, ParameterNodeExt p, CallContext outercc, CallContextCall innercc, SummaryCtx sc, + PathNodeMid mid, ParamNode p, CallContext outercc, CallContextCall innercc, SummaryCtx sc, DataFlowCall call ) { exists(int i, DataFlowCallable callable, AccessPath ap | @@ -3575,7 +3559,7 @@ private module FlowExploration { private newtype TSummaryCtx1 = TSummaryCtx1None() or - TSummaryCtx1Param(ParameterNodeExt p) + TSummaryCtx1Param(ParamNode p) private newtype TSummaryCtx2 = TSummaryCtx2None() or @@ -3931,7 +3915,7 @@ private module FlowExploration { PartialPathNodeFwd mid, int i, CallContext cc, DataFlowCall call, PartialAccessPath ap, Configuration config ) { - exists(ArgumentNodeExt arg | + exists(ArgNode arg | arg = mid.getNode() and cc = mid.getCallContext() and arg.argumentOf(call, i) and @@ -3950,7 +3934,7 @@ private module FlowExploration { } private predicate partialPathIntoCallable( - PartialPathNodeFwd mid, ParameterNodeExt p, CallContext outercc, CallContextCall innercc, + PartialPathNodeFwd mid, ParamNode p, CallContext outercc, CallContextCall innercc, TSummaryCtx1 sc1, TSummaryCtx2 sc2, DataFlowCall call, PartialAccessPath ap, Configuration config ) { @@ -3987,7 +3971,7 @@ private module FlowExploration { DataFlowCall call, PartialPathNodeFwd mid, ReturnKindExt kind, CallContext cc, PartialAccessPath ap, Configuration config ) { - exists(ParameterNodeExt p, CallContext innercc, TSummaryCtx1 sc1, TSummaryCtx2 sc2 | + exists(ParamNode p, CallContext innercc, TSummaryCtx1 sc1, TSummaryCtx2 sc2 | partialPathIntoCallable(mid, p, cc, innercc, sc1, sc2, call, _, config) and paramFlowsThroughInPartialPath(kind, innercc, sc1, sc2, ap, config) ) @@ -4044,7 +4028,7 @@ private module FlowExploration { apConsRev(ap, c, ap0, config) ) or - exists(ParameterNodeExt p | + exists(ParamNode p | mid.getNode() = p and viableParamArg(_, p, node) and sc1 = mid.getSummaryCtx1() and @@ -4122,7 +4106,7 @@ private module FlowExploration { int pos, TRevSummaryCtx1Some sc1, TRevSummaryCtx2Some sc2, RevPartialAccessPath ap, Configuration config ) { - exists(PartialPathNodeRev mid, ParameterNodeExt p | + exists(PartialPathNodeRev mid, ParamNode p | mid.getNode() = p and p.isParameterOf(_, pos) and sc1 = mid.getSummaryCtx1() and @@ -4145,7 +4129,7 @@ private module FlowExploration { pragma[nomagic] private predicate revPartialPathThroughCallable( - PartialPathNodeRev mid, ArgumentNodeExt node, RevPartialAccessPath ap, Configuration config + PartialPathNodeRev mid, ArgNode node, RevPartialAccessPath ap, Configuration config ) { exists(DataFlowCall call, int pos | revPartialPathThroughCallable0(call, mid, pos, ap, config) and 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 cf83530bcbd..058d66b1496 100644 --- a/csharp/ql/src/semmle/code/csharp/dataflow/internal/DataFlowImpl5.qll +++ b/csharp/ql/src/semmle/code/csharp/dataflow/internal/DataFlowImpl5.qll @@ -385,7 +385,7 @@ private module Stage1 { */ pragma[nomagic] private predicate fwdFlowIsEntered(DataFlowCall call, Cc cc, Configuration config) { - exists(ArgumentNodeExt arg | + exists(ArgNode arg | fwdFlow(arg, cc, config) and viableParamArg(call, _, arg) ) @@ -512,24 +512,22 @@ private module Stage1 { pragma[nomagic] predicate viableParamArgNodeCandFwd1( - DataFlowCall call, ParameterNodeExt p, ArgumentNodeExt arg, Configuration config + DataFlowCall call, ParamNode p, ArgNode arg, Configuration config ) { viableParamArg(call, p, arg) and fwdFlow(arg, config) } pragma[nomagic] - private predicate revFlowIn( - DataFlowCall call, ArgumentNodeExt arg, boolean toReturn, Configuration config - ) { - exists(ParameterNodeExt p | + private predicate revFlowIn(DataFlowCall call, ArgNode arg, boolean toReturn, Configuration config) { + exists(ParamNode p | revFlow(p, toReturn, config) and viableParamArgNodeCandFwd1(call, p, arg, config) ) } pragma[nomagic] - private predicate revFlowInToReturn(DataFlowCall call, ArgumentNodeExt arg, Configuration config) { + private predicate revFlowInToReturn(DataFlowCall call, ArgNode arg, Configuration config) { revFlowIn(call, arg, true, config) } @@ -594,9 +592,7 @@ private module Stage1 { * Holds if flow may enter through `p` and reach a return node making `p` a * candidate for the origin of a summary. */ - predicate parameterMayFlowThrough( - ParameterNodeExt p, DataFlowCallable c, Ap ap, Configuration config - ) { + predicate parameterMayFlowThrough(ParamNode p, DataFlowCallable c, Ap ap, Configuration config) { exists(ReturnKindExt kind | throughFlowNodeCand(p, config) and returnFlowCallableNodeCand(c, kind, config) and @@ -662,7 +658,7 @@ private predicate flowOutOfCallNodeCand1( pragma[nomagic] private predicate viableParamArgNodeCand1( - DataFlowCall call, ParameterNodeExt p, ArgumentNodeExt arg, Configuration config + DataFlowCall call, ParamNode p, ArgNode arg, Configuration config ) { Stage1::viableParamArgNodeCandFwd1(call, p, arg, config) and Stage1::revFlow(arg, config) @@ -674,7 +670,7 @@ private predicate viableParamArgNodeCand1( */ pragma[nomagic] private predicate flowIntoCallNodeCand1( - DataFlowCall call, ArgumentNodeExt arg, ParameterNodeExt p, Configuration config + DataFlowCall call, ArgNode arg, ParamNode p, Configuration config ) { viableParamArgNodeCand1(call, p, arg, config) and Stage1::revFlow(p, config) and @@ -734,8 +730,7 @@ private predicate flowOutOfCallNodeCand1( */ pragma[nomagic] private predicate flowIntoCallNodeCand1( - DataFlowCall call, ArgumentNodeExt arg, ParameterNodeExt p, boolean allowsFieldFlow, - Configuration config + DataFlowCall call, ArgNode arg, ParamNode p, boolean allowsFieldFlow, Configuration config ) { flowIntoCallNodeCand1(call, arg, p, config) and exists(int b, int j | @@ -943,10 +938,10 @@ private module Stage2 { pragma[nomagic] private predicate fwdFlowIn( - DataFlowCall call, ParameterNodeExt p, Cc outercc, Cc innercc, ApOption argAp, Ap ap, + DataFlowCall call, ParamNode p, Cc outercc, Cc innercc, ApOption argAp, Ap ap, Configuration config ) { - exists(ArgumentNodeExt arg, boolean allowsFieldFlow | + exists(ArgNode arg, boolean allowsFieldFlow | fwdFlow(arg, outercc, argAp, ap, config) and flowIntoCall(call, arg, p, allowsFieldFlow, config) and innercc = getCallContextCall(call, getNodeEnclosingCallable(p), outercc) @@ -991,7 +986,7 @@ private module Stage2 { private predicate fwdFlowIsEntered( DataFlowCall call, Cc cc, ApOption argAp, Ap ap, Configuration config ) { - exists(ParameterNodeExt p | + exists(ParamNode p | fwdFlowIn(call, p, cc, _, argAp, ap, config) and PrevStage::parameterMayFlowThrough(p, _, getApprox(ap), config) ) @@ -1132,10 +1127,9 @@ private module Stage2 { pragma[nomagic] private predicate revFlowIn( - DataFlowCall call, ArgumentNodeExt arg, boolean toReturn, ApOption returnAp, Ap ap, - Configuration config + DataFlowCall call, ArgNode arg, boolean toReturn, ApOption returnAp, Ap ap, Configuration config ) { - exists(ParameterNodeExt p, boolean allowsFieldFlow | + exists(ParamNode p, boolean allowsFieldFlow | revFlow(p, toReturn, returnAp, ap, config) and flowIntoCall(call, arg, p, allowsFieldFlow, config) | @@ -1145,7 +1139,7 @@ private module Stage2 { pragma[nomagic] private predicate revFlowInToReturn( - DataFlowCall call, ArgumentNodeExt arg, Ap returnAp, Ap ap, Configuration config + DataFlowCall call, ArgNode arg, Ap returnAp, Ap ap, Configuration config ) { revFlowIn(call, arg, true, apSome(returnAp), ap, config) } @@ -1198,15 +1192,13 @@ private module Stage2 { pragma[noinline] private predicate parameterFlow( - ParameterNodeExt p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config + ParamNode p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config ) { revFlow(p, true, apSome(ap0), ap, config) and c = getNodeEnclosingCallable(p) } - predicate parameterMayFlowThrough( - ParameterNodeExt p, DataFlowCallable c, Ap ap, Configuration config - ) { + predicate parameterMayFlowThrough(ParamNode p, DataFlowCallable c, Ap ap, Configuration config) { exists(ReturnNodeExt ret, Ap ap0, ReturnKindExt kind, int pos | parameterFlow(p, ap, ap0, c, config) and c = getNodeEnclosingCallable(ret) and @@ -1246,8 +1238,7 @@ private predicate flowOutOfCallNodeCand2( pragma[nomagic] private predicate flowIntoCallNodeCand2( - DataFlowCall call, ArgumentNodeExt node1, ParameterNodeExt node2, boolean allowsFieldFlow, - Configuration config + DataFlowCall call, ArgNode node1, ParamNode node2, boolean allowsFieldFlow, Configuration config ) { flowIntoCallNodeCand1(call, node1, node2, allowsFieldFlow, config) and Stage2::revFlow(node2, pragma[only_bind_into](config)) and @@ -1276,7 +1267,7 @@ private module LocalFlowBigStep { config.isSource(node) or jumpStep(_, node, config) or additionalJumpStep(_, node, config) or - node instanceof ParameterNodeExt or + node instanceof ParamNode or node instanceof OutNodeExt or store(_, _, node, _) or read(_, _, node) or @@ -1586,10 +1577,10 @@ private module Stage3 { pragma[nomagic] private predicate fwdFlowIn( - DataFlowCall call, ParameterNodeExt p, Cc outercc, Cc innercc, ApOption argAp, Ap ap, + DataFlowCall call, ParamNode p, Cc outercc, Cc innercc, ApOption argAp, Ap ap, Configuration config ) { - exists(ArgumentNodeExt arg, boolean allowsFieldFlow | + exists(ArgNode arg, boolean allowsFieldFlow | fwdFlow(arg, outercc, argAp, ap, config) and flowIntoCall(call, arg, p, allowsFieldFlow, config) and innercc = getCallContextCall(call, getNodeEnclosingCallable(p), outercc) @@ -1634,7 +1625,7 @@ private module Stage3 { private predicate fwdFlowIsEntered( DataFlowCall call, Cc cc, ApOption argAp, Ap ap, Configuration config ) { - exists(ParameterNodeExt p | + exists(ParamNode p | fwdFlowIn(call, p, cc, _, argAp, ap, config) and PrevStage::parameterMayFlowThrough(p, _, unbindApa(getApprox(ap)), config) ) @@ -1775,10 +1766,9 @@ private module Stage3 { pragma[nomagic] private predicate revFlowIn( - DataFlowCall call, ArgumentNodeExt arg, boolean toReturn, ApOption returnAp, Ap ap, - Configuration config + DataFlowCall call, ArgNode arg, boolean toReturn, ApOption returnAp, Ap ap, Configuration config ) { - exists(ParameterNodeExt p, boolean allowsFieldFlow | + exists(ParamNode p, boolean allowsFieldFlow | revFlow(p, toReturn, returnAp, ap, config) and flowIntoCall(call, arg, p, allowsFieldFlow, config) | @@ -1788,7 +1778,7 @@ private module Stage3 { pragma[nomagic] private predicate revFlowInToReturn( - DataFlowCall call, ArgumentNodeExt arg, Ap returnAp, Ap ap, Configuration config + DataFlowCall call, ArgNode arg, Ap returnAp, Ap ap, Configuration config ) { revFlowIn(call, arg, true, apSome(returnAp), ap, config) } @@ -1841,15 +1831,13 @@ private module Stage3 { pragma[noinline] private predicate parameterFlow( - ParameterNodeExt p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config + ParamNode p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config ) { revFlow(p, true, apSome(ap0), ap, config) and c = getNodeEnclosingCallable(p) } - predicate parameterMayFlowThrough( - ParameterNodeExt p, DataFlowCallable c, Ap ap, Configuration config - ) { + predicate parameterMayFlowThrough(ParamNode p, DataFlowCallable c, Ap ap, Configuration config) { exists(ReturnNodeExt ret, Ap ap0, ReturnKindExt kind, int pos | parameterFlow(p, ap, ap0, c, config) and c = getNodeEnclosingCallable(ret) and @@ -2160,8 +2148,7 @@ private module Stage4 { pragma[nomagic] private predicate flowIntoCall( - DataFlowCall call, ArgumentNodeExt node1, ParameterNodeExt node2, boolean allowsFieldFlow, - Configuration config + DataFlowCall call, ArgNode node1, ParamNode node2, boolean allowsFieldFlow, Configuration config ) { flowIntoCallNodeCand2(call, node1, node2, allowsFieldFlow, config) and PrevStage::revFlow(node2, _, _, _, pragma[only_bind_into](config)) and @@ -2305,10 +2292,10 @@ private module Stage4 { pragma[nomagic] private predicate fwdFlowIn( - DataFlowCall call, ParameterNodeExt p, Cc outercc, Cc innercc, ApOption argAp, Ap ap, + DataFlowCall call, ParamNode p, Cc outercc, Cc innercc, ApOption argAp, Ap ap, Configuration config ) { - exists(ArgumentNodeExt arg, boolean allowsFieldFlow | + exists(ArgNode arg, boolean allowsFieldFlow | fwdFlow(arg, outercc, argAp, ap, config) and flowIntoCall(call, arg, p, allowsFieldFlow, config) and innercc = getCallContextCall(call, getNodeEnclosingCallable(p), outercc) @@ -2353,7 +2340,7 @@ private module Stage4 { private predicate fwdFlowIsEntered( DataFlowCall call, Cc cc, ApOption argAp, Ap ap, Configuration config ) { - exists(ParameterNodeExt p | + exists(ParamNode p | fwdFlowIn(call, p, cc, _, argAp, ap, config) and PrevStage::parameterMayFlowThrough(p, _, unbindApa(getApprox(ap)), config) ) @@ -2494,10 +2481,9 @@ private module Stage4 { pragma[nomagic] private predicate revFlowIn( - DataFlowCall call, ArgumentNodeExt arg, boolean toReturn, ApOption returnAp, Ap ap, - Configuration config + DataFlowCall call, ArgNode arg, boolean toReturn, ApOption returnAp, Ap ap, Configuration config ) { - exists(ParameterNodeExt p, boolean allowsFieldFlow | + exists(ParamNode p, boolean allowsFieldFlow | revFlow(p, toReturn, returnAp, ap, config) and flowIntoCall(call, arg, p, allowsFieldFlow, config) | @@ -2507,7 +2493,7 @@ private module Stage4 { pragma[nomagic] private predicate revFlowInToReturn( - DataFlowCall call, ArgumentNodeExt arg, Ap returnAp, Ap ap, Configuration config + DataFlowCall call, ArgNode arg, Ap returnAp, Ap ap, Configuration config ) { revFlowIn(call, arg, true, apSome(returnAp), ap, config) } @@ -2560,15 +2546,13 @@ private module Stage4 { pragma[noinline] private predicate parameterFlow( - ParameterNodeExt p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config + ParamNode p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config ) { revFlow(p, true, apSome(ap0), ap, config) and c = getNodeEnclosingCallable(p) } - predicate parameterMayFlowThrough( - ParameterNodeExt p, DataFlowCallable c, Ap ap, Configuration config - ) { + predicate parameterMayFlowThrough(ParamNode p, DataFlowCallable c, Ap ap, Configuration config) { exists(ReturnNodeExt ret, Ap ap0, ReturnKindExt kind, int pos | parameterFlow(p, ap, ap0, c, config) and c = getNodeEnclosingCallable(ret) and @@ -2613,7 +2597,7 @@ private predicate nodeMayUseSummary(Node n, AccessPathApprox apa, Configuration private newtype TSummaryCtx = TSummaryCtxNone() or - TSummaryCtxSome(ParameterNodeExt p, AccessPath ap) { + TSummaryCtxSome(ParamNode p, AccessPath ap) { Stage4::parameterMayFlowThrough(p, _, ap.getApprox(), _) } @@ -2634,7 +2618,7 @@ private class SummaryCtxNone extends SummaryCtx, TSummaryCtxNone { /** A summary context from which a flow summary can be generated. */ private class SummaryCtxSome extends SummaryCtx, TSummaryCtxSome { - private ParameterNodeExt p; + private ParamNode p; private AccessPath ap; SummaryCtxSome() { this = TSummaryCtxSome(p, ap) } @@ -3242,7 +3226,7 @@ pragma[noinline] private predicate pathIntoArg( PathNodeMid mid, int i, CallContext cc, DataFlowCall call, AccessPath ap, AccessPathApprox apa ) { - exists(ArgumentNodeExt arg | + exists(ArgNode arg | arg = mid.getNode() and cc = mid.getCallContext() and arg.argumentOf(call, i) and @@ -3255,7 +3239,7 @@ pragma[noinline] private predicate parameterCand( DataFlowCallable callable, int i, AccessPathApprox apa, Configuration config ) { - exists(ParameterNodeExt p | + exists(ParamNode p | Stage4::revFlow(p, _, _, apa, config) and p.isParameterOf(callable, i) ) @@ -3279,7 +3263,7 @@ private predicate pathIntoCallable0( * respectively. */ private predicate pathIntoCallable( - PathNodeMid mid, ParameterNodeExt p, CallContext outercc, CallContextCall innercc, SummaryCtx sc, + PathNodeMid mid, ParamNode p, CallContext outercc, CallContextCall innercc, SummaryCtx sc, DataFlowCall call ) { exists(int i, DataFlowCallable callable, AccessPath ap | @@ -3575,7 +3559,7 @@ private module FlowExploration { private newtype TSummaryCtx1 = TSummaryCtx1None() or - TSummaryCtx1Param(ParameterNodeExt p) + TSummaryCtx1Param(ParamNode p) private newtype TSummaryCtx2 = TSummaryCtx2None() or @@ -3931,7 +3915,7 @@ private module FlowExploration { PartialPathNodeFwd mid, int i, CallContext cc, DataFlowCall call, PartialAccessPath ap, Configuration config ) { - exists(ArgumentNodeExt arg | + exists(ArgNode arg | arg = mid.getNode() and cc = mid.getCallContext() and arg.argumentOf(call, i) and @@ -3950,7 +3934,7 @@ private module FlowExploration { } private predicate partialPathIntoCallable( - PartialPathNodeFwd mid, ParameterNodeExt p, CallContext outercc, CallContextCall innercc, + PartialPathNodeFwd mid, ParamNode p, CallContext outercc, CallContextCall innercc, TSummaryCtx1 sc1, TSummaryCtx2 sc2, DataFlowCall call, PartialAccessPath ap, Configuration config ) { @@ -3987,7 +3971,7 @@ private module FlowExploration { DataFlowCall call, PartialPathNodeFwd mid, ReturnKindExt kind, CallContext cc, PartialAccessPath ap, Configuration config ) { - exists(ParameterNodeExt p, CallContext innercc, TSummaryCtx1 sc1, TSummaryCtx2 sc2 | + exists(ParamNode p, CallContext innercc, TSummaryCtx1 sc1, TSummaryCtx2 sc2 | partialPathIntoCallable(mid, p, cc, innercc, sc1, sc2, call, _, config) and paramFlowsThroughInPartialPath(kind, innercc, sc1, sc2, ap, config) ) @@ -4044,7 +4028,7 @@ private module FlowExploration { apConsRev(ap, c, ap0, config) ) or - exists(ParameterNodeExt p | + exists(ParamNode p | mid.getNode() = p and viableParamArg(_, p, node) and sc1 = mid.getSummaryCtx1() and @@ -4122,7 +4106,7 @@ private module FlowExploration { int pos, TRevSummaryCtx1Some sc1, TRevSummaryCtx2Some sc2, RevPartialAccessPath ap, Configuration config ) { - exists(PartialPathNodeRev mid, ParameterNodeExt p | + exists(PartialPathNodeRev mid, ParamNode p | mid.getNode() = p and p.isParameterOf(_, pos) and sc1 = mid.getSummaryCtx1() and @@ -4145,7 +4129,7 @@ private module FlowExploration { pragma[nomagic] private predicate revPartialPathThroughCallable( - PartialPathNodeRev mid, ArgumentNodeExt node, RevPartialAccessPath ap, Configuration config + PartialPathNodeRev mid, ArgNode node, RevPartialAccessPath ap, Configuration config ) { exists(DataFlowCall call, int pos | revPartialPathThroughCallable0(call, mid, pos, ap, config) and 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 b5ceabc605d..b8e08408911 100644 --- a/csharp/ql/src/semmle/code/csharp/dataflow/internal/DataFlowImplCommon.qll +++ b/csharp/ql/src/semmle/code/csharp/dataflow/internal/DataFlowImplCommon.qll @@ -35,24 +35,22 @@ predicate accessPathCostLimits(int apLimit, int tupleLimit) { * calls. For this reason, we cannot reuse the code from `DataFlowImpl.qll` directly. */ private module LambdaFlow { - private predicate viableParamNonLambda(DataFlowCall call, int i, ParameterNodeExt p) { + private predicate viableParamNonLambda(DataFlowCall call, int i, ParamNode p) { p.isParameterOf(viableCallable(call), i) } - private predicate viableParamLambda(DataFlowCall call, int i, ParameterNodeExt p) { + private predicate viableParamLambda(DataFlowCall call, int i, ParamNode p) { p.isParameterOf(viableCallableLambda(call, _), i) } - private predicate viableParamArgNonLambda( - DataFlowCall call, ParameterNodeExt p, ArgumentNodeExt arg - ) { + private predicate viableParamArgNonLambda(DataFlowCall call, ParamNode p, ArgNode arg) { exists(int i | viableParamNonLambda(call, i, p) and arg.argumentOf(call, i) ) } - private predicate viableParamArgLambda(DataFlowCall call, ParameterNodeExt p, ArgumentNodeExt arg) { + private predicate viableParamArgLambda(DataFlowCall call, ParamNode p, ArgNode arg) { exists(int i | viableParamLambda(call, i, p) and arg.argumentOf(call, i) @@ -120,7 +118,7 @@ private module LambdaFlow { boolean toJump, DataFlowCallOption lastCall ) { revLambdaFlow0(lambdaCall, kind, node, t, toReturn, toJump, lastCall) and - if castNode(node) or node instanceof ArgumentNodeExt or node instanceof ReturnNode + if castNode(node) or node instanceof ArgNode or node instanceof ReturnNode then compatibleTypes(t, getNodeDataFlowType(node)) else any() } @@ -178,7 +176,7 @@ private module LambdaFlow { ) or // flow into a callable - exists(ParameterNodeExt p, DataFlowCallOption lastCall0, DataFlowCall call | + exists(ParamNode p, DataFlowCallOption lastCall0, DataFlowCall call | revLambdaFlowIn(lambdaCall, kind, p, t, toJump, lastCall0) and ( if lastCall0 = TDataFlowCallNone() and toJump = false @@ -229,8 +227,8 @@ private module LambdaFlow { pragma[nomagic] predicate revLambdaFlowIn( - DataFlowCall lambdaCall, LambdaCallKind kind, ParameterNodeExt p, DataFlowType t, - boolean toJump, DataFlowCallOption lastCall + DataFlowCall lambdaCall, LambdaCallKind kind, ParamNode p, DataFlowType t, boolean toJump, + DataFlowCallOption lastCall ) { revLambdaFlow(lambdaCall, kind, p, t, false, toJump, lastCall) } @@ -276,7 +274,7 @@ private module Cached { predicate outNodeExt(Node n) { n instanceof OutNode or - n.(PostUpdateNode).getPreUpdateNode() instanceof ArgumentNodeExt + n.(PostUpdateNode).getPreUpdateNode() instanceof ArgNode } cached @@ -286,7 +284,7 @@ private module Cached { OutNodeExt getAnOutNodeExt(DataFlowCall call, ReturnKindExt k) { result = getAnOutNode(call, k.(ValueReturnKind).getKind()) or - exists(ArgumentNodeExt arg | + exists(ArgNode arg | result.(PostUpdateNode).getPreUpdateNode() = arg and arg.argumentOf(call, k.(ParamUpdateReturnKind).getPosition()) ) @@ -296,7 +294,7 @@ private module Cached { predicate returnNodeExt(Node n, ReturnKindExt k) { k = TValueReturn(n.(ReturnNode).getKind()) or - exists(ParameterNodeExt p, int pos | + exists(ParamNode p, int pos | parameterValueFlowsToPreUpdate(p, n) and p.isParameterOf(_, pos) and k = TParamUpdate(pos) @@ -309,7 +307,7 @@ private module Cached { cached predicate castingNode(Node n) { castNode(n) or - n instanceof ParameterNodeExt or + n instanceof ParamNode or n instanceof OutNodeExt or // For reads, `x.f`, we want to check that the tracked type after the read (which // is obtained by popping the head of the access path stack) is compatible with @@ -346,7 +344,7 @@ private module Cached { * The instance parameter is considered to have index `-1`. */ pragma[nomagic] - private predicate viableParam(DataFlowCall call, int i, ParameterNodeExt p) { + private predicate viableParam(DataFlowCall call, int i, ParamNode p) { p.isParameterOf(viableCallableExt(call), i) } @@ -355,7 +353,7 @@ private module Cached { * dispatch into account. */ cached - predicate viableParamArg(DataFlowCall call, ParameterNodeExt p, ArgumentNodeExt arg) { + predicate viableParamArg(DataFlowCall call, ParamNode p, ArgNode arg) { exists(int i | viableParam(call, i, p) and arg.argumentOf(call, i) and @@ -397,7 +395,7 @@ private module Cached { * `read` indicates whether it is contents of `p` that can flow to `node`. */ pragma[nomagic] - private predicate parameterValueFlowCand(ParameterNodeExt p, Node node, boolean read) { + private predicate parameterValueFlowCand(ParamNode p, Node node, boolean read) { p = node and read = false or @@ -415,27 +413,25 @@ private module Cached { ) or // flow through: no prior read - exists(ArgumentNodeExt arg | + exists(ArgNode arg | parameterValueFlowArgCand(p, arg, false) and argumentValueFlowsThroughCand(arg, node, read) ) or // flow through: no read inside method - exists(ArgumentNodeExt arg | + exists(ArgNode arg | parameterValueFlowArgCand(p, arg, read) and argumentValueFlowsThroughCand(arg, node, false) ) } pragma[nomagic] - private predicate parameterValueFlowArgCand( - ParameterNodeExt p, ArgumentNodeExt arg, boolean read - ) { + private predicate parameterValueFlowArgCand(ParamNode p, ArgNode arg, boolean read) { parameterValueFlowCand(p, arg, read) } pragma[nomagic] - predicate parameterValueFlowsToPreUpdateCand(ParameterNodeExt p, PostUpdateNode n) { + predicate parameterValueFlowsToPreUpdateCand(ParamNode p, PostUpdateNode n) { parameterValueFlowCand(p, n.getPreUpdateNode(), false) } @@ -447,7 +443,7 @@ private module Cached { * `read` indicates whether it is contents of `p` that can flow to the return * node. */ - predicate parameterValueFlowReturnCand(ParameterNodeExt p, ReturnKind kind, boolean read) { + predicate parameterValueFlowReturnCand(ParamNode p, ReturnKind kind, boolean read) { exists(ReturnNode ret | parameterValueFlowCand(p, ret, read) and kind = ret.getKind() @@ -456,9 +452,9 @@ private module Cached { pragma[nomagic] private predicate argumentValueFlowsThroughCand0( - DataFlowCall call, ArgumentNodeExt arg, ReturnKind kind, boolean read + DataFlowCall call, ArgNode arg, ReturnKind kind, boolean read ) { - exists(ParameterNodeExt param | viableParamArg(call, param, arg) | + exists(ParamNode param | viableParamArg(call, param, arg) | parameterValueFlowReturnCand(param, kind, read) ) } @@ -469,14 +465,14 @@ private module Cached { * * `read` indicates whether it is contents of `arg` that can flow to `out`. */ - predicate argumentValueFlowsThroughCand(ArgumentNodeExt arg, Node out, boolean read) { + predicate argumentValueFlowsThroughCand(ArgNode arg, Node out, boolean read) { exists(DataFlowCall call, ReturnKind kind | argumentValueFlowsThroughCand0(call, arg, kind, read) and out = getAnOutNode(call, kind) ) } - predicate cand(ParameterNodeExt p, Node n) { + predicate cand(ParamNode p, Node n) { parameterValueFlowCand(p, n, _) and ( parameterValueFlowReturnCand(p, _, _) @@ -503,7 +499,7 @@ private module Cached { * If a read step was taken, then `read` captures the `Content`, the * container type, and the content type. */ - predicate parameterValueFlow(ParameterNodeExt p, Node node, ReadStepTypesOption read) { + predicate parameterValueFlow(ParamNode p, Node node, ReadStepTypesOption read) { parameterValueFlow0(p, node, read) and if node instanceof CastingNode then @@ -517,7 +513,7 @@ private module Cached { } pragma[nomagic] - private predicate parameterValueFlow0(ParameterNodeExt p, Node node, ReadStepTypesOption read) { + private predicate parameterValueFlow0(ParamNode p, Node node, ReadStepTypesOption read) { p = node and Cand::cand(p, _) and read = TReadStepTypesNone() @@ -542,34 +538,32 @@ private module Cached { pragma[nomagic] private predicate parameterValueFlow0_0( - ReadStepTypesOption mustBeNone, ParameterNodeExt p, Node node, ReadStepTypesOption read + ReadStepTypesOption mustBeNone, ParamNode p, Node node, ReadStepTypesOption read ) { // flow through: no prior read - exists(ArgumentNodeExt arg | + exists(ArgNode arg | parameterValueFlowArg(p, arg, mustBeNone) and argumentValueFlowsThrough(arg, read, node) ) or // flow through: no read inside method - exists(ArgumentNodeExt arg | + exists(ArgNode arg | parameterValueFlowArg(p, arg, read) and argumentValueFlowsThrough(arg, mustBeNone, node) ) } pragma[nomagic] - private predicate parameterValueFlowArg( - ParameterNodeExt p, ArgumentNodeExt arg, ReadStepTypesOption read - ) { + private predicate parameterValueFlowArg(ParamNode p, ArgNode arg, ReadStepTypesOption read) { parameterValueFlow(p, arg, read) and Cand::argumentValueFlowsThroughCand(arg, _, _) } pragma[nomagic] private predicate argumentValueFlowsThrough0( - DataFlowCall call, ArgumentNodeExt arg, ReturnKind kind, ReadStepTypesOption read + DataFlowCall call, ArgNode arg, ReturnKind kind, ReadStepTypesOption read ) { - exists(ParameterNodeExt param | viableParamArg(call, param, arg) | + exists(ParamNode param | viableParamArg(call, param, arg) | parameterValueFlowReturn(param, kind, read) ) } @@ -583,7 +577,7 @@ private module Cached { * container type, and the content type. */ pragma[nomagic] - predicate argumentValueFlowsThrough(ArgumentNodeExt arg, ReadStepTypesOption read, Node out) { + predicate argumentValueFlowsThrough(ArgNode arg, ReadStepTypesOption read, Node out) { exists(DataFlowCall call, ReturnKind kind | argumentValueFlowsThrough0(call, arg, kind, read) and out = getAnOutNode(call, kind) @@ -603,7 +597,7 @@ private module Cached { * value-preserving steps and a single read step, not taking call * contexts into account, thus representing a getter-step. */ - predicate getterStep(ArgumentNodeExt arg, Content c, Node out) { + predicate getterStep(ArgNode arg, Content c, Node out) { argumentValueFlowsThrough(arg, TReadStepTypesSome(_, c, _), out) } @@ -616,7 +610,7 @@ private module Cached { * container type, and the content type. */ private predicate parameterValueFlowReturn( - ParameterNodeExt p, ReturnKind kind, ReadStepTypesOption read + ParamNode p, ReturnKind kind, ReadStepTypesOption read ) { exists(ReturnNode ret | parameterValueFlow(p, ret, read) and @@ -722,7 +716,7 @@ private module Cached { * Holds if `p` can flow to the pre-update node associated with post-update * node `n`, in the same callable, using only value-preserving steps. */ - private predicate parameterValueFlowsToPreUpdate(ParameterNodeExt p, PostUpdateNode n) { + private predicate parameterValueFlowsToPreUpdate(ParamNode p, PostUpdateNode n) { parameterValueFlow(p, n.getPreUpdateNode(), TReadStepTypesNone()) } @@ -778,8 +772,8 @@ private module Cached { // Does the language-specific simpleLocalFlowStep already model flow // from function input to output? fromPre = getAnOutNode(c, _) and - toPre.(ArgumentNodeExt).argumentOf(c, _) and - simpleLocalFlowStep(toPre.(ArgumentNodeExt), fromPre) + toPre.(ArgNode).argumentOf(c, _) and + simpleLocalFlowStep(toPre.(ArgNode), fromPre) ) or argumentValueFlowsThrough(toPre, TReadStepTypesNone(), fromPre) @@ -827,7 +821,7 @@ private module Cached { cached newtype TReturnKindExt = TValueReturn(ReturnKind kind) or - TParamUpdate(int pos) { exists(ParameterNodeExt p | p.isParameterOf(_, pos)) } + TParamUpdate(int pos) { exists(ParamNode p | p.isParameterOf(_, pos)) } cached newtype TBooleanOption = @@ -942,7 +936,7 @@ class CallContextSomeCall extends CallContextCall, TSomeCall { override string toString() { result = "CcSomeCall" } override predicate relevantFor(DataFlowCallable callable) { - exists(ParameterNodeExt p | getNodeEnclosingCallable(p) = callable) + exists(ParamNode p | getNodeEnclosingCallable(p) = callable) } override predicate matchesCall(DataFlowCall call) { any() } @@ -1005,8 +999,8 @@ LocalCallContext getLocalCallContext(CallContext ctx, DataFlowCallable callable) * The value of a parameter at function entry, viewed as a node in a data * flow graph. */ -class ParameterNodeExt extends Node { - ParameterNodeExt() { parameterNode(this, _, _) } +class ParamNode extends Node { + ParamNode() { parameterNode(this, _, _) } /** * Holds if this node is the parameter of callable `c` at the specified @@ -1016,8 +1010,8 @@ class ParameterNodeExt extends Node { } /** A data-flow node that represents a call argument. */ -class ArgumentNodeExt extends Node { - ArgumentNodeExt() { argumentNode(this, _, _) } +class ArgNode extends Node { + ArgNode() { argumentNode(this, _, _) } /** Holds if this argument occurs at the given position in the given call. */ final predicate argumentOf(DataFlowCall call, int pos) { argumentNode(this, call, pos) } 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 cf83530bcbd..058d66b1496 100644 --- a/java/ql/src/semmle/code/java/dataflow/internal/DataFlowImpl.qll +++ b/java/ql/src/semmle/code/java/dataflow/internal/DataFlowImpl.qll @@ -385,7 +385,7 @@ private module Stage1 { */ pragma[nomagic] private predicate fwdFlowIsEntered(DataFlowCall call, Cc cc, Configuration config) { - exists(ArgumentNodeExt arg | + exists(ArgNode arg | fwdFlow(arg, cc, config) and viableParamArg(call, _, arg) ) @@ -512,24 +512,22 @@ private module Stage1 { pragma[nomagic] predicate viableParamArgNodeCandFwd1( - DataFlowCall call, ParameterNodeExt p, ArgumentNodeExt arg, Configuration config + DataFlowCall call, ParamNode p, ArgNode arg, Configuration config ) { viableParamArg(call, p, arg) and fwdFlow(arg, config) } pragma[nomagic] - private predicate revFlowIn( - DataFlowCall call, ArgumentNodeExt arg, boolean toReturn, Configuration config - ) { - exists(ParameterNodeExt p | + private predicate revFlowIn(DataFlowCall call, ArgNode arg, boolean toReturn, Configuration config) { + exists(ParamNode p | revFlow(p, toReturn, config) and viableParamArgNodeCandFwd1(call, p, arg, config) ) } pragma[nomagic] - private predicate revFlowInToReturn(DataFlowCall call, ArgumentNodeExt arg, Configuration config) { + private predicate revFlowInToReturn(DataFlowCall call, ArgNode arg, Configuration config) { revFlowIn(call, arg, true, config) } @@ -594,9 +592,7 @@ private module Stage1 { * Holds if flow may enter through `p` and reach a return node making `p` a * candidate for the origin of a summary. */ - predicate parameterMayFlowThrough( - ParameterNodeExt p, DataFlowCallable c, Ap ap, Configuration config - ) { + predicate parameterMayFlowThrough(ParamNode p, DataFlowCallable c, Ap ap, Configuration config) { exists(ReturnKindExt kind | throughFlowNodeCand(p, config) and returnFlowCallableNodeCand(c, kind, config) and @@ -662,7 +658,7 @@ private predicate flowOutOfCallNodeCand1( pragma[nomagic] private predicate viableParamArgNodeCand1( - DataFlowCall call, ParameterNodeExt p, ArgumentNodeExt arg, Configuration config + DataFlowCall call, ParamNode p, ArgNode arg, Configuration config ) { Stage1::viableParamArgNodeCandFwd1(call, p, arg, config) and Stage1::revFlow(arg, config) @@ -674,7 +670,7 @@ private predicate viableParamArgNodeCand1( */ pragma[nomagic] private predicate flowIntoCallNodeCand1( - DataFlowCall call, ArgumentNodeExt arg, ParameterNodeExt p, Configuration config + DataFlowCall call, ArgNode arg, ParamNode p, Configuration config ) { viableParamArgNodeCand1(call, p, arg, config) and Stage1::revFlow(p, config) and @@ -734,8 +730,7 @@ private predicate flowOutOfCallNodeCand1( */ pragma[nomagic] private predicate flowIntoCallNodeCand1( - DataFlowCall call, ArgumentNodeExt arg, ParameterNodeExt p, boolean allowsFieldFlow, - Configuration config + DataFlowCall call, ArgNode arg, ParamNode p, boolean allowsFieldFlow, Configuration config ) { flowIntoCallNodeCand1(call, arg, p, config) and exists(int b, int j | @@ -943,10 +938,10 @@ private module Stage2 { pragma[nomagic] private predicate fwdFlowIn( - DataFlowCall call, ParameterNodeExt p, Cc outercc, Cc innercc, ApOption argAp, Ap ap, + DataFlowCall call, ParamNode p, Cc outercc, Cc innercc, ApOption argAp, Ap ap, Configuration config ) { - exists(ArgumentNodeExt arg, boolean allowsFieldFlow | + exists(ArgNode arg, boolean allowsFieldFlow | fwdFlow(arg, outercc, argAp, ap, config) and flowIntoCall(call, arg, p, allowsFieldFlow, config) and innercc = getCallContextCall(call, getNodeEnclosingCallable(p), outercc) @@ -991,7 +986,7 @@ private module Stage2 { private predicate fwdFlowIsEntered( DataFlowCall call, Cc cc, ApOption argAp, Ap ap, Configuration config ) { - exists(ParameterNodeExt p | + exists(ParamNode p | fwdFlowIn(call, p, cc, _, argAp, ap, config) and PrevStage::parameterMayFlowThrough(p, _, getApprox(ap), config) ) @@ -1132,10 +1127,9 @@ private module Stage2 { pragma[nomagic] private predicate revFlowIn( - DataFlowCall call, ArgumentNodeExt arg, boolean toReturn, ApOption returnAp, Ap ap, - Configuration config + DataFlowCall call, ArgNode arg, boolean toReturn, ApOption returnAp, Ap ap, Configuration config ) { - exists(ParameterNodeExt p, boolean allowsFieldFlow | + exists(ParamNode p, boolean allowsFieldFlow | revFlow(p, toReturn, returnAp, ap, config) and flowIntoCall(call, arg, p, allowsFieldFlow, config) | @@ -1145,7 +1139,7 @@ private module Stage2 { pragma[nomagic] private predicate revFlowInToReturn( - DataFlowCall call, ArgumentNodeExt arg, Ap returnAp, Ap ap, Configuration config + DataFlowCall call, ArgNode arg, Ap returnAp, Ap ap, Configuration config ) { revFlowIn(call, arg, true, apSome(returnAp), ap, config) } @@ -1198,15 +1192,13 @@ private module Stage2 { pragma[noinline] private predicate parameterFlow( - ParameterNodeExt p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config + ParamNode p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config ) { revFlow(p, true, apSome(ap0), ap, config) and c = getNodeEnclosingCallable(p) } - predicate parameterMayFlowThrough( - ParameterNodeExt p, DataFlowCallable c, Ap ap, Configuration config - ) { + predicate parameterMayFlowThrough(ParamNode p, DataFlowCallable c, Ap ap, Configuration config) { exists(ReturnNodeExt ret, Ap ap0, ReturnKindExt kind, int pos | parameterFlow(p, ap, ap0, c, config) and c = getNodeEnclosingCallable(ret) and @@ -1246,8 +1238,7 @@ private predicate flowOutOfCallNodeCand2( pragma[nomagic] private predicate flowIntoCallNodeCand2( - DataFlowCall call, ArgumentNodeExt node1, ParameterNodeExt node2, boolean allowsFieldFlow, - Configuration config + DataFlowCall call, ArgNode node1, ParamNode node2, boolean allowsFieldFlow, Configuration config ) { flowIntoCallNodeCand1(call, node1, node2, allowsFieldFlow, config) and Stage2::revFlow(node2, pragma[only_bind_into](config)) and @@ -1276,7 +1267,7 @@ private module LocalFlowBigStep { config.isSource(node) or jumpStep(_, node, config) or additionalJumpStep(_, node, config) or - node instanceof ParameterNodeExt or + node instanceof ParamNode or node instanceof OutNodeExt or store(_, _, node, _) or read(_, _, node) or @@ -1586,10 +1577,10 @@ private module Stage3 { pragma[nomagic] private predicate fwdFlowIn( - DataFlowCall call, ParameterNodeExt p, Cc outercc, Cc innercc, ApOption argAp, Ap ap, + DataFlowCall call, ParamNode p, Cc outercc, Cc innercc, ApOption argAp, Ap ap, Configuration config ) { - exists(ArgumentNodeExt arg, boolean allowsFieldFlow | + exists(ArgNode arg, boolean allowsFieldFlow | fwdFlow(arg, outercc, argAp, ap, config) and flowIntoCall(call, arg, p, allowsFieldFlow, config) and innercc = getCallContextCall(call, getNodeEnclosingCallable(p), outercc) @@ -1634,7 +1625,7 @@ private module Stage3 { private predicate fwdFlowIsEntered( DataFlowCall call, Cc cc, ApOption argAp, Ap ap, Configuration config ) { - exists(ParameterNodeExt p | + exists(ParamNode p | fwdFlowIn(call, p, cc, _, argAp, ap, config) and PrevStage::parameterMayFlowThrough(p, _, unbindApa(getApprox(ap)), config) ) @@ -1775,10 +1766,9 @@ private module Stage3 { pragma[nomagic] private predicate revFlowIn( - DataFlowCall call, ArgumentNodeExt arg, boolean toReturn, ApOption returnAp, Ap ap, - Configuration config + DataFlowCall call, ArgNode arg, boolean toReturn, ApOption returnAp, Ap ap, Configuration config ) { - exists(ParameterNodeExt p, boolean allowsFieldFlow | + exists(ParamNode p, boolean allowsFieldFlow | revFlow(p, toReturn, returnAp, ap, config) and flowIntoCall(call, arg, p, allowsFieldFlow, config) | @@ -1788,7 +1778,7 @@ private module Stage3 { pragma[nomagic] private predicate revFlowInToReturn( - DataFlowCall call, ArgumentNodeExt arg, Ap returnAp, Ap ap, Configuration config + DataFlowCall call, ArgNode arg, Ap returnAp, Ap ap, Configuration config ) { revFlowIn(call, arg, true, apSome(returnAp), ap, config) } @@ -1841,15 +1831,13 @@ private module Stage3 { pragma[noinline] private predicate parameterFlow( - ParameterNodeExt p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config + ParamNode p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config ) { revFlow(p, true, apSome(ap0), ap, config) and c = getNodeEnclosingCallable(p) } - predicate parameterMayFlowThrough( - ParameterNodeExt p, DataFlowCallable c, Ap ap, Configuration config - ) { + predicate parameterMayFlowThrough(ParamNode p, DataFlowCallable c, Ap ap, Configuration config) { exists(ReturnNodeExt ret, Ap ap0, ReturnKindExt kind, int pos | parameterFlow(p, ap, ap0, c, config) and c = getNodeEnclosingCallable(ret) and @@ -2160,8 +2148,7 @@ private module Stage4 { pragma[nomagic] private predicate flowIntoCall( - DataFlowCall call, ArgumentNodeExt node1, ParameterNodeExt node2, boolean allowsFieldFlow, - Configuration config + DataFlowCall call, ArgNode node1, ParamNode node2, boolean allowsFieldFlow, Configuration config ) { flowIntoCallNodeCand2(call, node1, node2, allowsFieldFlow, config) and PrevStage::revFlow(node2, _, _, _, pragma[only_bind_into](config)) and @@ -2305,10 +2292,10 @@ private module Stage4 { pragma[nomagic] private predicate fwdFlowIn( - DataFlowCall call, ParameterNodeExt p, Cc outercc, Cc innercc, ApOption argAp, Ap ap, + DataFlowCall call, ParamNode p, Cc outercc, Cc innercc, ApOption argAp, Ap ap, Configuration config ) { - exists(ArgumentNodeExt arg, boolean allowsFieldFlow | + exists(ArgNode arg, boolean allowsFieldFlow | fwdFlow(arg, outercc, argAp, ap, config) and flowIntoCall(call, arg, p, allowsFieldFlow, config) and innercc = getCallContextCall(call, getNodeEnclosingCallable(p), outercc) @@ -2353,7 +2340,7 @@ private module Stage4 { private predicate fwdFlowIsEntered( DataFlowCall call, Cc cc, ApOption argAp, Ap ap, Configuration config ) { - exists(ParameterNodeExt p | + exists(ParamNode p | fwdFlowIn(call, p, cc, _, argAp, ap, config) and PrevStage::parameterMayFlowThrough(p, _, unbindApa(getApprox(ap)), config) ) @@ -2494,10 +2481,9 @@ private module Stage4 { pragma[nomagic] private predicate revFlowIn( - DataFlowCall call, ArgumentNodeExt arg, boolean toReturn, ApOption returnAp, Ap ap, - Configuration config + DataFlowCall call, ArgNode arg, boolean toReturn, ApOption returnAp, Ap ap, Configuration config ) { - exists(ParameterNodeExt p, boolean allowsFieldFlow | + exists(ParamNode p, boolean allowsFieldFlow | revFlow(p, toReturn, returnAp, ap, config) and flowIntoCall(call, arg, p, allowsFieldFlow, config) | @@ -2507,7 +2493,7 @@ private module Stage4 { pragma[nomagic] private predicate revFlowInToReturn( - DataFlowCall call, ArgumentNodeExt arg, Ap returnAp, Ap ap, Configuration config + DataFlowCall call, ArgNode arg, Ap returnAp, Ap ap, Configuration config ) { revFlowIn(call, arg, true, apSome(returnAp), ap, config) } @@ -2560,15 +2546,13 @@ private module Stage4 { pragma[noinline] private predicate parameterFlow( - ParameterNodeExt p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config + ParamNode p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config ) { revFlow(p, true, apSome(ap0), ap, config) and c = getNodeEnclosingCallable(p) } - predicate parameterMayFlowThrough( - ParameterNodeExt p, DataFlowCallable c, Ap ap, Configuration config - ) { + predicate parameterMayFlowThrough(ParamNode p, DataFlowCallable c, Ap ap, Configuration config) { exists(ReturnNodeExt ret, Ap ap0, ReturnKindExt kind, int pos | parameterFlow(p, ap, ap0, c, config) and c = getNodeEnclosingCallable(ret) and @@ -2613,7 +2597,7 @@ private predicate nodeMayUseSummary(Node n, AccessPathApprox apa, Configuration private newtype TSummaryCtx = TSummaryCtxNone() or - TSummaryCtxSome(ParameterNodeExt p, AccessPath ap) { + TSummaryCtxSome(ParamNode p, AccessPath ap) { Stage4::parameterMayFlowThrough(p, _, ap.getApprox(), _) } @@ -2634,7 +2618,7 @@ private class SummaryCtxNone extends SummaryCtx, TSummaryCtxNone { /** A summary context from which a flow summary can be generated. */ private class SummaryCtxSome extends SummaryCtx, TSummaryCtxSome { - private ParameterNodeExt p; + private ParamNode p; private AccessPath ap; SummaryCtxSome() { this = TSummaryCtxSome(p, ap) } @@ -3242,7 +3226,7 @@ pragma[noinline] private predicate pathIntoArg( PathNodeMid mid, int i, CallContext cc, DataFlowCall call, AccessPath ap, AccessPathApprox apa ) { - exists(ArgumentNodeExt arg | + exists(ArgNode arg | arg = mid.getNode() and cc = mid.getCallContext() and arg.argumentOf(call, i) and @@ -3255,7 +3239,7 @@ pragma[noinline] private predicate parameterCand( DataFlowCallable callable, int i, AccessPathApprox apa, Configuration config ) { - exists(ParameterNodeExt p | + exists(ParamNode p | Stage4::revFlow(p, _, _, apa, config) and p.isParameterOf(callable, i) ) @@ -3279,7 +3263,7 @@ private predicate pathIntoCallable0( * respectively. */ private predicate pathIntoCallable( - PathNodeMid mid, ParameterNodeExt p, CallContext outercc, CallContextCall innercc, SummaryCtx sc, + PathNodeMid mid, ParamNode p, CallContext outercc, CallContextCall innercc, SummaryCtx sc, DataFlowCall call ) { exists(int i, DataFlowCallable callable, AccessPath ap | @@ -3575,7 +3559,7 @@ private module FlowExploration { private newtype TSummaryCtx1 = TSummaryCtx1None() or - TSummaryCtx1Param(ParameterNodeExt p) + TSummaryCtx1Param(ParamNode p) private newtype TSummaryCtx2 = TSummaryCtx2None() or @@ -3931,7 +3915,7 @@ private module FlowExploration { PartialPathNodeFwd mid, int i, CallContext cc, DataFlowCall call, PartialAccessPath ap, Configuration config ) { - exists(ArgumentNodeExt arg | + exists(ArgNode arg | arg = mid.getNode() and cc = mid.getCallContext() and arg.argumentOf(call, i) and @@ -3950,7 +3934,7 @@ private module FlowExploration { } private predicate partialPathIntoCallable( - PartialPathNodeFwd mid, ParameterNodeExt p, CallContext outercc, CallContextCall innercc, + PartialPathNodeFwd mid, ParamNode p, CallContext outercc, CallContextCall innercc, TSummaryCtx1 sc1, TSummaryCtx2 sc2, DataFlowCall call, PartialAccessPath ap, Configuration config ) { @@ -3987,7 +3971,7 @@ private module FlowExploration { DataFlowCall call, PartialPathNodeFwd mid, ReturnKindExt kind, CallContext cc, PartialAccessPath ap, Configuration config ) { - exists(ParameterNodeExt p, CallContext innercc, TSummaryCtx1 sc1, TSummaryCtx2 sc2 | + exists(ParamNode p, CallContext innercc, TSummaryCtx1 sc1, TSummaryCtx2 sc2 | partialPathIntoCallable(mid, p, cc, innercc, sc1, sc2, call, _, config) and paramFlowsThroughInPartialPath(kind, innercc, sc1, sc2, ap, config) ) @@ -4044,7 +4028,7 @@ private module FlowExploration { apConsRev(ap, c, ap0, config) ) or - exists(ParameterNodeExt p | + exists(ParamNode p | mid.getNode() = p and viableParamArg(_, p, node) and sc1 = mid.getSummaryCtx1() and @@ -4122,7 +4106,7 @@ private module FlowExploration { int pos, TRevSummaryCtx1Some sc1, TRevSummaryCtx2Some sc2, RevPartialAccessPath ap, Configuration config ) { - exists(PartialPathNodeRev mid, ParameterNodeExt p | + exists(PartialPathNodeRev mid, ParamNode p | mid.getNode() = p and p.isParameterOf(_, pos) and sc1 = mid.getSummaryCtx1() and @@ -4145,7 +4129,7 @@ private module FlowExploration { pragma[nomagic] private predicate revPartialPathThroughCallable( - PartialPathNodeRev mid, ArgumentNodeExt node, RevPartialAccessPath ap, Configuration config + PartialPathNodeRev mid, ArgNode node, RevPartialAccessPath ap, Configuration config ) { exists(DataFlowCall call, int pos | revPartialPathThroughCallable0(call, mid, pos, ap, config) and 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 cf83530bcbd..058d66b1496 100644 --- a/java/ql/src/semmle/code/java/dataflow/internal/DataFlowImpl2.qll +++ b/java/ql/src/semmle/code/java/dataflow/internal/DataFlowImpl2.qll @@ -385,7 +385,7 @@ private module Stage1 { */ pragma[nomagic] private predicate fwdFlowIsEntered(DataFlowCall call, Cc cc, Configuration config) { - exists(ArgumentNodeExt arg | + exists(ArgNode arg | fwdFlow(arg, cc, config) and viableParamArg(call, _, arg) ) @@ -512,24 +512,22 @@ private module Stage1 { pragma[nomagic] predicate viableParamArgNodeCandFwd1( - DataFlowCall call, ParameterNodeExt p, ArgumentNodeExt arg, Configuration config + DataFlowCall call, ParamNode p, ArgNode arg, Configuration config ) { viableParamArg(call, p, arg) and fwdFlow(arg, config) } pragma[nomagic] - private predicate revFlowIn( - DataFlowCall call, ArgumentNodeExt arg, boolean toReturn, Configuration config - ) { - exists(ParameterNodeExt p | + private predicate revFlowIn(DataFlowCall call, ArgNode arg, boolean toReturn, Configuration config) { + exists(ParamNode p | revFlow(p, toReturn, config) and viableParamArgNodeCandFwd1(call, p, arg, config) ) } pragma[nomagic] - private predicate revFlowInToReturn(DataFlowCall call, ArgumentNodeExt arg, Configuration config) { + private predicate revFlowInToReturn(DataFlowCall call, ArgNode arg, Configuration config) { revFlowIn(call, arg, true, config) } @@ -594,9 +592,7 @@ private module Stage1 { * Holds if flow may enter through `p` and reach a return node making `p` a * candidate for the origin of a summary. */ - predicate parameterMayFlowThrough( - ParameterNodeExt p, DataFlowCallable c, Ap ap, Configuration config - ) { + predicate parameterMayFlowThrough(ParamNode p, DataFlowCallable c, Ap ap, Configuration config) { exists(ReturnKindExt kind | throughFlowNodeCand(p, config) and returnFlowCallableNodeCand(c, kind, config) and @@ -662,7 +658,7 @@ private predicate flowOutOfCallNodeCand1( pragma[nomagic] private predicate viableParamArgNodeCand1( - DataFlowCall call, ParameterNodeExt p, ArgumentNodeExt arg, Configuration config + DataFlowCall call, ParamNode p, ArgNode arg, Configuration config ) { Stage1::viableParamArgNodeCandFwd1(call, p, arg, config) and Stage1::revFlow(arg, config) @@ -674,7 +670,7 @@ private predicate viableParamArgNodeCand1( */ pragma[nomagic] private predicate flowIntoCallNodeCand1( - DataFlowCall call, ArgumentNodeExt arg, ParameterNodeExt p, Configuration config + DataFlowCall call, ArgNode arg, ParamNode p, Configuration config ) { viableParamArgNodeCand1(call, p, arg, config) and Stage1::revFlow(p, config) and @@ -734,8 +730,7 @@ private predicate flowOutOfCallNodeCand1( */ pragma[nomagic] private predicate flowIntoCallNodeCand1( - DataFlowCall call, ArgumentNodeExt arg, ParameterNodeExt p, boolean allowsFieldFlow, - Configuration config + DataFlowCall call, ArgNode arg, ParamNode p, boolean allowsFieldFlow, Configuration config ) { flowIntoCallNodeCand1(call, arg, p, config) and exists(int b, int j | @@ -943,10 +938,10 @@ private module Stage2 { pragma[nomagic] private predicate fwdFlowIn( - DataFlowCall call, ParameterNodeExt p, Cc outercc, Cc innercc, ApOption argAp, Ap ap, + DataFlowCall call, ParamNode p, Cc outercc, Cc innercc, ApOption argAp, Ap ap, Configuration config ) { - exists(ArgumentNodeExt arg, boolean allowsFieldFlow | + exists(ArgNode arg, boolean allowsFieldFlow | fwdFlow(arg, outercc, argAp, ap, config) and flowIntoCall(call, arg, p, allowsFieldFlow, config) and innercc = getCallContextCall(call, getNodeEnclosingCallable(p), outercc) @@ -991,7 +986,7 @@ private module Stage2 { private predicate fwdFlowIsEntered( DataFlowCall call, Cc cc, ApOption argAp, Ap ap, Configuration config ) { - exists(ParameterNodeExt p | + exists(ParamNode p | fwdFlowIn(call, p, cc, _, argAp, ap, config) and PrevStage::parameterMayFlowThrough(p, _, getApprox(ap), config) ) @@ -1132,10 +1127,9 @@ private module Stage2 { pragma[nomagic] private predicate revFlowIn( - DataFlowCall call, ArgumentNodeExt arg, boolean toReturn, ApOption returnAp, Ap ap, - Configuration config + DataFlowCall call, ArgNode arg, boolean toReturn, ApOption returnAp, Ap ap, Configuration config ) { - exists(ParameterNodeExt p, boolean allowsFieldFlow | + exists(ParamNode p, boolean allowsFieldFlow | revFlow(p, toReturn, returnAp, ap, config) and flowIntoCall(call, arg, p, allowsFieldFlow, config) | @@ -1145,7 +1139,7 @@ private module Stage2 { pragma[nomagic] private predicate revFlowInToReturn( - DataFlowCall call, ArgumentNodeExt arg, Ap returnAp, Ap ap, Configuration config + DataFlowCall call, ArgNode arg, Ap returnAp, Ap ap, Configuration config ) { revFlowIn(call, arg, true, apSome(returnAp), ap, config) } @@ -1198,15 +1192,13 @@ private module Stage2 { pragma[noinline] private predicate parameterFlow( - ParameterNodeExt p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config + ParamNode p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config ) { revFlow(p, true, apSome(ap0), ap, config) and c = getNodeEnclosingCallable(p) } - predicate parameterMayFlowThrough( - ParameterNodeExt p, DataFlowCallable c, Ap ap, Configuration config - ) { + predicate parameterMayFlowThrough(ParamNode p, DataFlowCallable c, Ap ap, Configuration config) { exists(ReturnNodeExt ret, Ap ap0, ReturnKindExt kind, int pos | parameterFlow(p, ap, ap0, c, config) and c = getNodeEnclosingCallable(ret) and @@ -1246,8 +1238,7 @@ private predicate flowOutOfCallNodeCand2( pragma[nomagic] private predicate flowIntoCallNodeCand2( - DataFlowCall call, ArgumentNodeExt node1, ParameterNodeExt node2, boolean allowsFieldFlow, - Configuration config + DataFlowCall call, ArgNode node1, ParamNode node2, boolean allowsFieldFlow, Configuration config ) { flowIntoCallNodeCand1(call, node1, node2, allowsFieldFlow, config) and Stage2::revFlow(node2, pragma[only_bind_into](config)) and @@ -1276,7 +1267,7 @@ private module LocalFlowBigStep { config.isSource(node) or jumpStep(_, node, config) or additionalJumpStep(_, node, config) or - node instanceof ParameterNodeExt or + node instanceof ParamNode or node instanceof OutNodeExt or store(_, _, node, _) or read(_, _, node) or @@ -1586,10 +1577,10 @@ private module Stage3 { pragma[nomagic] private predicate fwdFlowIn( - DataFlowCall call, ParameterNodeExt p, Cc outercc, Cc innercc, ApOption argAp, Ap ap, + DataFlowCall call, ParamNode p, Cc outercc, Cc innercc, ApOption argAp, Ap ap, Configuration config ) { - exists(ArgumentNodeExt arg, boolean allowsFieldFlow | + exists(ArgNode arg, boolean allowsFieldFlow | fwdFlow(arg, outercc, argAp, ap, config) and flowIntoCall(call, arg, p, allowsFieldFlow, config) and innercc = getCallContextCall(call, getNodeEnclosingCallable(p), outercc) @@ -1634,7 +1625,7 @@ private module Stage3 { private predicate fwdFlowIsEntered( DataFlowCall call, Cc cc, ApOption argAp, Ap ap, Configuration config ) { - exists(ParameterNodeExt p | + exists(ParamNode p | fwdFlowIn(call, p, cc, _, argAp, ap, config) and PrevStage::parameterMayFlowThrough(p, _, unbindApa(getApprox(ap)), config) ) @@ -1775,10 +1766,9 @@ private module Stage3 { pragma[nomagic] private predicate revFlowIn( - DataFlowCall call, ArgumentNodeExt arg, boolean toReturn, ApOption returnAp, Ap ap, - Configuration config + DataFlowCall call, ArgNode arg, boolean toReturn, ApOption returnAp, Ap ap, Configuration config ) { - exists(ParameterNodeExt p, boolean allowsFieldFlow | + exists(ParamNode p, boolean allowsFieldFlow | revFlow(p, toReturn, returnAp, ap, config) and flowIntoCall(call, arg, p, allowsFieldFlow, config) | @@ -1788,7 +1778,7 @@ private module Stage3 { pragma[nomagic] private predicate revFlowInToReturn( - DataFlowCall call, ArgumentNodeExt arg, Ap returnAp, Ap ap, Configuration config + DataFlowCall call, ArgNode arg, Ap returnAp, Ap ap, Configuration config ) { revFlowIn(call, arg, true, apSome(returnAp), ap, config) } @@ -1841,15 +1831,13 @@ private module Stage3 { pragma[noinline] private predicate parameterFlow( - ParameterNodeExt p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config + ParamNode p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config ) { revFlow(p, true, apSome(ap0), ap, config) and c = getNodeEnclosingCallable(p) } - predicate parameterMayFlowThrough( - ParameterNodeExt p, DataFlowCallable c, Ap ap, Configuration config - ) { + predicate parameterMayFlowThrough(ParamNode p, DataFlowCallable c, Ap ap, Configuration config) { exists(ReturnNodeExt ret, Ap ap0, ReturnKindExt kind, int pos | parameterFlow(p, ap, ap0, c, config) and c = getNodeEnclosingCallable(ret) and @@ -2160,8 +2148,7 @@ private module Stage4 { pragma[nomagic] private predicate flowIntoCall( - DataFlowCall call, ArgumentNodeExt node1, ParameterNodeExt node2, boolean allowsFieldFlow, - Configuration config + DataFlowCall call, ArgNode node1, ParamNode node2, boolean allowsFieldFlow, Configuration config ) { flowIntoCallNodeCand2(call, node1, node2, allowsFieldFlow, config) and PrevStage::revFlow(node2, _, _, _, pragma[only_bind_into](config)) and @@ -2305,10 +2292,10 @@ private module Stage4 { pragma[nomagic] private predicate fwdFlowIn( - DataFlowCall call, ParameterNodeExt p, Cc outercc, Cc innercc, ApOption argAp, Ap ap, + DataFlowCall call, ParamNode p, Cc outercc, Cc innercc, ApOption argAp, Ap ap, Configuration config ) { - exists(ArgumentNodeExt arg, boolean allowsFieldFlow | + exists(ArgNode arg, boolean allowsFieldFlow | fwdFlow(arg, outercc, argAp, ap, config) and flowIntoCall(call, arg, p, allowsFieldFlow, config) and innercc = getCallContextCall(call, getNodeEnclosingCallable(p), outercc) @@ -2353,7 +2340,7 @@ private module Stage4 { private predicate fwdFlowIsEntered( DataFlowCall call, Cc cc, ApOption argAp, Ap ap, Configuration config ) { - exists(ParameterNodeExt p | + exists(ParamNode p | fwdFlowIn(call, p, cc, _, argAp, ap, config) and PrevStage::parameterMayFlowThrough(p, _, unbindApa(getApprox(ap)), config) ) @@ -2494,10 +2481,9 @@ private module Stage4 { pragma[nomagic] private predicate revFlowIn( - DataFlowCall call, ArgumentNodeExt arg, boolean toReturn, ApOption returnAp, Ap ap, - Configuration config + DataFlowCall call, ArgNode arg, boolean toReturn, ApOption returnAp, Ap ap, Configuration config ) { - exists(ParameterNodeExt p, boolean allowsFieldFlow | + exists(ParamNode p, boolean allowsFieldFlow | revFlow(p, toReturn, returnAp, ap, config) and flowIntoCall(call, arg, p, allowsFieldFlow, config) | @@ -2507,7 +2493,7 @@ private module Stage4 { pragma[nomagic] private predicate revFlowInToReturn( - DataFlowCall call, ArgumentNodeExt arg, Ap returnAp, Ap ap, Configuration config + DataFlowCall call, ArgNode arg, Ap returnAp, Ap ap, Configuration config ) { revFlowIn(call, arg, true, apSome(returnAp), ap, config) } @@ -2560,15 +2546,13 @@ private module Stage4 { pragma[noinline] private predicate parameterFlow( - ParameterNodeExt p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config + ParamNode p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config ) { revFlow(p, true, apSome(ap0), ap, config) and c = getNodeEnclosingCallable(p) } - predicate parameterMayFlowThrough( - ParameterNodeExt p, DataFlowCallable c, Ap ap, Configuration config - ) { + predicate parameterMayFlowThrough(ParamNode p, DataFlowCallable c, Ap ap, Configuration config) { exists(ReturnNodeExt ret, Ap ap0, ReturnKindExt kind, int pos | parameterFlow(p, ap, ap0, c, config) and c = getNodeEnclosingCallable(ret) and @@ -2613,7 +2597,7 @@ private predicate nodeMayUseSummary(Node n, AccessPathApprox apa, Configuration private newtype TSummaryCtx = TSummaryCtxNone() or - TSummaryCtxSome(ParameterNodeExt p, AccessPath ap) { + TSummaryCtxSome(ParamNode p, AccessPath ap) { Stage4::parameterMayFlowThrough(p, _, ap.getApprox(), _) } @@ -2634,7 +2618,7 @@ private class SummaryCtxNone extends SummaryCtx, TSummaryCtxNone { /** A summary context from which a flow summary can be generated. */ private class SummaryCtxSome extends SummaryCtx, TSummaryCtxSome { - private ParameterNodeExt p; + private ParamNode p; private AccessPath ap; SummaryCtxSome() { this = TSummaryCtxSome(p, ap) } @@ -3242,7 +3226,7 @@ pragma[noinline] private predicate pathIntoArg( PathNodeMid mid, int i, CallContext cc, DataFlowCall call, AccessPath ap, AccessPathApprox apa ) { - exists(ArgumentNodeExt arg | + exists(ArgNode arg | arg = mid.getNode() and cc = mid.getCallContext() and arg.argumentOf(call, i) and @@ -3255,7 +3239,7 @@ pragma[noinline] private predicate parameterCand( DataFlowCallable callable, int i, AccessPathApprox apa, Configuration config ) { - exists(ParameterNodeExt p | + exists(ParamNode p | Stage4::revFlow(p, _, _, apa, config) and p.isParameterOf(callable, i) ) @@ -3279,7 +3263,7 @@ private predicate pathIntoCallable0( * respectively. */ private predicate pathIntoCallable( - PathNodeMid mid, ParameterNodeExt p, CallContext outercc, CallContextCall innercc, SummaryCtx sc, + PathNodeMid mid, ParamNode p, CallContext outercc, CallContextCall innercc, SummaryCtx sc, DataFlowCall call ) { exists(int i, DataFlowCallable callable, AccessPath ap | @@ -3575,7 +3559,7 @@ private module FlowExploration { private newtype TSummaryCtx1 = TSummaryCtx1None() or - TSummaryCtx1Param(ParameterNodeExt p) + TSummaryCtx1Param(ParamNode p) private newtype TSummaryCtx2 = TSummaryCtx2None() or @@ -3931,7 +3915,7 @@ private module FlowExploration { PartialPathNodeFwd mid, int i, CallContext cc, DataFlowCall call, PartialAccessPath ap, Configuration config ) { - exists(ArgumentNodeExt arg | + exists(ArgNode arg | arg = mid.getNode() and cc = mid.getCallContext() and arg.argumentOf(call, i) and @@ -3950,7 +3934,7 @@ private module FlowExploration { } private predicate partialPathIntoCallable( - PartialPathNodeFwd mid, ParameterNodeExt p, CallContext outercc, CallContextCall innercc, + PartialPathNodeFwd mid, ParamNode p, CallContext outercc, CallContextCall innercc, TSummaryCtx1 sc1, TSummaryCtx2 sc2, DataFlowCall call, PartialAccessPath ap, Configuration config ) { @@ -3987,7 +3971,7 @@ private module FlowExploration { DataFlowCall call, PartialPathNodeFwd mid, ReturnKindExt kind, CallContext cc, PartialAccessPath ap, Configuration config ) { - exists(ParameterNodeExt p, CallContext innercc, TSummaryCtx1 sc1, TSummaryCtx2 sc2 | + exists(ParamNode p, CallContext innercc, TSummaryCtx1 sc1, TSummaryCtx2 sc2 | partialPathIntoCallable(mid, p, cc, innercc, sc1, sc2, call, _, config) and paramFlowsThroughInPartialPath(kind, innercc, sc1, sc2, ap, config) ) @@ -4044,7 +4028,7 @@ private module FlowExploration { apConsRev(ap, c, ap0, config) ) or - exists(ParameterNodeExt p | + exists(ParamNode p | mid.getNode() = p and viableParamArg(_, p, node) and sc1 = mid.getSummaryCtx1() and @@ -4122,7 +4106,7 @@ private module FlowExploration { int pos, TRevSummaryCtx1Some sc1, TRevSummaryCtx2Some sc2, RevPartialAccessPath ap, Configuration config ) { - exists(PartialPathNodeRev mid, ParameterNodeExt p | + exists(PartialPathNodeRev mid, ParamNode p | mid.getNode() = p and p.isParameterOf(_, pos) and sc1 = mid.getSummaryCtx1() and @@ -4145,7 +4129,7 @@ private module FlowExploration { pragma[nomagic] private predicate revPartialPathThroughCallable( - PartialPathNodeRev mid, ArgumentNodeExt node, RevPartialAccessPath ap, Configuration config + PartialPathNodeRev mid, ArgNode node, RevPartialAccessPath ap, Configuration config ) { exists(DataFlowCall call, int pos | revPartialPathThroughCallable0(call, mid, pos, ap, config) and 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 cf83530bcbd..058d66b1496 100644 --- a/java/ql/src/semmle/code/java/dataflow/internal/DataFlowImpl3.qll +++ b/java/ql/src/semmle/code/java/dataflow/internal/DataFlowImpl3.qll @@ -385,7 +385,7 @@ private module Stage1 { */ pragma[nomagic] private predicate fwdFlowIsEntered(DataFlowCall call, Cc cc, Configuration config) { - exists(ArgumentNodeExt arg | + exists(ArgNode arg | fwdFlow(arg, cc, config) and viableParamArg(call, _, arg) ) @@ -512,24 +512,22 @@ private module Stage1 { pragma[nomagic] predicate viableParamArgNodeCandFwd1( - DataFlowCall call, ParameterNodeExt p, ArgumentNodeExt arg, Configuration config + DataFlowCall call, ParamNode p, ArgNode arg, Configuration config ) { viableParamArg(call, p, arg) and fwdFlow(arg, config) } pragma[nomagic] - private predicate revFlowIn( - DataFlowCall call, ArgumentNodeExt arg, boolean toReturn, Configuration config - ) { - exists(ParameterNodeExt p | + private predicate revFlowIn(DataFlowCall call, ArgNode arg, boolean toReturn, Configuration config) { + exists(ParamNode p | revFlow(p, toReturn, config) and viableParamArgNodeCandFwd1(call, p, arg, config) ) } pragma[nomagic] - private predicate revFlowInToReturn(DataFlowCall call, ArgumentNodeExt arg, Configuration config) { + private predicate revFlowInToReturn(DataFlowCall call, ArgNode arg, Configuration config) { revFlowIn(call, arg, true, config) } @@ -594,9 +592,7 @@ private module Stage1 { * Holds if flow may enter through `p` and reach a return node making `p` a * candidate for the origin of a summary. */ - predicate parameterMayFlowThrough( - ParameterNodeExt p, DataFlowCallable c, Ap ap, Configuration config - ) { + predicate parameterMayFlowThrough(ParamNode p, DataFlowCallable c, Ap ap, Configuration config) { exists(ReturnKindExt kind | throughFlowNodeCand(p, config) and returnFlowCallableNodeCand(c, kind, config) and @@ -662,7 +658,7 @@ private predicate flowOutOfCallNodeCand1( pragma[nomagic] private predicate viableParamArgNodeCand1( - DataFlowCall call, ParameterNodeExt p, ArgumentNodeExt arg, Configuration config + DataFlowCall call, ParamNode p, ArgNode arg, Configuration config ) { Stage1::viableParamArgNodeCandFwd1(call, p, arg, config) and Stage1::revFlow(arg, config) @@ -674,7 +670,7 @@ private predicate viableParamArgNodeCand1( */ pragma[nomagic] private predicate flowIntoCallNodeCand1( - DataFlowCall call, ArgumentNodeExt arg, ParameterNodeExt p, Configuration config + DataFlowCall call, ArgNode arg, ParamNode p, Configuration config ) { viableParamArgNodeCand1(call, p, arg, config) and Stage1::revFlow(p, config) and @@ -734,8 +730,7 @@ private predicate flowOutOfCallNodeCand1( */ pragma[nomagic] private predicate flowIntoCallNodeCand1( - DataFlowCall call, ArgumentNodeExt arg, ParameterNodeExt p, boolean allowsFieldFlow, - Configuration config + DataFlowCall call, ArgNode arg, ParamNode p, boolean allowsFieldFlow, Configuration config ) { flowIntoCallNodeCand1(call, arg, p, config) and exists(int b, int j | @@ -943,10 +938,10 @@ private module Stage2 { pragma[nomagic] private predicate fwdFlowIn( - DataFlowCall call, ParameterNodeExt p, Cc outercc, Cc innercc, ApOption argAp, Ap ap, + DataFlowCall call, ParamNode p, Cc outercc, Cc innercc, ApOption argAp, Ap ap, Configuration config ) { - exists(ArgumentNodeExt arg, boolean allowsFieldFlow | + exists(ArgNode arg, boolean allowsFieldFlow | fwdFlow(arg, outercc, argAp, ap, config) and flowIntoCall(call, arg, p, allowsFieldFlow, config) and innercc = getCallContextCall(call, getNodeEnclosingCallable(p), outercc) @@ -991,7 +986,7 @@ private module Stage2 { private predicate fwdFlowIsEntered( DataFlowCall call, Cc cc, ApOption argAp, Ap ap, Configuration config ) { - exists(ParameterNodeExt p | + exists(ParamNode p | fwdFlowIn(call, p, cc, _, argAp, ap, config) and PrevStage::parameterMayFlowThrough(p, _, getApprox(ap), config) ) @@ -1132,10 +1127,9 @@ private module Stage2 { pragma[nomagic] private predicate revFlowIn( - DataFlowCall call, ArgumentNodeExt arg, boolean toReturn, ApOption returnAp, Ap ap, - Configuration config + DataFlowCall call, ArgNode arg, boolean toReturn, ApOption returnAp, Ap ap, Configuration config ) { - exists(ParameterNodeExt p, boolean allowsFieldFlow | + exists(ParamNode p, boolean allowsFieldFlow | revFlow(p, toReturn, returnAp, ap, config) and flowIntoCall(call, arg, p, allowsFieldFlow, config) | @@ -1145,7 +1139,7 @@ private module Stage2 { pragma[nomagic] private predicate revFlowInToReturn( - DataFlowCall call, ArgumentNodeExt arg, Ap returnAp, Ap ap, Configuration config + DataFlowCall call, ArgNode arg, Ap returnAp, Ap ap, Configuration config ) { revFlowIn(call, arg, true, apSome(returnAp), ap, config) } @@ -1198,15 +1192,13 @@ private module Stage2 { pragma[noinline] private predicate parameterFlow( - ParameterNodeExt p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config + ParamNode p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config ) { revFlow(p, true, apSome(ap0), ap, config) and c = getNodeEnclosingCallable(p) } - predicate parameterMayFlowThrough( - ParameterNodeExt p, DataFlowCallable c, Ap ap, Configuration config - ) { + predicate parameterMayFlowThrough(ParamNode p, DataFlowCallable c, Ap ap, Configuration config) { exists(ReturnNodeExt ret, Ap ap0, ReturnKindExt kind, int pos | parameterFlow(p, ap, ap0, c, config) and c = getNodeEnclosingCallable(ret) and @@ -1246,8 +1238,7 @@ private predicate flowOutOfCallNodeCand2( pragma[nomagic] private predicate flowIntoCallNodeCand2( - DataFlowCall call, ArgumentNodeExt node1, ParameterNodeExt node2, boolean allowsFieldFlow, - Configuration config + DataFlowCall call, ArgNode node1, ParamNode node2, boolean allowsFieldFlow, Configuration config ) { flowIntoCallNodeCand1(call, node1, node2, allowsFieldFlow, config) and Stage2::revFlow(node2, pragma[only_bind_into](config)) and @@ -1276,7 +1267,7 @@ private module LocalFlowBigStep { config.isSource(node) or jumpStep(_, node, config) or additionalJumpStep(_, node, config) or - node instanceof ParameterNodeExt or + node instanceof ParamNode or node instanceof OutNodeExt or store(_, _, node, _) or read(_, _, node) or @@ -1586,10 +1577,10 @@ private module Stage3 { pragma[nomagic] private predicate fwdFlowIn( - DataFlowCall call, ParameterNodeExt p, Cc outercc, Cc innercc, ApOption argAp, Ap ap, + DataFlowCall call, ParamNode p, Cc outercc, Cc innercc, ApOption argAp, Ap ap, Configuration config ) { - exists(ArgumentNodeExt arg, boolean allowsFieldFlow | + exists(ArgNode arg, boolean allowsFieldFlow | fwdFlow(arg, outercc, argAp, ap, config) and flowIntoCall(call, arg, p, allowsFieldFlow, config) and innercc = getCallContextCall(call, getNodeEnclosingCallable(p), outercc) @@ -1634,7 +1625,7 @@ private module Stage3 { private predicate fwdFlowIsEntered( DataFlowCall call, Cc cc, ApOption argAp, Ap ap, Configuration config ) { - exists(ParameterNodeExt p | + exists(ParamNode p | fwdFlowIn(call, p, cc, _, argAp, ap, config) and PrevStage::parameterMayFlowThrough(p, _, unbindApa(getApprox(ap)), config) ) @@ -1775,10 +1766,9 @@ private module Stage3 { pragma[nomagic] private predicate revFlowIn( - DataFlowCall call, ArgumentNodeExt arg, boolean toReturn, ApOption returnAp, Ap ap, - Configuration config + DataFlowCall call, ArgNode arg, boolean toReturn, ApOption returnAp, Ap ap, Configuration config ) { - exists(ParameterNodeExt p, boolean allowsFieldFlow | + exists(ParamNode p, boolean allowsFieldFlow | revFlow(p, toReturn, returnAp, ap, config) and flowIntoCall(call, arg, p, allowsFieldFlow, config) | @@ -1788,7 +1778,7 @@ private module Stage3 { pragma[nomagic] private predicate revFlowInToReturn( - DataFlowCall call, ArgumentNodeExt arg, Ap returnAp, Ap ap, Configuration config + DataFlowCall call, ArgNode arg, Ap returnAp, Ap ap, Configuration config ) { revFlowIn(call, arg, true, apSome(returnAp), ap, config) } @@ -1841,15 +1831,13 @@ private module Stage3 { pragma[noinline] private predicate parameterFlow( - ParameterNodeExt p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config + ParamNode p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config ) { revFlow(p, true, apSome(ap0), ap, config) and c = getNodeEnclosingCallable(p) } - predicate parameterMayFlowThrough( - ParameterNodeExt p, DataFlowCallable c, Ap ap, Configuration config - ) { + predicate parameterMayFlowThrough(ParamNode p, DataFlowCallable c, Ap ap, Configuration config) { exists(ReturnNodeExt ret, Ap ap0, ReturnKindExt kind, int pos | parameterFlow(p, ap, ap0, c, config) and c = getNodeEnclosingCallable(ret) and @@ -2160,8 +2148,7 @@ private module Stage4 { pragma[nomagic] private predicate flowIntoCall( - DataFlowCall call, ArgumentNodeExt node1, ParameterNodeExt node2, boolean allowsFieldFlow, - Configuration config + DataFlowCall call, ArgNode node1, ParamNode node2, boolean allowsFieldFlow, Configuration config ) { flowIntoCallNodeCand2(call, node1, node2, allowsFieldFlow, config) and PrevStage::revFlow(node2, _, _, _, pragma[only_bind_into](config)) and @@ -2305,10 +2292,10 @@ private module Stage4 { pragma[nomagic] private predicate fwdFlowIn( - DataFlowCall call, ParameterNodeExt p, Cc outercc, Cc innercc, ApOption argAp, Ap ap, + DataFlowCall call, ParamNode p, Cc outercc, Cc innercc, ApOption argAp, Ap ap, Configuration config ) { - exists(ArgumentNodeExt arg, boolean allowsFieldFlow | + exists(ArgNode arg, boolean allowsFieldFlow | fwdFlow(arg, outercc, argAp, ap, config) and flowIntoCall(call, arg, p, allowsFieldFlow, config) and innercc = getCallContextCall(call, getNodeEnclosingCallable(p), outercc) @@ -2353,7 +2340,7 @@ private module Stage4 { private predicate fwdFlowIsEntered( DataFlowCall call, Cc cc, ApOption argAp, Ap ap, Configuration config ) { - exists(ParameterNodeExt p | + exists(ParamNode p | fwdFlowIn(call, p, cc, _, argAp, ap, config) and PrevStage::parameterMayFlowThrough(p, _, unbindApa(getApprox(ap)), config) ) @@ -2494,10 +2481,9 @@ private module Stage4 { pragma[nomagic] private predicate revFlowIn( - DataFlowCall call, ArgumentNodeExt arg, boolean toReturn, ApOption returnAp, Ap ap, - Configuration config + DataFlowCall call, ArgNode arg, boolean toReturn, ApOption returnAp, Ap ap, Configuration config ) { - exists(ParameterNodeExt p, boolean allowsFieldFlow | + exists(ParamNode p, boolean allowsFieldFlow | revFlow(p, toReturn, returnAp, ap, config) and flowIntoCall(call, arg, p, allowsFieldFlow, config) | @@ -2507,7 +2493,7 @@ private module Stage4 { pragma[nomagic] private predicate revFlowInToReturn( - DataFlowCall call, ArgumentNodeExt arg, Ap returnAp, Ap ap, Configuration config + DataFlowCall call, ArgNode arg, Ap returnAp, Ap ap, Configuration config ) { revFlowIn(call, arg, true, apSome(returnAp), ap, config) } @@ -2560,15 +2546,13 @@ private module Stage4 { pragma[noinline] private predicate parameterFlow( - ParameterNodeExt p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config + ParamNode p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config ) { revFlow(p, true, apSome(ap0), ap, config) and c = getNodeEnclosingCallable(p) } - predicate parameterMayFlowThrough( - ParameterNodeExt p, DataFlowCallable c, Ap ap, Configuration config - ) { + predicate parameterMayFlowThrough(ParamNode p, DataFlowCallable c, Ap ap, Configuration config) { exists(ReturnNodeExt ret, Ap ap0, ReturnKindExt kind, int pos | parameterFlow(p, ap, ap0, c, config) and c = getNodeEnclosingCallable(ret) and @@ -2613,7 +2597,7 @@ private predicate nodeMayUseSummary(Node n, AccessPathApprox apa, Configuration private newtype TSummaryCtx = TSummaryCtxNone() or - TSummaryCtxSome(ParameterNodeExt p, AccessPath ap) { + TSummaryCtxSome(ParamNode p, AccessPath ap) { Stage4::parameterMayFlowThrough(p, _, ap.getApprox(), _) } @@ -2634,7 +2618,7 @@ private class SummaryCtxNone extends SummaryCtx, TSummaryCtxNone { /** A summary context from which a flow summary can be generated. */ private class SummaryCtxSome extends SummaryCtx, TSummaryCtxSome { - private ParameterNodeExt p; + private ParamNode p; private AccessPath ap; SummaryCtxSome() { this = TSummaryCtxSome(p, ap) } @@ -3242,7 +3226,7 @@ pragma[noinline] private predicate pathIntoArg( PathNodeMid mid, int i, CallContext cc, DataFlowCall call, AccessPath ap, AccessPathApprox apa ) { - exists(ArgumentNodeExt arg | + exists(ArgNode arg | arg = mid.getNode() and cc = mid.getCallContext() and arg.argumentOf(call, i) and @@ -3255,7 +3239,7 @@ pragma[noinline] private predicate parameterCand( DataFlowCallable callable, int i, AccessPathApprox apa, Configuration config ) { - exists(ParameterNodeExt p | + exists(ParamNode p | Stage4::revFlow(p, _, _, apa, config) and p.isParameterOf(callable, i) ) @@ -3279,7 +3263,7 @@ private predicate pathIntoCallable0( * respectively. */ private predicate pathIntoCallable( - PathNodeMid mid, ParameterNodeExt p, CallContext outercc, CallContextCall innercc, SummaryCtx sc, + PathNodeMid mid, ParamNode p, CallContext outercc, CallContextCall innercc, SummaryCtx sc, DataFlowCall call ) { exists(int i, DataFlowCallable callable, AccessPath ap | @@ -3575,7 +3559,7 @@ private module FlowExploration { private newtype TSummaryCtx1 = TSummaryCtx1None() or - TSummaryCtx1Param(ParameterNodeExt p) + TSummaryCtx1Param(ParamNode p) private newtype TSummaryCtx2 = TSummaryCtx2None() or @@ -3931,7 +3915,7 @@ private module FlowExploration { PartialPathNodeFwd mid, int i, CallContext cc, DataFlowCall call, PartialAccessPath ap, Configuration config ) { - exists(ArgumentNodeExt arg | + exists(ArgNode arg | arg = mid.getNode() and cc = mid.getCallContext() and arg.argumentOf(call, i) and @@ -3950,7 +3934,7 @@ private module FlowExploration { } private predicate partialPathIntoCallable( - PartialPathNodeFwd mid, ParameterNodeExt p, CallContext outercc, CallContextCall innercc, + PartialPathNodeFwd mid, ParamNode p, CallContext outercc, CallContextCall innercc, TSummaryCtx1 sc1, TSummaryCtx2 sc2, DataFlowCall call, PartialAccessPath ap, Configuration config ) { @@ -3987,7 +3971,7 @@ private module FlowExploration { DataFlowCall call, PartialPathNodeFwd mid, ReturnKindExt kind, CallContext cc, PartialAccessPath ap, Configuration config ) { - exists(ParameterNodeExt p, CallContext innercc, TSummaryCtx1 sc1, TSummaryCtx2 sc2 | + exists(ParamNode p, CallContext innercc, TSummaryCtx1 sc1, TSummaryCtx2 sc2 | partialPathIntoCallable(mid, p, cc, innercc, sc1, sc2, call, _, config) and paramFlowsThroughInPartialPath(kind, innercc, sc1, sc2, ap, config) ) @@ -4044,7 +4028,7 @@ private module FlowExploration { apConsRev(ap, c, ap0, config) ) or - exists(ParameterNodeExt p | + exists(ParamNode p | mid.getNode() = p and viableParamArg(_, p, node) and sc1 = mid.getSummaryCtx1() and @@ -4122,7 +4106,7 @@ private module FlowExploration { int pos, TRevSummaryCtx1Some sc1, TRevSummaryCtx2Some sc2, RevPartialAccessPath ap, Configuration config ) { - exists(PartialPathNodeRev mid, ParameterNodeExt p | + exists(PartialPathNodeRev mid, ParamNode p | mid.getNode() = p and p.isParameterOf(_, pos) and sc1 = mid.getSummaryCtx1() and @@ -4145,7 +4129,7 @@ private module FlowExploration { pragma[nomagic] private predicate revPartialPathThroughCallable( - PartialPathNodeRev mid, ArgumentNodeExt node, RevPartialAccessPath ap, Configuration config + PartialPathNodeRev mid, ArgNode node, RevPartialAccessPath ap, Configuration config ) { exists(DataFlowCall call, int pos | revPartialPathThroughCallable0(call, mid, pos, ap, config) and 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 cf83530bcbd..058d66b1496 100644 --- a/java/ql/src/semmle/code/java/dataflow/internal/DataFlowImpl4.qll +++ b/java/ql/src/semmle/code/java/dataflow/internal/DataFlowImpl4.qll @@ -385,7 +385,7 @@ private module Stage1 { */ pragma[nomagic] private predicate fwdFlowIsEntered(DataFlowCall call, Cc cc, Configuration config) { - exists(ArgumentNodeExt arg | + exists(ArgNode arg | fwdFlow(arg, cc, config) and viableParamArg(call, _, arg) ) @@ -512,24 +512,22 @@ private module Stage1 { pragma[nomagic] predicate viableParamArgNodeCandFwd1( - DataFlowCall call, ParameterNodeExt p, ArgumentNodeExt arg, Configuration config + DataFlowCall call, ParamNode p, ArgNode arg, Configuration config ) { viableParamArg(call, p, arg) and fwdFlow(arg, config) } pragma[nomagic] - private predicate revFlowIn( - DataFlowCall call, ArgumentNodeExt arg, boolean toReturn, Configuration config - ) { - exists(ParameterNodeExt p | + private predicate revFlowIn(DataFlowCall call, ArgNode arg, boolean toReturn, Configuration config) { + exists(ParamNode p | revFlow(p, toReturn, config) and viableParamArgNodeCandFwd1(call, p, arg, config) ) } pragma[nomagic] - private predicate revFlowInToReturn(DataFlowCall call, ArgumentNodeExt arg, Configuration config) { + private predicate revFlowInToReturn(DataFlowCall call, ArgNode arg, Configuration config) { revFlowIn(call, arg, true, config) } @@ -594,9 +592,7 @@ private module Stage1 { * Holds if flow may enter through `p` and reach a return node making `p` a * candidate for the origin of a summary. */ - predicate parameterMayFlowThrough( - ParameterNodeExt p, DataFlowCallable c, Ap ap, Configuration config - ) { + predicate parameterMayFlowThrough(ParamNode p, DataFlowCallable c, Ap ap, Configuration config) { exists(ReturnKindExt kind | throughFlowNodeCand(p, config) and returnFlowCallableNodeCand(c, kind, config) and @@ -662,7 +658,7 @@ private predicate flowOutOfCallNodeCand1( pragma[nomagic] private predicate viableParamArgNodeCand1( - DataFlowCall call, ParameterNodeExt p, ArgumentNodeExt arg, Configuration config + DataFlowCall call, ParamNode p, ArgNode arg, Configuration config ) { Stage1::viableParamArgNodeCandFwd1(call, p, arg, config) and Stage1::revFlow(arg, config) @@ -674,7 +670,7 @@ private predicate viableParamArgNodeCand1( */ pragma[nomagic] private predicate flowIntoCallNodeCand1( - DataFlowCall call, ArgumentNodeExt arg, ParameterNodeExt p, Configuration config + DataFlowCall call, ArgNode arg, ParamNode p, Configuration config ) { viableParamArgNodeCand1(call, p, arg, config) and Stage1::revFlow(p, config) and @@ -734,8 +730,7 @@ private predicate flowOutOfCallNodeCand1( */ pragma[nomagic] private predicate flowIntoCallNodeCand1( - DataFlowCall call, ArgumentNodeExt arg, ParameterNodeExt p, boolean allowsFieldFlow, - Configuration config + DataFlowCall call, ArgNode arg, ParamNode p, boolean allowsFieldFlow, Configuration config ) { flowIntoCallNodeCand1(call, arg, p, config) and exists(int b, int j | @@ -943,10 +938,10 @@ private module Stage2 { pragma[nomagic] private predicate fwdFlowIn( - DataFlowCall call, ParameterNodeExt p, Cc outercc, Cc innercc, ApOption argAp, Ap ap, + DataFlowCall call, ParamNode p, Cc outercc, Cc innercc, ApOption argAp, Ap ap, Configuration config ) { - exists(ArgumentNodeExt arg, boolean allowsFieldFlow | + exists(ArgNode arg, boolean allowsFieldFlow | fwdFlow(arg, outercc, argAp, ap, config) and flowIntoCall(call, arg, p, allowsFieldFlow, config) and innercc = getCallContextCall(call, getNodeEnclosingCallable(p), outercc) @@ -991,7 +986,7 @@ private module Stage2 { private predicate fwdFlowIsEntered( DataFlowCall call, Cc cc, ApOption argAp, Ap ap, Configuration config ) { - exists(ParameterNodeExt p | + exists(ParamNode p | fwdFlowIn(call, p, cc, _, argAp, ap, config) and PrevStage::parameterMayFlowThrough(p, _, getApprox(ap), config) ) @@ -1132,10 +1127,9 @@ private module Stage2 { pragma[nomagic] private predicate revFlowIn( - DataFlowCall call, ArgumentNodeExt arg, boolean toReturn, ApOption returnAp, Ap ap, - Configuration config + DataFlowCall call, ArgNode arg, boolean toReturn, ApOption returnAp, Ap ap, Configuration config ) { - exists(ParameterNodeExt p, boolean allowsFieldFlow | + exists(ParamNode p, boolean allowsFieldFlow | revFlow(p, toReturn, returnAp, ap, config) and flowIntoCall(call, arg, p, allowsFieldFlow, config) | @@ -1145,7 +1139,7 @@ private module Stage2 { pragma[nomagic] private predicate revFlowInToReturn( - DataFlowCall call, ArgumentNodeExt arg, Ap returnAp, Ap ap, Configuration config + DataFlowCall call, ArgNode arg, Ap returnAp, Ap ap, Configuration config ) { revFlowIn(call, arg, true, apSome(returnAp), ap, config) } @@ -1198,15 +1192,13 @@ private module Stage2 { pragma[noinline] private predicate parameterFlow( - ParameterNodeExt p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config + ParamNode p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config ) { revFlow(p, true, apSome(ap0), ap, config) and c = getNodeEnclosingCallable(p) } - predicate parameterMayFlowThrough( - ParameterNodeExt p, DataFlowCallable c, Ap ap, Configuration config - ) { + predicate parameterMayFlowThrough(ParamNode p, DataFlowCallable c, Ap ap, Configuration config) { exists(ReturnNodeExt ret, Ap ap0, ReturnKindExt kind, int pos | parameterFlow(p, ap, ap0, c, config) and c = getNodeEnclosingCallable(ret) and @@ -1246,8 +1238,7 @@ private predicate flowOutOfCallNodeCand2( pragma[nomagic] private predicate flowIntoCallNodeCand2( - DataFlowCall call, ArgumentNodeExt node1, ParameterNodeExt node2, boolean allowsFieldFlow, - Configuration config + DataFlowCall call, ArgNode node1, ParamNode node2, boolean allowsFieldFlow, Configuration config ) { flowIntoCallNodeCand1(call, node1, node2, allowsFieldFlow, config) and Stage2::revFlow(node2, pragma[only_bind_into](config)) and @@ -1276,7 +1267,7 @@ private module LocalFlowBigStep { config.isSource(node) or jumpStep(_, node, config) or additionalJumpStep(_, node, config) or - node instanceof ParameterNodeExt or + node instanceof ParamNode or node instanceof OutNodeExt or store(_, _, node, _) or read(_, _, node) or @@ -1586,10 +1577,10 @@ private module Stage3 { pragma[nomagic] private predicate fwdFlowIn( - DataFlowCall call, ParameterNodeExt p, Cc outercc, Cc innercc, ApOption argAp, Ap ap, + DataFlowCall call, ParamNode p, Cc outercc, Cc innercc, ApOption argAp, Ap ap, Configuration config ) { - exists(ArgumentNodeExt arg, boolean allowsFieldFlow | + exists(ArgNode arg, boolean allowsFieldFlow | fwdFlow(arg, outercc, argAp, ap, config) and flowIntoCall(call, arg, p, allowsFieldFlow, config) and innercc = getCallContextCall(call, getNodeEnclosingCallable(p), outercc) @@ -1634,7 +1625,7 @@ private module Stage3 { private predicate fwdFlowIsEntered( DataFlowCall call, Cc cc, ApOption argAp, Ap ap, Configuration config ) { - exists(ParameterNodeExt p | + exists(ParamNode p | fwdFlowIn(call, p, cc, _, argAp, ap, config) and PrevStage::parameterMayFlowThrough(p, _, unbindApa(getApprox(ap)), config) ) @@ -1775,10 +1766,9 @@ private module Stage3 { pragma[nomagic] private predicate revFlowIn( - DataFlowCall call, ArgumentNodeExt arg, boolean toReturn, ApOption returnAp, Ap ap, - Configuration config + DataFlowCall call, ArgNode arg, boolean toReturn, ApOption returnAp, Ap ap, Configuration config ) { - exists(ParameterNodeExt p, boolean allowsFieldFlow | + exists(ParamNode p, boolean allowsFieldFlow | revFlow(p, toReturn, returnAp, ap, config) and flowIntoCall(call, arg, p, allowsFieldFlow, config) | @@ -1788,7 +1778,7 @@ private module Stage3 { pragma[nomagic] private predicate revFlowInToReturn( - DataFlowCall call, ArgumentNodeExt arg, Ap returnAp, Ap ap, Configuration config + DataFlowCall call, ArgNode arg, Ap returnAp, Ap ap, Configuration config ) { revFlowIn(call, arg, true, apSome(returnAp), ap, config) } @@ -1841,15 +1831,13 @@ private module Stage3 { pragma[noinline] private predicate parameterFlow( - ParameterNodeExt p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config + ParamNode p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config ) { revFlow(p, true, apSome(ap0), ap, config) and c = getNodeEnclosingCallable(p) } - predicate parameterMayFlowThrough( - ParameterNodeExt p, DataFlowCallable c, Ap ap, Configuration config - ) { + predicate parameterMayFlowThrough(ParamNode p, DataFlowCallable c, Ap ap, Configuration config) { exists(ReturnNodeExt ret, Ap ap0, ReturnKindExt kind, int pos | parameterFlow(p, ap, ap0, c, config) and c = getNodeEnclosingCallable(ret) and @@ -2160,8 +2148,7 @@ private module Stage4 { pragma[nomagic] private predicate flowIntoCall( - DataFlowCall call, ArgumentNodeExt node1, ParameterNodeExt node2, boolean allowsFieldFlow, - Configuration config + DataFlowCall call, ArgNode node1, ParamNode node2, boolean allowsFieldFlow, Configuration config ) { flowIntoCallNodeCand2(call, node1, node2, allowsFieldFlow, config) and PrevStage::revFlow(node2, _, _, _, pragma[only_bind_into](config)) and @@ -2305,10 +2292,10 @@ private module Stage4 { pragma[nomagic] private predicate fwdFlowIn( - DataFlowCall call, ParameterNodeExt p, Cc outercc, Cc innercc, ApOption argAp, Ap ap, + DataFlowCall call, ParamNode p, Cc outercc, Cc innercc, ApOption argAp, Ap ap, Configuration config ) { - exists(ArgumentNodeExt arg, boolean allowsFieldFlow | + exists(ArgNode arg, boolean allowsFieldFlow | fwdFlow(arg, outercc, argAp, ap, config) and flowIntoCall(call, arg, p, allowsFieldFlow, config) and innercc = getCallContextCall(call, getNodeEnclosingCallable(p), outercc) @@ -2353,7 +2340,7 @@ private module Stage4 { private predicate fwdFlowIsEntered( DataFlowCall call, Cc cc, ApOption argAp, Ap ap, Configuration config ) { - exists(ParameterNodeExt p | + exists(ParamNode p | fwdFlowIn(call, p, cc, _, argAp, ap, config) and PrevStage::parameterMayFlowThrough(p, _, unbindApa(getApprox(ap)), config) ) @@ -2494,10 +2481,9 @@ private module Stage4 { pragma[nomagic] private predicate revFlowIn( - DataFlowCall call, ArgumentNodeExt arg, boolean toReturn, ApOption returnAp, Ap ap, - Configuration config + DataFlowCall call, ArgNode arg, boolean toReturn, ApOption returnAp, Ap ap, Configuration config ) { - exists(ParameterNodeExt p, boolean allowsFieldFlow | + exists(ParamNode p, boolean allowsFieldFlow | revFlow(p, toReturn, returnAp, ap, config) and flowIntoCall(call, arg, p, allowsFieldFlow, config) | @@ -2507,7 +2493,7 @@ private module Stage4 { pragma[nomagic] private predicate revFlowInToReturn( - DataFlowCall call, ArgumentNodeExt arg, Ap returnAp, Ap ap, Configuration config + DataFlowCall call, ArgNode arg, Ap returnAp, Ap ap, Configuration config ) { revFlowIn(call, arg, true, apSome(returnAp), ap, config) } @@ -2560,15 +2546,13 @@ private module Stage4 { pragma[noinline] private predicate parameterFlow( - ParameterNodeExt p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config + ParamNode p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config ) { revFlow(p, true, apSome(ap0), ap, config) and c = getNodeEnclosingCallable(p) } - predicate parameterMayFlowThrough( - ParameterNodeExt p, DataFlowCallable c, Ap ap, Configuration config - ) { + predicate parameterMayFlowThrough(ParamNode p, DataFlowCallable c, Ap ap, Configuration config) { exists(ReturnNodeExt ret, Ap ap0, ReturnKindExt kind, int pos | parameterFlow(p, ap, ap0, c, config) and c = getNodeEnclosingCallable(ret) and @@ -2613,7 +2597,7 @@ private predicate nodeMayUseSummary(Node n, AccessPathApprox apa, Configuration private newtype TSummaryCtx = TSummaryCtxNone() or - TSummaryCtxSome(ParameterNodeExt p, AccessPath ap) { + TSummaryCtxSome(ParamNode p, AccessPath ap) { Stage4::parameterMayFlowThrough(p, _, ap.getApprox(), _) } @@ -2634,7 +2618,7 @@ private class SummaryCtxNone extends SummaryCtx, TSummaryCtxNone { /** A summary context from which a flow summary can be generated. */ private class SummaryCtxSome extends SummaryCtx, TSummaryCtxSome { - private ParameterNodeExt p; + private ParamNode p; private AccessPath ap; SummaryCtxSome() { this = TSummaryCtxSome(p, ap) } @@ -3242,7 +3226,7 @@ pragma[noinline] private predicate pathIntoArg( PathNodeMid mid, int i, CallContext cc, DataFlowCall call, AccessPath ap, AccessPathApprox apa ) { - exists(ArgumentNodeExt arg | + exists(ArgNode arg | arg = mid.getNode() and cc = mid.getCallContext() and arg.argumentOf(call, i) and @@ -3255,7 +3239,7 @@ pragma[noinline] private predicate parameterCand( DataFlowCallable callable, int i, AccessPathApprox apa, Configuration config ) { - exists(ParameterNodeExt p | + exists(ParamNode p | Stage4::revFlow(p, _, _, apa, config) and p.isParameterOf(callable, i) ) @@ -3279,7 +3263,7 @@ private predicate pathIntoCallable0( * respectively. */ private predicate pathIntoCallable( - PathNodeMid mid, ParameterNodeExt p, CallContext outercc, CallContextCall innercc, SummaryCtx sc, + PathNodeMid mid, ParamNode p, CallContext outercc, CallContextCall innercc, SummaryCtx sc, DataFlowCall call ) { exists(int i, DataFlowCallable callable, AccessPath ap | @@ -3575,7 +3559,7 @@ private module FlowExploration { private newtype TSummaryCtx1 = TSummaryCtx1None() or - TSummaryCtx1Param(ParameterNodeExt p) + TSummaryCtx1Param(ParamNode p) private newtype TSummaryCtx2 = TSummaryCtx2None() or @@ -3931,7 +3915,7 @@ private module FlowExploration { PartialPathNodeFwd mid, int i, CallContext cc, DataFlowCall call, PartialAccessPath ap, Configuration config ) { - exists(ArgumentNodeExt arg | + exists(ArgNode arg | arg = mid.getNode() and cc = mid.getCallContext() and arg.argumentOf(call, i) and @@ -3950,7 +3934,7 @@ private module FlowExploration { } private predicate partialPathIntoCallable( - PartialPathNodeFwd mid, ParameterNodeExt p, CallContext outercc, CallContextCall innercc, + PartialPathNodeFwd mid, ParamNode p, CallContext outercc, CallContextCall innercc, TSummaryCtx1 sc1, TSummaryCtx2 sc2, DataFlowCall call, PartialAccessPath ap, Configuration config ) { @@ -3987,7 +3971,7 @@ private module FlowExploration { DataFlowCall call, PartialPathNodeFwd mid, ReturnKindExt kind, CallContext cc, PartialAccessPath ap, Configuration config ) { - exists(ParameterNodeExt p, CallContext innercc, TSummaryCtx1 sc1, TSummaryCtx2 sc2 | + exists(ParamNode p, CallContext innercc, TSummaryCtx1 sc1, TSummaryCtx2 sc2 | partialPathIntoCallable(mid, p, cc, innercc, sc1, sc2, call, _, config) and paramFlowsThroughInPartialPath(kind, innercc, sc1, sc2, ap, config) ) @@ -4044,7 +4028,7 @@ private module FlowExploration { apConsRev(ap, c, ap0, config) ) or - exists(ParameterNodeExt p | + exists(ParamNode p | mid.getNode() = p and viableParamArg(_, p, node) and sc1 = mid.getSummaryCtx1() and @@ -4122,7 +4106,7 @@ private module FlowExploration { int pos, TRevSummaryCtx1Some sc1, TRevSummaryCtx2Some sc2, RevPartialAccessPath ap, Configuration config ) { - exists(PartialPathNodeRev mid, ParameterNodeExt p | + exists(PartialPathNodeRev mid, ParamNode p | mid.getNode() = p and p.isParameterOf(_, pos) and sc1 = mid.getSummaryCtx1() and @@ -4145,7 +4129,7 @@ private module FlowExploration { pragma[nomagic] private predicate revPartialPathThroughCallable( - PartialPathNodeRev mid, ArgumentNodeExt node, RevPartialAccessPath ap, Configuration config + PartialPathNodeRev mid, ArgNode node, RevPartialAccessPath ap, Configuration config ) { exists(DataFlowCall call, int pos | revPartialPathThroughCallable0(call, mid, pos, ap, config) and 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 cf83530bcbd..058d66b1496 100644 --- a/java/ql/src/semmle/code/java/dataflow/internal/DataFlowImpl5.qll +++ b/java/ql/src/semmle/code/java/dataflow/internal/DataFlowImpl5.qll @@ -385,7 +385,7 @@ private module Stage1 { */ pragma[nomagic] private predicate fwdFlowIsEntered(DataFlowCall call, Cc cc, Configuration config) { - exists(ArgumentNodeExt arg | + exists(ArgNode arg | fwdFlow(arg, cc, config) and viableParamArg(call, _, arg) ) @@ -512,24 +512,22 @@ private module Stage1 { pragma[nomagic] predicate viableParamArgNodeCandFwd1( - DataFlowCall call, ParameterNodeExt p, ArgumentNodeExt arg, Configuration config + DataFlowCall call, ParamNode p, ArgNode arg, Configuration config ) { viableParamArg(call, p, arg) and fwdFlow(arg, config) } pragma[nomagic] - private predicate revFlowIn( - DataFlowCall call, ArgumentNodeExt arg, boolean toReturn, Configuration config - ) { - exists(ParameterNodeExt p | + private predicate revFlowIn(DataFlowCall call, ArgNode arg, boolean toReturn, Configuration config) { + exists(ParamNode p | revFlow(p, toReturn, config) and viableParamArgNodeCandFwd1(call, p, arg, config) ) } pragma[nomagic] - private predicate revFlowInToReturn(DataFlowCall call, ArgumentNodeExt arg, Configuration config) { + private predicate revFlowInToReturn(DataFlowCall call, ArgNode arg, Configuration config) { revFlowIn(call, arg, true, config) } @@ -594,9 +592,7 @@ private module Stage1 { * Holds if flow may enter through `p` and reach a return node making `p` a * candidate for the origin of a summary. */ - predicate parameterMayFlowThrough( - ParameterNodeExt p, DataFlowCallable c, Ap ap, Configuration config - ) { + predicate parameterMayFlowThrough(ParamNode p, DataFlowCallable c, Ap ap, Configuration config) { exists(ReturnKindExt kind | throughFlowNodeCand(p, config) and returnFlowCallableNodeCand(c, kind, config) and @@ -662,7 +658,7 @@ private predicate flowOutOfCallNodeCand1( pragma[nomagic] private predicate viableParamArgNodeCand1( - DataFlowCall call, ParameterNodeExt p, ArgumentNodeExt arg, Configuration config + DataFlowCall call, ParamNode p, ArgNode arg, Configuration config ) { Stage1::viableParamArgNodeCandFwd1(call, p, arg, config) and Stage1::revFlow(arg, config) @@ -674,7 +670,7 @@ private predicate viableParamArgNodeCand1( */ pragma[nomagic] private predicate flowIntoCallNodeCand1( - DataFlowCall call, ArgumentNodeExt arg, ParameterNodeExt p, Configuration config + DataFlowCall call, ArgNode arg, ParamNode p, Configuration config ) { viableParamArgNodeCand1(call, p, arg, config) and Stage1::revFlow(p, config) and @@ -734,8 +730,7 @@ private predicate flowOutOfCallNodeCand1( */ pragma[nomagic] private predicate flowIntoCallNodeCand1( - DataFlowCall call, ArgumentNodeExt arg, ParameterNodeExt p, boolean allowsFieldFlow, - Configuration config + DataFlowCall call, ArgNode arg, ParamNode p, boolean allowsFieldFlow, Configuration config ) { flowIntoCallNodeCand1(call, arg, p, config) and exists(int b, int j | @@ -943,10 +938,10 @@ private module Stage2 { pragma[nomagic] private predicate fwdFlowIn( - DataFlowCall call, ParameterNodeExt p, Cc outercc, Cc innercc, ApOption argAp, Ap ap, + DataFlowCall call, ParamNode p, Cc outercc, Cc innercc, ApOption argAp, Ap ap, Configuration config ) { - exists(ArgumentNodeExt arg, boolean allowsFieldFlow | + exists(ArgNode arg, boolean allowsFieldFlow | fwdFlow(arg, outercc, argAp, ap, config) and flowIntoCall(call, arg, p, allowsFieldFlow, config) and innercc = getCallContextCall(call, getNodeEnclosingCallable(p), outercc) @@ -991,7 +986,7 @@ private module Stage2 { private predicate fwdFlowIsEntered( DataFlowCall call, Cc cc, ApOption argAp, Ap ap, Configuration config ) { - exists(ParameterNodeExt p | + exists(ParamNode p | fwdFlowIn(call, p, cc, _, argAp, ap, config) and PrevStage::parameterMayFlowThrough(p, _, getApprox(ap), config) ) @@ -1132,10 +1127,9 @@ private module Stage2 { pragma[nomagic] private predicate revFlowIn( - DataFlowCall call, ArgumentNodeExt arg, boolean toReturn, ApOption returnAp, Ap ap, - Configuration config + DataFlowCall call, ArgNode arg, boolean toReturn, ApOption returnAp, Ap ap, Configuration config ) { - exists(ParameterNodeExt p, boolean allowsFieldFlow | + exists(ParamNode p, boolean allowsFieldFlow | revFlow(p, toReturn, returnAp, ap, config) and flowIntoCall(call, arg, p, allowsFieldFlow, config) | @@ -1145,7 +1139,7 @@ private module Stage2 { pragma[nomagic] private predicate revFlowInToReturn( - DataFlowCall call, ArgumentNodeExt arg, Ap returnAp, Ap ap, Configuration config + DataFlowCall call, ArgNode arg, Ap returnAp, Ap ap, Configuration config ) { revFlowIn(call, arg, true, apSome(returnAp), ap, config) } @@ -1198,15 +1192,13 @@ private module Stage2 { pragma[noinline] private predicate parameterFlow( - ParameterNodeExt p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config + ParamNode p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config ) { revFlow(p, true, apSome(ap0), ap, config) and c = getNodeEnclosingCallable(p) } - predicate parameterMayFlowThrough( - ParameterNodeExt p, DataFlowCallable c, Ap ap, Configuration config - ) { + predicate parameterMayFlowThrough(ParamNode p, DataFlowCallable c, Ap ap, Configuration config) { exists(ReturnNodeExt ret, Ap ap0, ReturnKindExt kind, int pos | parameterFlow(p, ap, ap0, c, config) and c = getNodeEnclosingCallable(ret) and @@ -1246,8 +1238,7 @@ private predicate flowOutOfCallNodeCand2( pragma[nomagic] private predicate flowIntoCallNodeCand2( - DataFlowCall call, ArgumentNodeExt node1, ParameterNodeExt node2, boolean allowsFieldFlow, - Configuration config + DataFlowCall call, ArgNode node1, ParamNode node2, boolean allowsFieldFlow, Configuration config ) { flowIntoCallNodeCand1(call, node1, node2, allowsFieldFlow, config) and Stage2::revFlow(node2, pragma[only_bind_into](config)) and @@ -1276,7 +1267,7 @@ private module LocalFlowBigStep { config.isSource(node) or jumpStep(_, node, config) or additionalJumpStep(_, node, config) or - node instanceof ParameterNodeExt or + node instanceof ParamNode or node instanceof OutNodeExt or store(_, _, node, _) or read(_, _, node) or @@ -1586,10 +1577,10 @@ private module Stage3 { pragma[nomagic] private predicate fwdFlowIn( - DataFlowCall call, ParameterNodeExt p, Cc outercc, Cc innercc, ApOption argAp, Ap ap, + DataFlowCall call, ParamNode p, Cc outercc, Cc innercc, ApOption argAp, Ap ap, Configuration config ) { - exists(ArgumentNodeExt arg, boolean allowsFieldFlow | + exists(ArgNode arg, boolean allowsFieldFlow | fwdFlow(arg, outercc, argAp, ap, config) and flowIntoCall(call, arg, p, allowsFieldFlow, config) and innercc = getCallContextCall(call, getNodeEnclosingCallable(p), outercc) @@ -1634,7 +1625,7 @@ private module Stage3 { private predicate fwdFlowIsEntered( DataFlowCall call, Cc cc, ApOption argAp, Ap ap, Configuration config ) { - exists(ParameterNodeExt p | + exists(ParamNode p | fwdFlowIn(call, p, cc, _, argAp, ap, config) and PrevStage::parameterMayFlowThrough(p, _, unbindApa(getApprox(ap)), config) ) @@ -1775,10 +1766,9 @@ private module Stage3 { pragma[nomagic] private predicate revFlowIn( - DataFlowCall call, ArgumentNodeExt arg, boolean toReturn, ApOption returnAp, Ap ap, - Configuration config + DataFlowCall call, ArgNode arg, boolean toReturn, ApOption returnAp, Ap ap, Configuration config ) { - exists(ParameterNodeExt p, boolean allowsFieldFlow | + exists(ParamNode p, boolean allowsFieldFlow | revFlow(p, toReturn, returnAp, ap, config) and flowIntoCall(call, arg, p, allowsFieldFlow, config) | @@ -1788,7 +1778,7 @@ private module Stage3 { pragma[nomagic] private predicate revFlowInToReturn( - DataFlowCall call, ArgumentNodeExt arg, Ap returnAp, Ap ap, Configuration config + DataFlowCall call, ArgNode arg, Ap returnAp, Ap ap, Configuration config ) { revFlowIn(call, arg, true, apSome(returnAp), ap, config) } @@ -1841,15 +1831,13 @@ private module Stage3 { pragma[noinline] private predicate parameterFlow( - ParameterNodeExt p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config + ParamNode p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config ) { revFlow(p, true, apSome(ap0), ap, config) and c = getNodeEnclosingCallable(p) } - predicate parameterMayFlowThrough( - ParameterNodeExt p, DataFlowCallable c, Ap ap, Configuration config - ) { + predicate parameterMayFlowThrough(ParamNode p, DataFlowCallable c, Ap ap, Configuration config) { exists(ReturnNodeExt ret, Ap ap0, ReturnKindExt kind, int pos | parameterFlow(p, ap, ap0, c, config) and c = getNodeEnclosingCallable(ret) and @@ -2160,8 +2148,7 @@ private module Stage4 { pragma[nomagic] private predicate flowIntoCall( - DataFlowCall call, ArgumentNodeExt node1, ParameterNodeExt node2, boolean allowsFieldFlow, - Configuration config + DataFlowCall call, ArgNode node1, ParamNode node2, boolean allowsFieldFlow, Configuration config ) { flowIntoCallNodeCand2(call, node1, node2, allowsFieldFlow, config) and PrevStage::revFlow(node2, _, _, _, pragma[only_bind_into](config)) and @@ -2305,10 +2292,10 @@ private module Stage4 { pragma[nomagic] private predicate fwdFlowIn( - DataFlowCall call, ParameterNodeExt p, Cc outercc, Cc innercc, ApOption argAp, Ap ap, + DataFlowCall call, ParamNode p, Cc outercc, Cc innercc, ApOption argAp, Ap ap, Configuration config ) { - exists(ArgumentNodeExt arg, boolean allowsFieldFlow | + exists(ArgNode arg, boolean allowsFieldFlow | fwdFlow(arg, outercc, argAp, ap, config) and flowIntoCall(call, arg, p, allowsFieldFlow, config) and innercc = getCallContextCall(call, getNodeEnclosingCallable(p), outercc) @@ -2353,7 +2340,7 @@ private module Stage4 { private predicate fwdFlowIsEntered( DataFlowCall call, Cc cc, ApOption argAp, Ap ap, Configuration config ) { - exists(ParameterNodeExt p | + exists(ParamNode p | fwdFlowIn(call, p, cc, _, argAp, ap, config) and PrevStage::parameterMayFlowThrough(p, _, unbindApa(getApprox(ap)), config) ) @@ -2494,10 +2481,9 @@ private module Stage4 { pragma[nomagic] private predicate revFlowIn( - DataFlowCall call, ArgumentNodeExt arg, boolean toReturn, ApOption returnAp, Ap ap, - Configuration config + DataFlowCall call, ArgNode arg, boolean toReturn, ApOption returnAp, Ap ap, Configuration config ) { - exists(ParameterNodeExt p, boolean allowsFieldFlow | + exists(ParamNode p, boolean allowsFieldFlow | revFlow(p, toReturn, returnAp, ap, config) and flowIntoCall(call, arg, p, allowsFieldFlow, config) | @@ -2507,7 +2493,7 @@ private module Stage4 { pragma[nomagic] private predicate revFlowInToReturn( - DataFlowCall call, ArgumentNodeExt arg, Ap returnAp, Ap ap, Configuration config + DataFlowCall call, ArgNode arg, Ap returnAp, Ap ap, Configuration config ) { revFlowIn(call, arg, true, apSome(returnAp), ap, config) } @@ -2560,15 +2546,13 @@ private module Stage4 { pragma[noinline] private predicate parameterFlow( - ParameterNodeExt p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config + ParamNode p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config ) { revFlow(p, true, apSome(ap0), ap, config) and c = getNodeEnclosingCallable(p) } - predicate parameterMayFlowThrough( - ParameterNodeExt p, DataFlowCallable c, Ap ap, Configuration config - ) { + predicate parameterMayFlowThrough(ParamNode p, DataFlowCallable c, Ap ap, Configuration config) { exists(ReturnNodeExt ret, Ap ap0, ReturnKindExt kind, int pos | parameterFlow(p, ap, ap0, c, config) and c = getNodeEnclosingCallable(ret) and @@ -2613,7 +2597,7 @@ private predicate nodeMayUseSummary(Node n, AccessPathApprox apa, Configuration private newtype TSummaryCtx = TSummaryCtxNone() or - TSummaryCtxSome(ParameterNodeExt p, AccessPath ap) { + TSummaryCtxSome(ParamNode p, AccessPath ap) { Stage4::parameterMayFlowThrough(p, _, ap.getApprox(), _) } @@ -2634,7 +2618,7 @@ private class SummaryCtxNone extends SummaryCtx, TSummaryCtxNone { /** A summary context from which a flow summary can be generated. */ private class SummaryCtxSome extends SummaryCtx, TSummaryCtxSome { - private ParameterNodeExt p; + private ParamNode p; private AccessPath ap; SummaryCtxSome() { this = TSummaryCtxSome(p, ap) } @@ -3242,7 +3226,7 @@ pragma[noinline] private predicate pathIntoArg( PathNodeMid mid, int i, CallContext cc, DataFlowCall call, AccessPath ap, AccessPathApprox apa ) { - exists(ArgumentNodeExt arg | + exists(ArgNode arg | arg = mid.getNode() and cc = mid.getCallContext() and arg.argumentOf(call, i) and @@ -3255,7 +3239,7 @@ pragma[noinline] private predicate parameterCand( DataFlowCallable callable, int i, AccessPathApprox apa, Configuration config ) { - exists(ParameterNodeExt p | + exists(ParamNode p | Stage4::revFlow(p, _, _, apa, config) and p.isParameterOf(callable, i) ) @@ -3279,7 +3263,7 @@ private predicate pathIntoCallable0( * respectively. */ private predicate pathIntoCallable( - PathNodeMid mid, ParameterNodeExt p, CallContext outercc, CallContextCall innercc, SummaryCtx sc, + PathNodeMid mid, ParamNode p, CallContext outercc, CallContextCall innercc, SummaryCtx sc, DataFlowCall call ) { exists(int i, DataFlowCallable callable, AccessPath ap | @@ -3575,7 +3559,7 @@ private module FlowExploration { private newtype TSummaryCtx1 = TSummaryCtx1None() or - TSummaryCtx1Param(ParameterNodeExt p) + TSummaryCtx1Param(ParamNode p) private newtype TSummaryCtx2 = TSummaryCtx2None() or @@ -3931,7 +3915,7 @@ private module FlowExploration { PartialPathNodeFwd mid, int i, CallContext cc, DataFlowCall call, PartialAccessPath ap, Configuration config ) { - exists(ArgumentNodeExt arg | + exists(ArgNode arg | arg = mid.getNode() and cc = mid.getCallContext() and arg.argumentOf(call, i) and @@ -3950,7 +3934,7 @@ private module FlowExploration { } private predicate partialPathIntoCallable( - PartialPathNodeFwd mid, ParameterNodeExt p, CallContext outercc, CallContextCall innercc, + PartialPathNodeFwd mid, ParamNode p, CallContext outercc, CallContextCall innercc, TSummaryCtx1 sc1, TSummaryCtx2 sc2, DataFlowCall call, PartialAccessPath ap, Configuration config ) { @@ -3987,7 +3971,7 @@ private module FlowExploration { DataFlowCall call, PartialPathNodeFwd mid, ReturnKindExt kind, CallContext cc, PartialAccessPath ap, Configuration config ) { - exists(ParameterNodeExt p, CallContext innercc, TSummaryCtx1 sc1, TSummaryCtx2 sc2 | + exists(ParamNode p, CallContext innercc, TSummaryCtx1 sc1, TSummaryCtx2 sc2 | partialPathIntoCallable(mid, p, cc, innercc, sc1, sc2, call, _, config) and paramFlowsThroughInPartialPath(kind, innercc, sc1, sc2, ap, config) ) @@ -4044,7 +4028,7 @@ private module FlowExploration { apConsRev(ap, c, ap0, config) ) or - exists(ParameterNodeExt p | + exists(ParamNode p | mid.getNode() = p and viableParamArg(_, p, node) and sc1 = mid.getSummaryCtx1() and @@ -4122,7 +4106,7 @@ private module FlowExploration { int pos, TRevSummaryCtx1Some sc1, TRevSummaryCtx2Some sc2, RevPartialAccessPath ap, Configuration config ) { - exists(PartialPathNodeRev mid, ParameterNodeExt p | + exists(PartialPathNodeRev mid, ParamNode p | mid.getNode() = p and p.isParameterOf(_, pos) and sc1 = mid.getSummaryCtx1() and @@ -4145,7 +4129,7 @@ private module FlowExploration { pragma[nomagic] private predicate revPartialPathThroughCallable( - PartialPathNodeRev mid, ArgumentNodeExt node, RevPartialAccessPath ap, Configuration config + PartialPathNodeRev mid, ArgNode node, RevPartialAccessPath ap, Configuration config ) { exists(DataFlowCall call, int pos | revPartialPathThroughCallable0(call, mid, pos, ap, config) and diff --git a/java/ql/src/semmle/code/java/dataflow/internal/DataFlowImpl6.qll b/java/ql/src/semmle/code/java/dataflow/internal/DataFlowImpl6.qll index cf83530bcbd..058d66b1496 100644 --- a/java/ql/src/semmle/code/java/dataflow/internal/DataFlowImpl6.qll +++ b/java/ql/src/semmle/code/java/dataflow/internal/DataFlowImpl6.qll @@ -385,7 +385,7 @@ private module Stage1 { */ pragma[nomagic] private predicate fwdFlowIsEntered(DataFlowCall call, Cc cc, Configuration config) { - exists(ArgumentNodeExt arg | + exists(ArgNode arg | fwdFlow(arg, cc, config) and viableParamArg(call, _, arg) ) @@ -512,24 +512,22 @@ private module Stage1 { pragma[nomagic] predicate viableParamArgNodeCandFwd1( - DataFlowCall call, ParameterNodeExt p, ArgumentNodeExt arg, Configuration config + DataFlowCall call, ParamNode p, ArgNode arg, Configuration config ) { viableParamArg(call, p, arg) and fwdFlow(arg, config) } pragma[nomagic] - private predicate revFlowIn( - DataFlowCall call, ArgumentNodeExt arg, boolean toReturn, Configuration config - ) { - exists(ParameterNodeExt p | + private predicate revFlowIn(DataFlowCall call, ArgNode arg, boolean toReturn, Configuration config) { + exists(ParamNode p | revFlow(p, toReturn, config) and viableParamArgNodeCandFwd1(call, p, arg, config) ) } pragma[nomagic] - private predicate revFlowInToReturn(DataFlowCall call, ArgumentNodeExt arg, Configuration config) { + private predicate revFlowInToReturn(DataFlowCall call, ArgNode arg, Configuration config) { revFlowIn(call, arg, true, config) } @@ -594,9 +592,7 @@ private module Stage1 { * Holds if flow may enter through `p` and reach a return node making `p` a * candidate for the origin of a summary. */ - predicate parameterMayFlowThrough( - ParameterNodeExt p, DataFlowCallable c, Ap ap, Configuration config - ) { + predicate parameterMayFlowThrough(ParamNode p, DataFlowCallable c, Ap ap, Configuration config) { exists(ReturnKindExt kind | throughFlowNodeCand(p, config) and returnFlowCallableNodeCand(c, kind, config) and @@ -662,7 +658,7 @@ private predicate flowOutOfCallNodeCand1( pragma[nomagic] private predicate viableParamArgNodeCand1( - DataFlowCall call, ParameterNodeExt p, ArgumentNodeExt arg, Configuration config + DataFlowCall call, ParamNode p, ArgNode arg, Configuration config ) { Stage1::viableParamArgNodeCandFwd1(call, p, arg, config) and Stage1::revFlow(arg, config) @@ -674,7 +670,7 @@ private predicate viableParamArgNodeCand1( */ pragma[nomagic] private predicate flowIntoCallNodeCand1( - DataFlowCall call, ArgumentNodeExt arg, ParameterNodeExt p, Configuration config + DataFlowCall call, ArgNode arg, ParamNode p, Configuration config ) { viableParamArgNodeCand1(call, p, arg, config) and Stage1::revFlow(p, config) and @@ -734,8 +730,7 @@ private predicate flowOutOfCallNodeCand1( */ pragma[nomagic] private predicate flowIntoCallNodeCand1( - DataFlowCall call, ArgumentNodeExt arg, ParameterNodeExt p, boolean allowsFieldFlow, - Configuration config + DataFlowCall call, ArgNode arg, ParamNode p, boolean allowsFieldFlow, Configuration config ) { flowIntoCallNodeCand1(call, arg, p, config) and exists(int b, int j | @@ -943,10 +938,10 @@ private module Stage2 { pragma[nomagic] private predicate fwdFlowIn( - DataFlowCall call, ParameterNodeExt p, Cc outercc, Cc innercc, ApOption argAp, Ap ap, + DataFlowCall call, ParamNode p, Cc outercc, Cc innercc, ApOption argAp, Ap ap, Configuration config ) { - exists(ArgumentNodeExt arg, boolean allowsFieldFlow | + exists(ArgNode arg, boolean allowsFieldFlow | fwdFlow(arg, outercc, argAp, ap, config) and flowIntoCall(call, arg, p, allowsFieldFlow, config) and innercc = getCallContextCall(call, getNodeEnclosingCallable(p), outercc) @@ -991,7 +986,7 @@ private module Stage2 { private predicate fwdFlowIsEntered( DataFlowCall call, Cc cc, ApOption argAp, Ap ap, Configuration config ) { - exists(ParameterNodeExt p | + exists(ParamNode p | fwdFlowIn(call, p, cc, _, argAp, ap, config) and PrevStage::parameterMayFlowThrough(p, _, getApprox(ap), config) ) @@ -1132,10 +1127,9 @@ private module Stage2 { pragma[nomagic] private predicate revFlowIn( - DataFlowCall call, ArgumentNodeExt arg, boolean toReturn, ApOption returnAp, Ap ap, - Configuration config + DataFlowCall call, ArgNode arg, boolean toReturn, ApOption returnAp, Ap ap, Configuration config ) { - exists(ParameterNodeExt p, boolean allowsFieldFlow | + exists(ParamNode p, boolean allowsFieldFlow | revFlow(p, toReturn, returnAp, ap, config) and flowIntoCall(call, arg, p, allowsFieldFlow, config) | @@ -1145,7 +1139,7 @@ private module Stage2 { pragma[nomagic] private predicate revFlowInToReturn( - DataFlowCall call, ArgumentNodeExt arg, Ap returnAp, Ap ap, Configuration config + DataFlowCall call, ArgNode arg, Ap returnAp, Ap ap, Configuration config ) { revFlowIn(call, arg, true, apSome(returnAp), ap, config) } @@ -1198,15 +1192,13 @@ private module Stage2 { pragma[noinline] private predicate parameterFlow( - ParameterNodeExt p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config + ParamNode p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config ) { revFlow(p, true, apSome(ap0), ap, config) and c = getNodeEnclosingCallable(p) } - predicate parameterMayFlowThrough( - ParameterNodeExt p, DataFlowCallable c, Ap ap, Configuration config - ) { + predicate parameterMayFlowThrough(ParamNode p, DataFlowCallable c, Ap ap, Configuration config) { exists(ReturnNodeExt ret, Ap ap0, ReturnKindExt kind, int pos | parameterFlow(p, ap, ap0, c, config) and c = getNodeEnclosingCallable(ret) and @@ -1246,8 +1238,7 @@ private predicate flowOutOfCallNodeCand2( pragma[nomagic] private predicate flowIntoCallNodeCand2( - DataFlowCall call, ArgumentNodeExt node1, ParameterNodeExt node2, boolean allowsFieldFlow, - Configuration config + DataFlowCall call, ArgNode node1, ParamNode node2, boolean allowsFieldFlow, Configuration config ) { flowIntoCallNodeCand1(call, node1, node2, allowsFieldFlow, config) and Stage2::revFlow(node2, pragma[only_bind_into](config)) and @@ -1276,7 +1267,7 @@ private module LocalFlowBigStep { config.isSource(node) or jumpStep(_, node, config) or additionalJumpStep(_, node, config) or - node instanceof ParameterNodeExt or + node instanceof ParamNode or node instanceof OutNodeExt or store(_, _, node, _) or read(_, _, node) or @@ -1586,10 +1577,10 @@ private module Stage3 { pragma[nomagic] private predicate fwdFlowIn( - DataFlowCall call, ParameterNodeExt p, Cc outercc, Cc innercc, ApOption argAp, Ap ap, + DataFlowCall call, ParamNode p, Cc outercc, Cc innercc, ApOption argAp, Ap ap, Configuration config ) { - exists(ArgumentNodeExt arg, boolean allowsFieldFlow | + exists(ArgNode arg, boolean allowsFieldFlow | fwdFlow(arg, outercc, argAp, ap, config) and flowIntoCall(call, arg, p, allowsFieldFlow, config) and innercc = getCallContextCall(call, getNodeEnclosingCallable(p), outercc) @@ -1634,7 +1625,7 @@ private module Stage3 { private predicate fwdFlowIsEntered( DataFlowCall call, Cc cc, ApOption argAp, Ap ap, Configuration config ) { - exists(ParameterNodeExt p | + exists(ParamNode p | fwdFlowIn(call, p, cc, _, argAp, ap, config) and PrevStage::parameterMayFlowThrough(p, _, unbindApa(getApprox(ap)), config) ) @@ -1775,10 +1766,9 @@ private module Stage3 { pragma[nomagic] private predicate revFlowIn( - DataFlowCall call, ArgumentNodeExt arg, boolean toReturn, ApOption returnAp, Ap ap, - Configuration config + DataFlowCall call, ArgNode arg, boolean toReturn, ApOption returnAp, Ap ap, Configuration config ) { - exists(ParameterNodeExt p, boolean allowsFieldFlow | + exists(ParamNode p, boolean allowsFieldFlow | revFlow(p, toReturn, returnAp, ap, config) and flowIntoCall(call, arg, p, allowsFieldFlow, config) | @@ -1788,7 +1778,7 @@ private module Stage3 { pragma[nomagic] private predicate revFlowInToReturn( - DataFlowCall call, ArgumentNodeExt arg, Ap returnAp, Ap ap, Configuration config + DataFlowCall call, ArgNode arg, Ap returnAp, Ap ap, Configuration config ) { revFlowIn(call, arg, true, apSome(returnAp), ap, config) } @@ -1841,15 +1831,13 @@ private module Stage3 { pragma[noinline] private predicate parameterFlow( - ParameterNodeExt p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config + ParamNode p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config ) { revFlow(p, true, apSome(ap0), ap, config) and c = getNodeEnclosingCallable(p) } - predicate parameterMayFlowThrough( - ParameterNodeExt p, DataFlowCallable c, Ap ap, Configuration config - ) { + predicate parameterMayFlowThrough(ParamNode p, DataFlowCallable c, Ap ap, Configuration config) { exists(ReturnNodeExt ret, Ap ap0, ReturnKindExt kind, int pos | parameterFlow(p, ap, ap0, c, config) and c = getNodeEnclosingCallable(ret) and @@ -2160,8 +2148,7 @@ private module Stage4 { pragma[nomagic] private predicate flowIntoCall( - DataFlowCall call, ArgumentNodeExt node1, ParameterNodeExt node2, boolean allowsFieldFlow, - Configuration config + DataFlowCall call, ArgNode node1, ParamNode node2, boolean allowsFieldFlow, Configuration config ) { flowIntoCallNodeCand2(call, node1, node2, allowsFieldFlow, config) and PrevStage::revFlow(node2, _, _, _, pragma[only_bind_into](config)) and @@ -2305,10 +2292,10 @@ private module Stage4 { pragma[nomagic] private predicate fwdFlowIn( - DataFlowCall call, ParameterNodeExt p, Cc outercc, Cc innercc, ApOption argAp, Ap ap, + DataFlowCall call, ParamNode p, Cc outercc, Cc innercc, ApOption argAp, Ap ap, Configuration config ) { - exists(ArgumentNodeExt arg, boolean allowsFieldFlow | + exists(ArgNode arg, boolean allowsFieldFlow | fwdFlow(arg, outercc, argAp, ap, config) and flowIntoCall(call, arg, p, allowsFieldFlow, config) and innercc = getCallContextCall(call, getNodeEnclosingCallable(p), outercc) @@ -2353,7 +2340,7 @@ private module Stage4 { private predicate fwdFlowIsEntered( DataFlowCall call, Cc cc, ApOption argAp, Ap ap, Configuration config ) { - exists(ParameterNodeExt p | + exists(ParamNode p | fwdFlowIn(call, p, cc, _, argAp, ap, config) and PrevStage::parameterMayFlowThrough(p, _, unbindApa(getApprox(ap)), config) ) @@ -2494,10 +2481,9 @@ private module Stage4 { pragma[nomagic] private predicate revFlowIn( - DataFlowCall call, ArgumentNodeExt arg, boolean toReturn, ApOption returnAp, Ap ap, - Configuration config + DataFlowCall call, ArgNode arg, boolean toReturn, ApOption returnAp, Ap ap, Configuration config ) { - exists(ParameterNodeExt p, boolean allowsFieldFlow | + exists(ParamNode p, boolean allowsFieldFlow | revFlow(p, toReturn, returnAp, ap, config) and flowIntoCall(call, arg, p, allowsFieldFlow, config) | @@ -2507,7 +2493,7 @@ private module Stage4 { pragma[nomagic] private predicate revFlowInToReturn( - DataFlowCall call, ArgumentNodeExt arg, Ap returnAp, Ap ap, Configuration config + DataFlowCall call, ArgNode arg, Ap returnAp, Ap ap, Configuration config ) { revFlowIn(call, arg, true, apSome(returnAp), ap, config) } @@ -2560,15 +2546,13 @@ private module Stage4 { pragma[noinline] private predicate parameterFlow( - ParameterNodeExt p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config + ParamNode p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config ) { revFlow(p, true, apSome(ap0), ap, config) and c = getNodeEnclosingCallable(p) } - predicate parameterMayFlowThrough( - ParameterNodeExt p, DataFlowCallable c, Ap ap, Configuration config - ) { + predicate parameterMayFlowThrough(ParamNode p, DataFlowCallable c, Ap ap, Configuration config) { exists(ReturnNodeExt ret, Ap ap0, ReturnKindExt kind, int pos | parameterFlow(p, ap, ap0, c, config) and c = getNodeEnclosingCallable(ret) and @@ -2613,7 +2597,7 @@ private predicate nodeMayUseSummary(Node n, AccessPathApprox apa, Configuration private newtype TSummaryCtx = TSummaryCtxNone() or - TSummaryCtxSome(ParameterNodeExt p, AccessPath ap) { + TSummaryCtxSome(ParamNode p, AccessPath ap) { Stage4::parameterMayFlowThrough(p, _, ap.getApprox(), _) } @@ -2634,7 +2618,7 @@ private class SummaryCtxNone extends SummaryCtx, TSummaryCtxNone { /** A summary context from which a flow summary can be generated. */ private class SummaryCtxSome extends SummaryCtx, TSummaryCtxSome { - private ParameterNodeExt p; + private ParamNode p; private AccessPath ap; SummaryCtxSome() { this = TSummaryCtxSome(p, ap) } @@ -3242,7 +3226,7 @@ pragma[noinline] private predicate pathIntoArg( PathNodeMid mid, int i, CallContext cc, DataFlowCall call, AccessPath ap, AccessPathApprox apa ) { - exists(ArgumentNodeExt arg | + exists(ArgNode arg | arg = mid.getNode() and cc = mid.getCallContext() and arg.argumentOf(call, i) and @@ -3255,7 +3239,7 @@ pragma[noinline] private predicate parameterCand( DataFlowCallable callable, int i, AccessPathApprox apa, Configuration config ) { - exists(ParameterNodeExt p | + exists(ParamNode p | Stage4::revFlow(p, _, _, apa, config) and p.isParameterOf(callable, i) ) @@ -3279,7 +3263,7 @@ private predicate pathIntoCallable0( * respectively. */ private predicate pathIntoCallable( - PathNodeMid mid, ParameterNodeExt p, CallContext outercc, CallContextCall innercc, SummaryCtx sc, + PathNodeMid mid, ParamNode p, CallContext outercc, CallContextCall innercc, SummaryCtx sc, DataFlowCall call ) { exists(int i, DataFlowCallable callable, AccessPath ap | @@ -3575,7 +3559,7 @@ private module FlowExploration { private newtype TSummaryCtx1 = TSummaryCtx1None() or - TSummaryCtx1Param(ParameterNodeExt p) + TSummaryCtx1Param(ParamNode p) private newtype TSummaryCtx2 = TSummaryCtx2None() or @@ -3931,7 +3915,7 @@ private module FlowExploration { PartialPathNodeFwd mid, int i, CallContext cc, DataFlowCall call, PartialAccessPath ap, Configuration config ) { - exists(ArgumentNodeExt arg | + exists(ArgNode arg | arg = mid.getNode() and cc = mid.getCallContext() and arg.argumentOf(call, i) and @@ -3950,7 +3934,7 @@ private module FlowExploration { } private predicate partialPathIntoCallable( - PartialPathNodeFwd mid, ParameterNodeExt p, CallContext outercc, CallContextCall innercc, + PartialPathNodeFwd mid, ParamNode p, CallContext outercc, CallContextCall innercc, TSummaryCtx1 sc1, TSummaryCtx2 sc2, DataFlowCall call, PartialAccessPath ap, Configuration config ) { @@ -3987,7 +3971,7 @@ private module FlowExploration { DataFlowCall call, PartialPathNodeFwd mid, ReturnKindExt kind, CallContext cc, PartialAccessPath ap, Configuration config ) { - exists(ParameterNodeExt p, CallContext innercc, TSummaryCtx1 sc1, TSummaryCtx2 sc2 | + exists(ParamNode p, CallContext innercc, TSummaryCtx1 sc1, TSummaryCtx2 sc2 | partialPathIntoCallable(mid, p, cc, innercc, sc1, sc2, call, _, config) and paramFlowsThroughInPartialPath(kind, innercc, sc1, sc2, ap, config) ) @@ -4044,7 +4028,7 @@ private module FlowExploration { apConsRev(ap, c, ap0, config) ) or - exists(ParameterNodeExt p | + exists(ParamNode p | mid.getNode() = p and viableParamArg(_, p, node) and sc1 = mid.getSummaryCtx1() and @@ -4122,7 +4106,7 @@ private module FlowExploration { int pos, TRevSummaryCtx1Some sc1, TRevSummaryCtx2Some sc2, RevPartialAccessPath ap, Configuration config ) { - exists(PartialPathNodeRev mid, ParameterNodeExt p | + exists(PartialPathNodeRev mid, ParamNode p | mid.getNode() = p and p.isParameterOf(_, pos) and sc1 = mid.getSummaryCtx1() and @@ -4145,7 +4129,7 @@ private module FlowExploration { pragma[nomagic] private predicate revPartialPathThroughCallable( - PartialPathNodeRev mid, ArgumentNodeExt node, RevPartialAccessPath ap, Configuration config + PartialPathNodeRev mid, ArgNode node, RevPartialAccessPath ap, Configuration config ) { exists(DataFlowCall call, int pos | revPartialPathThroughCallable0(call, mid, pos, ap, config) and 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 b5ceabc605d..b8e08408911 100644 --- a/java/ql/src/semmle/code/java/dataflow/internal/DataFlowImplCommon.qll +++ b/java/ql/src/semmle/code/java/dataflow/internal/DataFlowImplCommon.qll @@ -35,24 +35,22 @@ predicate accessPathCostLimits(int apLimit, int tupleLimit) { * calls. For this reason, we cannot reuse the code from `DataFlowImpl.qll` directly. */ private module LambdaFlow { - private predicate viableParamNonLambda(DataFlowCall call, int i, ParameterNodeExt p) { + private predicate viableParamNonLambda(DataFlowCall call, int i, ParamNode p) { p.isParameterOf(viableCallable(call), i) } - private predicate viableParamLambda(DataFlowCall call, int i, ParameterNodeExt p) { + private predicate viableParamLambda(DataFlowCall call, int i, ParamNode p) { p.isParameterOf(viableCallableLambda(call, _), i) } - private predicate viableParamArgNonLambda( - DataFlowCall call, ParameterNodeExt p, ArgumentNodeExt arg - ) { + private predicate viableParamArgNonLambda(DataFlowCall call, ParamNode p, ArgNode arg) { exists(int i | viableParamNonLambda(call, i, p) and arg.argumentOf(call, i) ) } - private predicate viableParamArgLambda(DataFlowCall call, ParameterNodeExt p, ArgumentNodeExt arg) { + private predicate viableParamArgLambda(DataFlowCall call, ParamNode p, ArgNode arg) { exists(int i | viableParamLambda(call, i, p) and arg.argumentOf(call, i) @@ -120,7 +118,7 @@ private module LambdaFlow { boolean toJump, DataFlowCallOption lastCall ) { revLambdaFlow0(lambdaCall, kind, node, t, toReturn, toJump, lastCall) and - if castNode(node) or node instanceof ArgumentNodeExt or node instanceof ReturnNode + if castNode(node) or node instanceof ArgNode or node instanceof ReturnNode then compatibleTypes(t, getNodeDataFlowType(node)) else any() } @@ -178,7 +176,7 @@ private module LambdaFlow { ) or // flow into a callable - exists(ParameterNodeExt p, DataFlowCallOption lastCall0, DataFlowCall call | + exists(ParamNode p, DataFlowCallOption lastCall0, DataFlowCall call | revLambdaFlowIn(lambdaCall, kind, p, t, toJump, lastCall0) and ( if lastCall0 = TDataFlowCallNone() and toJump = false @@ -229,8 +227,8 @@ private module LambdaFlow { pragma[nomagic] predicate revLambdaFlowIn( - DataFlowCall lambdaCall, LambdaCallKind kind, ParameterNodeExt p, DataFlowType t, - boolean toJump, DataFlowCallOption lastCall + DataFlowCall lambdaCall, LambdaCallKind kind, ParamNode p, DataFlowType t, boolean toJump, + DataFlowCallOption lastCall ) { revLambdaFlow(lambdaCall, kind, p, t, false, toJump, lastCall) } @@ -276,7 +274,7 @@ private module Cached { predicate outNodeExt(Node n) { n instanceof OutNode or - n.(PostUpdateNode).getPreUpdateNode() instanceof ArgumentNodeExt + n.(PostUpdateNode).getPreUpdateNode() instanceof ArgNode } cached @@ -286,7 +284,7 @@ private module Cached { OutNodeExt getAnOutNodeExt(DataFlowCall call, ReturnKindExt k) { result = getAnOutNode(call, k.(ValueReturnKind).getKind()) or - exists(ArgumentNodeExt arg | + exists(ArgNode arg | result.(PostUpdateNode).getPreUpdateNode() = arg and arg.argumentOf(call, k.(ParamUpdateReturnKind).getPosition()) ) @@ -296,7 +294,7 @@ private module Cached { predicate returnNodeExt(Node n, ReturnKindExt k) { k = TValueReturn(n.(ReturnNode).getKind()) or - exists(ParameterNodeExt p, int pos | + exists(ParamNode p, int pos | parameterValueFlowsToPreUpdate(p, n) and p.isParameterOf(_, pos) and k = TParamUpdate(pos) @@ -309,7 +307,7 @@ private module Cached { cached predicate castingNode(Node n) { castNode(n) or - n instanceof ParameterNodeExt or + n instanceof ParamNode or n instanceof OutNodeExt or // For reads, `x.f`, we want to check that the tracked type after the read (which // is obtained by popping the head of the access path stack) is compatible with @@ -346,7 +344,7 @@ private module Cached { * The instance parameter is considered to have index `-1`. */ pragma[nomagic] - private predicate viableParam(DataFlowCall call, int i, ParameterNodeExt p) { + private predicate viableParam(DataFlowCall call, int i, ParamNode p) { p.isParameterOf(viableCallableExt(call), i) } @@ -355,7 +353,7 @@ private module Cached { * dispatch into account. */ cached - predicate viableParamArg(DataFlowCall call, ParameterNodeExt p, ArgumentNodeExt arg) { + predicate viableParamArg(DataFlowCall call, ParamNode p, ArgNode arg) { exists(int i | viableParam(call, i, p) and arg.argumentOf(call, i) and @@ -397,7 +395,7 @@ private module Cached { * `read` indicates whether it is contents of `p` that can flow to `node`. */ pragma[nomagic] - private predicate parameterValueFlowCand(ParameterNodeExt p, Node node, boolean read) { + private predicate parameterValueFlowCand(ParamNode p, Node node, boolean read) { p = node and read = false or @@ -415,27 +413,25 @@ private module Cached { ) or // flow through: no prior read - exists(ArgumentNodeExt arg | + exists(ArgNode arg | parameterValueFlowArgCand(p, arg, false) and argumentValueFlowsThroughCand(arg, node, read) ) or // flow through: no read inside method - exists(ArgumentNodeExt arg | + exists(ArgNode arg | parameterValueFlowArgCand(p, arg, read) and argumentValueFlowsThroughCand(arg, node, false) ) } pragma[nomagic] - private predicate parameterValueFlowArgCand( - ParameterNodeExt p, ArgumentNodeExt arg, boolean read - ) { + private predicate parameterValueFlowArgCand(ParamNode p, ArgNode arg, boolean read) { parameterValueFlowCand(p, arg, read) } pragma[nomagic] - predicate parameterValueFlowsToPreUpdateCand(ParameterNodeExt p, PostUpdateNode n) { + predicate parameterValueFlowsToPreUpdateCand(ParamNode p, PostUpdateNode n) { parameterValueFlowCand(p, n.getPreUpdateNode(), false) } @@ -447,7 +443,7 @@ private module Cached { * `read` indicates whether it is contents of `p` that can flow to the return * node. */ - predicate parameterValueFlowReturnCand(ParameterNodeExt p, ReturnKind kind, boolean read) { + predicate parameterValueFlowReturnCand(ParamNode p, ReturnKind kind, boolean read) { exists(ReturnNode ret | parameterValueFlowCand(p, ret, read) and kind = ret.getKind() @@ -456,9 +452,9 @@ private module Cached { pragma[nomagic] private predicate argumentValueFlowsThroughCand0( - DataFlowCall call, ArgumentNodeExt arg, ReturnKind kind, boolean read + DataFlowCall call, ArgNode arg, ReturnKind kind, boolean read ) { - exists(ParameterNodeExt param | viableParamArg(call, param, arg) | + exists(ParamNode param | viableParamArg(call, param, arg) | parameterValueFlowReturnCand(param, kind, read) ) } @@ -469,14 +465,14 @@ private module Cached { * * `read` indicates whether it is contents of `arg` that can flow to `out`. */ - predicate argumentValueFlowsThroughCand(ArgumentNodeExt arg, Node out, boolean read) { + predicate argumentValueFlowsThroughCand(ArgNode arg, Node out, boolean read) { exists(DataFlowCall call, ReturnKind kind | argumentValueFlowsThroughCand0(call, arg, kind, read) and out = getAnOutNode(call, kind) ) } - predicate cand(ParameterNodeExt p, Node n) { + predicate cand(ParamNode p, Node n) { parameterValueFlowCand(p, n, _) and ( parameterValueFlowReturnCand(p, _, _) @@ -503,7 +499,7 @@ private module Cached { * If a read step was taken, then `read` captures the `Content`, the * container type, and the content type. */ - predicate parameterValueFlow(ParameterNodeExt p, Node node, ReadStepTypesOption read) { + predicate parameterValueFlow(ParamNode p, Node node, ReadStepTypesOption read) { parameterValueFlow0(p, node, read) and if node instanceof CastingNode then @@ -517,7 +513,7 @@ private module Cached { } pragma[nomagic] - private predicate parameterValueFlow0(ParameterNodeExt p, Node node, ReadStepTypesOption read) { + private predicate parameterValueFlow0(ParamNode p, Node node, ReadStepTypesOption read) { p = node and Cand::cand(p, _) and read = TReadStepTypesNone() @@ -542,34 +538,32 @@ private module Cached { pragma[nomagic] private predicate parameterValueFlow0_0( - ReadStepTypesOption mustBeNone, ParameterNodeExt p, Node node, ReadStepTypesOption read + ReadStepTypesOption mustBeNone, ParamNode p, Node node, ReadStepTypesOption read ) { // flow through: no prior read - exists(ArgumentNodeExt arg | + exists(ArgNode arg | parameterValueFlowArg(p, arg, mustBeNone) and argumentValueFlowsThrough(arg, read, node) ) or // flow through: no read inside method - exists(ArgumentNodeExt arg | + exists(ArgNode arg | parameterValueFlowArg(p, arg, read) and argumentValueFlowsThrough(arg, mustBeNone, node) ) } pragma[nomagic] - private predicate parameterValueFlowArg( - ParameterNodeExt p, ArgumentNodeExt arg, ReadStepTypesOption read - ) { + private predicate parameterValueFlowArg(ParamNode p, ArgNode arg, ReadStepTypesOption read) { parameterValueFlow(p, arg, read) and Cand::argumentValueFlowsThroughCand(arg, _, _) } pragma[nomagic] private predicate argumentValueFlowsThrough0( - DataFlowCall call, ArgumentNodeExt arg, ReturnKind kind, ReadStepTypesOption read + DataFlowCall call, ArgNode arg, ReturnKind kind, ReadStepTypesOption read ) { - exists(ParameterNodeExt param | viableParamArg(call, param, arg) | + exists(ParamNode param | viableParamArg(call, param, arg) | parameterValueFlowReturn(param, kind, read) ) } @@ -583,7 +577,7 @@ private module Cached { * container type, and the content type. */ pragma[nomagic] - predicate argumentValueFlowsThrough(ArgumentNodeExt arg, ReadStepTypesOption read, Node out) { + predicate argumentValueFlowsThrough(ArgNode arg, ReadStepTypesOption read, Node out) { exists(DataFlowCall call, ReturnKind kind | argumentValueFlowsThrough0(call, arg, kind, read) and out = getAnOutNode(call, kind) @@ -603,7 +597,7 @@ private module Cached { * value-preserving steps and a single read step, not taking call * contexts into account, thus representing a getter-step. */ - predicate getterStep(ArgumentNodeExt arg, Content c, Node out) { + predicate getterStep(ArgNode arg, Content c, Node out) { argumentValueFlowsThrough(arg, TReadStepTypesSome(_, c, _), out) } @@ -616,7 +610,7 @@ private module Cached { * container type, and the content type. */ private predicate parameterValueFlowReturn( - ParameterNodeExt p, ReturnKind kind, ReadStepTypesOption read + ParamNode p, ReturnKind kind, ReadStepTypesOption read ) { exists(ReturnNode ret | parameterValueFlow(p, ret, read) and @@ -722,7 +716,7 @@ private module Cached { * Holds if `p` can flow to the pre-update node associated with post-update * node `n`, in the same callable, using only value-preserving steps. */ - private predicate parameterValueFlowsToPreUpdate(ParameterNodeExt p, PostUpdateNode n) { + private predicate parameterValueFlowsToPreUpdate(ParamNode p, PostUpdateNode n) { parameterValueFlow(p, n.getPreUpdateNode(), TReadStepTypesNone()) } @@ -778,8 +772,8 @@ private module Cached { // Does the language-specific simpleLocalFlowStep already model flow // from function input to output? fromPre = getAnOutNode(c, _) and - toPre.(ArgumentNodeExt).argumentOf(c, _) and - simpleLocalFlowStep(toPre.(ArgumentNodeExt), fromPre) + toPre.(ArgNode).argumentOf(c, _) and + simpleLocalFlowStep(toPre.(ArgNode), fromPre) ) or argumentValueFlowsThrough(toPre, TReadStepTypesNone(), fromPre) @@ -827,7 +821,7 @@ private module Cached { cached newtype TReturnKindExt = TValueReturn(ReturnKind kind) or - TParamUpdate(int pos) { exists(ParameterNodeExt p | p.isParameterOf(_, pos)) } + TParamUpdate(int pos) { exists(ParamNode p | p.isParameterOf(_, pos)) } cached newtype TBooleanOption = @@ -942,7 +936,7 @@ class CallContextSomeCall extends CallContextCall, TSomeCall { override string toString() { result = "CcSomeCall" } override predicate relevantFor(DataFlowCallable callable) { - exists(ParameterNodeExt p | getNodeEnclosingCallable(p) = callable) + exists(ParamNode p | getNodeEnclosingCallable(p) = callable) } override predicate matchesCall(DataFlowCall call) { any() } @@ -1005,8 +999,8 @@ LocalCallContext getLocalCallContext(CallContext ctx, DataFlowCallable callable) * The value of a parameter at function entry, viewed as a node in a data * flow graph. */ -class ParameterNodeExt extends Node { - ParameterNodeExt() { parameterNode(this, _, _) } +class ParamNode extends Node { + ParamNode() { parameterNode(this, _, _) } /** * Holds if this node is the parameter of callable `c` at the specified @@ -1016,8 +1010,8 @@ class ParameterNodeExt extends Node { } /** A data-flow node that represents a call argument. */ -class ArgumentNodeExt extends Node { - ArgumentNodeExt() { argumentNode(this, _, _) } +class ArgNode extends Node { + ArgNode() { argumentNode(this, _, _) } /** Holds if this argument occurs at the given position in the given call. */ final predicate argumentOf(DataFlowCall call, int pos) { argumentNode(this, call, pos) } diff --git a/python/ql/src/semmle/python/dataflow/new/internal/DataFlowImpl.qll b/python/ql/src/semmle/python/dataflow/new/internal/DataFlowImpl.qll index cf83530bcbd..058d66b1496 100644 --- a/python/ql/src/semmle/python/dataflow/new/internal/DataFlowImpl.qll +++ b/python/ql/src/semmle/python/dataflow/new/internal/DataFlowImpl.qll @@ -385,7 +385,7 @@ private module Stage1 { */ pragma[nomagic] private predicate fwdFlowIsEntered(DataFlowCall call, Cc cc, Configuration config) { - exists(ArgumentNodeExt arg | + exists(ArgNode arg | fwdFlow(arg, cc, config) and viableParamArg(call, _, arg) ) @@ -512,24 +512,22 @@ private module Stage1 { pragma[nomagic] predicate viableParamArgNodeCandFwd1( - DataFlowCall call, ParameterNodeExt p, ArgumentNodeExt arg, Configuration config + DataFlowCall call, ParamNode p, ArgNode arg, Configuration config ) { viableParamArg(call, p, arg) and fwdFlow(arg, config) } pragma[nomagic] - private predicate revFlowIn( - DataFlowCall call, ArgumentNodeExt arg, boolean toReturn, Configuration config - ) { - exists(ParameterNodeExt p | + private predicate revFlowIn(DataFlowCall call, ArgNode arg, boolean toReturn, Configuration config) { + exists(ParamNode p | revFlow(p, toReturn, config) and viableParamArgNodeCandFwd1(call, p, arg, config) ) } pragma[nomagic] - private predicate revFlowInToReturn(DataFlowCall call, ArgumentNodeExt arg, Configuration config) { + private predicate revFlowInToReturn(DataFlowCall call, ArgNode arg, Configuration config) { revFlowIn(call, arg, true, config) } @@ -594,9 +592,7 @@ private module Stage1 { * Holds if flow may enter through `p` and reach a return node making `p` a * candidate for the origin of a summary. */ - predicate parameterMayFlowThrough( - ParameterNodeExt p, DataFlowCallable c, Ap ap, Configuration config - ) { + predicate parameterMayFlowThrough(ParamNode p, DataFlowCallable c, Ap ap, Configuration config) { exists(ReturnKindExt kind | throughFlowNodeCand(p, config) and returnFlowCallableNodeCand(c, kind, config) and @@ -662,7 +658,7 @@ private predicate flowOutOfCallNodeCand1( pragma[nomagic] private predicate viableParamArgNodeCand1( - DataFlowCall call, ParameterNodeExt p, ArgumentNodeExt arg, Configuration config + DataFlowCall call, ParamNode p, ArgNode arg, Configuration config ) { Stage1::viableParamArgNodeCandFwd1(call, p, arg, config) and Stage1::revFlow(arg, config) @@ -674,7 +670,7 @@ private predicate viableParamArgNodeCand1( */ pragma[nomagic] private predicate flowIntoCallNodeCand1( - DataFlowCall call, ArgumentNodeExt arg, ParameterNodeExt p, Configuration config + DataFlowCall call, ArgNode arg, ParamNode p, Configuration config ) { viableParamArgNodeCand1(call, p, arg, config) and Stage1::revFlow(p, config) and @@ -734,8 +730,7 @@ private predicate flowOutOfCallNodeCand1( */ pragma[nomagic] private predicate flowIntoCallNodeCand1( - DataFlowCall call, ArgumentNodeExt arg, ParameterNodeExt p, boolean allowsFieldFlow, - Configuration config + DataFlowCall call, ArgNode arg, ParamNode p, boolean allowsFieldFlow, Configuration config ) { flowIntoCallNodeCand1(call, arg, p, config) and exists(int b, int j | @@ -943,10 +938,10 @@ private module Stage2 { pragma[nomagic] private predicate fwdFlowIn( - DataFlowCall call, ParameterNodeExt p, Cc outercc, Cc innercc, ApOption argAp, Ap ap, + DataFlowCall call, ParamNode p, Cc outercc, Cc innercc, ApOption argAp, Ap ap, Configuration config ) { - exists(ArgumentNodeExt arg, boolean allowsFieldFlow | + exists(ArgNode arg, boolean allowsFieldFlow | fwdFlow(arg, outercc, argAp, ap, config) and flowIntoCall(call, arg, p, allowsFieldFlow, config) and innercc = getCallContextCall(call, getNodeEnclosingCallable(p), outercc) @@ -991,7 +986,7 @@ private module Stage2 { private predicate fwdFlowIsEntered( DataFlowCall call, Cc cc, ApOption argAp, Ap ap, Configuration config ) { - exists(ParameterNodeExt p | + exists(ParamNode p | fwdFlowIn(call, p, cc, _, argAp, ap, config) and PrevStage::parameterMayFlowThrough(p, _, getApprox(ap), config) ) @@ -1132,10 +1127,9 @@ private module Stage2 { pragma[nomagic] private predicate revFlowIn( - DataFlowCall call, ArgumentNodeExt arg, boolean toReturn, ApOption returnAp, Ap ap, - Configuration config + DataFlowCall call, ArgNode arg, boolean toReturn, ApOption returnAp, Ap ap, Configuration config ) { - exists(ParameterNodeExt p, boolean allowsFieldFlow | + exists(ParamNode p, boolean allowsFieldFlow | revFlow(p, toReturn, returnAp, ap, config) and flowIntoCall(call, arg, p, allowsFieldFlow, config) | @@ -1145,7 +1139,7 @@ private module Stage2 { pragma[nomagic] private predicate revFlowInToReturn( - DataFlowCall call, ArgumentNodeExt arg, Ap returnAp, Ap ap, Configuration config + DataFlowCall call, ArgNode arg, Ap returnAp, Ap ap, Configuration config ) { revFlowIn(call, arg, true, apSome(returnAp), ap, config) } @@ -1198,15 +1192,13 @@ private module Stage2 { pragma[noinline] private predicate parameterFlow( - ParameterNodeExt p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config + ParamNode p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config ) { revFlow(p, true, apSome(ap0), ap, config) and c = getNodeEnclosingCallable(p) } - predicate parameterMayFlowThrough( - ParameterNodeExt p, DataFlowCallable c, Ap ap, Configuration config - ) { + predicate parameterMayFlowThrough(ParamNode p, DataFlowCallable c, Ap ap, Configuration config) { exists(ReturnNodeExt ret, Ap ap0, ReturnKindExt kind, int pos | parameterFlow(p, ap, ap0, c, config) and c = getNodeEnclosingCallable(ret) and @@ -1246,8 +1238,7 @@ private predicate flowOutOfCallNodeCand2( pragma[nomagic] private predicate flowIntoCallNodeCand2( - DataFlowCall call, ArgumentNodeExt node1, ParameterNodeExt node2, boolean allowsFieldFlow, - Configuration config + DataFlowCall call, ArgNode node1, ParamNode node2, boolean allowsFieldFlow, Configuration config ) { flowIntoCallNodeCand1(call, node1, node2, allowsFieldFlow, config) and Stage2::revFlow(node2, pragma[only_bind_into](config)) and @@ -1276,7 +1267,7 @@ private module LocalFlowBigStep { config.isSource(node) or jumpStep(_, node, config) or additionalJumpStep(_, node, config) or - node instanceof ParameterNodeExt or + node instanceof ParamNode or node instanceof OutNodeExt or store(_, _, node, _) or read(_, _, node) or @@ -1586,10 +1577,10 @@ private module Stage3 { pragma[nomagic] private predicate fwdFlowIn( - DataFlowCall call, ParameterNodeExt p, Cc outercc, Cc innercc, ApOption argAp, Ap ap, + DataFlowCall call, ParamNode p, Cc outercc, Cc innercc, ApOption argAp, Ap ap, Configuration config ) { - exists(ArgumentNodeExt arg, boolean allowsFieldFlow | + exists(ArgNode arg, boolean allowsFieldFlow | fwdFlow(arg, outercc, argAp, ap, config) and flowIntoCall(call, arg, p, allowsFieldFlow, config) and innercc = getCallContextCall(call, getNodeEnclosingCallable(p), outercc) @@ -1634,7 +1625,7 @@ private module Stage3 { private predicate fwdFlowIsEntered( DataFlowCall call, Cc cc, ApOption argAp, Ap ap, Configuration config ) { - exists(ParameterNodeExt p | + exists(ParamNode p | fwdFlowIn(call, p, cc, _, argAp, ap, config) and PrevStage::parameterMayFlowThrough(p, _, unbindApa(getApprox(ap)), config) ) @@ -1775,10 +1766,9 @@ private module Stage3 { pragma[nomagic] private predicate revFlowIn( - DataFlowCall call, ArgumentNodeExt arg, boolean toReturn, ApOption returnAp, Ap ap, - Configuration config + DataFlowCall call, ArgNode arg, boolean toReturn, ApOption returnAp, Ap ap, Configuration config ) { - exists(ParameterNodeExt p, boolean allowsFieldFlow | + exists(ParamNode p, boolean allowsFieldFlow | revFlow(p, toReturn, returnAp, ap, config) and flowIntoCall(call, arg, p, allowsFieldFlow, config) | @@ -1788,7 +1778,7 @@ private module Stage3 { pragma[nomagic] private predicate revFlowInToReturn( - DataFlowCall call, ArgumentNodeExt arg, Ap returnAp, Ap ap, Configuration config + DataFlowCall call, ArgNode arg, Ap returnAp, Ap ap, Configuration config ) { revFlowIn(call, arg, true, apSome(returnAp), ap, config) } @@ -1841,15 +1831,13 @@ private module Stage3 { pragma[noinline] private predicate parameterFlow( - ParameterNodeExt p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config + ParamNode p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config ) { revFlow(p, true, apSome(ap0), ap, config) and c = getNodeEnclosingCallable(p) } - predicate parameterMayFlowThrough( - ParameterNodeExt p, DataFlowCallable c, Ap ap, Configuration config - ) { + predicate parameterMayFlowThrough(ParamNode p, DataFlowCallable c, Ap ap, Configuration config) { exists(ReturnNodeExt ret, Ap ap0, ReturnKindExt kind, int pos | parameterFlow(p, ap, ap0, c, config) and c = getNodeEnclosingCallable(ret) and @@ -2160,8 +2148,7 @@ private module Stage4 { pragma[nomagic] private predicate flowIntoCall( - DataFlowCall call, ArgumentNodeExt node1, ParameterNodeExt node2, boolean allowsFieldFlow, - Configuration config + DataFlowCall call, ArgNode node1, ParamNode node2, boolean allowsFieldFlow, Configuration config ) { flowIntoCallNodeCand2(call, node1, node2, allowsFieldFlow, config) and PrevStage::revFlow(node2, _, _, _, pragma[only_bind_into](config)) and @@ -2305,10 +2292,10 @@ private module Stage4 { pragma[nomagic] private predicate fwdFlowIn( - DataFlowCall call, ParameterNodeExt p, Cc outercc, Cc innercc, ApOption argAp, Ap ap, + DataFlowCall call, ParamNode p, Cc outercc, Cc innercc, ApOption argAp, Ap ap, Configuration config ) { - exists(ArgumentNodeExt arg, boolean allowsFieldFlow | + exists(ArgNode arg, boolean allowsFieldFlow | fwdFlow(arg, outercc, argAp, ap, config) and flowIntoCall(call, arg, p, allowsFieldFlow, config) and innercc = getCallContextCall(call, getNodeEnclosingCallable(p), outercc) @@ -2353,7 +2340,7 @@ private module Stage4 { private predicate fwdFlowIsEntered( DataFlowCall call, Cc cc, ApOption argAp, Ap ap, Configuration config ) { - exists(ParameterNodeExt p | + exists(ParamNode p | fwdFlowIn(call, p, cc, _, argAp, ap, config) and PrevStage::parameterMayFlowThrough(p, _, unbindApa(getApprox(ap)), config) ) @@ -2494,10 +2481,9 @@ private module Stage4 { pragma[nomagic] private predicate revFlowIn( - DataFlowCall call, ArgumentNodeExt arg, boolean toReturn, ApOption returnAp, Ap ap, - Configuration config + DataFlowCall call, ArgNode arg, boolean toReturn, ApOption returnAp, Ap ap, Configuration config ) { - exists(ParameterNodeExt p, boolean allowsFieldFlow | + exists(ParamNode p, boolean allowsFieldFlow | revFlow(p, toReturn, returnAp, ap, config) and flowIntoCall(call, arg, p, allowsFieldFlow, config) | @@ -2507,7 +2493,7 @@ private module Stage4 { pragma[nomagic] private predicate revFlowInToReturn( - DataFlowCall call, ArgumentNodeExt arg, Ap returnAp, Ap ap, Configuration config + DataFlowCall call, ArgNode arg, Ap returnAp, Ap ap, Configuration config ) { revFlowIn(call, arg, true, apSome(returnAp), ap, config) } @@ -2560,15 +2546,13 @@ private module Stage4 { pragma[noinline] private predicate parameterFlow( - ParameterNodeExt p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config + ParamNode p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config ) { revFlow(p, true, apSome(ap0), ap, config) and c = getNodeEnclosingCallable(p) } - predicate parameterMayFlowThrough( - ParameterNodeExt p, DataFlowCallable c, Ap ap, Configuration config - ) { + predicate parameterMayFlowThrough(ParamNode p, DataFlowCallable c, Ap ap, Configuration config) { exists(ReturnNodeExt ret, Ap ap0, ReturnKindExt kind, int pos | parameterFlow(p, ap, ap0, c, config) and c = getNodeEnclosingCallable(ret) and @@ -2613,7 +2597,7 @@ private predicate nodeMayUseSummary(Node n, AccessPathApprox apa, Configuration private newtype TSummaryCtx = TSummaryCtxNone() or - TSummaryCtxSome(ParameterNodeExt p, AccessPath ap) { + TSummaryCtxSome(ParamNode p, AccessPath ap) { Stage4::parameterMayFlowThrough(p, _, ap.getApprox(), _) } @@ -2634,7 +2618,7 @@ private class SummaryCtxNone extends SummaryCtx, TSummaryCtxNone { /** A summary context from which a flow summary can be generated. */ private class SummaryCtxSome extends SummaryCtx, TSummaryCtxSome { - private ParameterNodeExt p; + private ParamNode p; private AccessPath ap; SummaryCtxSome() { this = TSummaryCtxSome(p, ap) } @@ -3242,7 +3226,7 @@ pragma[noinline] private predicate pathIntoArg( PathNodeMid mid, int i, CallContext cc, DataFlowCall call, AccessPath ap, AccessPathApprox apa ) { - exists(ArgumentNodeExt arg | + exists(ArgNode arg | arg = mid.getNode() and cc = mid.getCallContext() and arg.argumentOf(call, i) and @@ -3255,7 +3239,7 @@ pragma[noinline] private predicate parameterCand( DataFlowCallable callable, int i, AccessPathApprox apa, Configuration config ) { - exists(ParameterNodeExt p | + exists(ParamNode p | Stage4::revFlow(p, _, _, apa, config) and p.isParameterOf(callable, i) ) @@ -3279,7 +3263,7 @@ private predicate pathIntoCallable0( * respectively. */ private predicate pathIntoCallable( - PathNodeMid mid, ParameterNodeExt p, CallContext outercc, CallContextCall innercc, SummaryCtx sc, + PathNodeMid mid, ParamNode p, CallContext outercc, CallContextCall innercc, SummaryCtx sc, DataFlowCall call ) { exists(int i, DataFlowCallable callable, AccessPath ap | @@ -3575,7 +3559,7 @@ private module FlowExploration { private newtype TSummaryCtx1 = TSummaryCtx1None() or - TSummaryCtx1Param(ParameterNodeExt p) + TSummaryCtx1Param(ParamNode p) private newtype TSummaryCtx2 = TSummaryCtx2None() or @@ -3931,7 +3915,7 @@ private module FlowExploration { PartialPathNodeFwd mid, int i, CallContext cc, DataFlowCall call, PartialAccessPath ap, Configuration config ) { - exists(ArgumentNodeExt arg | + exists(ArgNode arg | arg = mid.getNode() and cc = mid.getCallContext() and arg.argumentOf(call, i) and @@ -3950,7 +3934,7 @@ private module FlowExploration { } private predicate partialPathIntoCallable( - PartialPathNodeFwd mid, ParameterNodeExt p, CallContext outercc, CallContextCall innercc, + PartialPathNodeFwd mid, ParamNode p, CallContext outercc, CallContextCall innercc, TSummaryCtx1 sc1, TSummaryCtx2 sc2, DataFlowCall call, PartialAccessPath ap, Configuration config ) { @@ -3987,7 +3971,7 @@ private module FlowExploration { DataFlowCall call, PartialPathNodeFwd mid, ReturnKindExt kind, CallContext cc, PartialAccessPath ap, Configuration config ) { - exists(ParameterNodeExt p, CallContext innercc, TSummaryCtx1 sc1, TSummaryCtx2 sc2 | + exists(ParamNode p, CallContext innercc, TSummaryCtx1 sc1, TSummaryCtx2 sc2 | partialPathIntoCallable(mid, p, cc, innercc, sc1, sc2, call, _, config) and paramFlowsThroughInPartialPath(kind, innercc, sc1, sc2, ap, config) ) @@ -4044,7 +4028,7 @@ private module FlowExploration { apConsRev(ap, c, ap0, config) ) or - exists(ParameterNodeExt p | + exists(ParamNode p | mid.getNode() = p and viableParamArg(_, p, node) and sc1 = mid.getSummaryCtx1() and @@ -4122,7 +4106,7 @@ private module FlowExploration { int pos, TRevSummaryCtx1Some sc1, TRevSummaryCtx2Some sc2, RevPartialAccessPath ap, Configuration config ) { - exists(PartialPathNodeRev mid, ParameterNodeExt p | + exists(PartialPathNodeRev mid, ParamNode p | mid.getNode() = p and p.isParameterOf(_, pos) and sc1 = mid.getSummaryCtx1() and @@ -4145,7 +4129,7 @@ private module FlowExploration { pragma[nomagic] private predicate revPartialPathThroughCallable( - PartialPathNodeRev mid, ArgumentNodeExt node, RevPartialAccessPath ap, Configuration config + PartialPathNodeRev mid, ArgNode node, RevPartialAccessPath ap, Configuration config ) { exists(DataFlowCall call, int pos | revPartialPathThroughCallable0(call, mid, pos, ap, config) and diff --git a/python/ql/src/semmle/python/dataflow/new/internal/DataFlowImpl2.qll b/python/ql/src/semmle/python/dataflow/new/internal/DataFlowImpl2.qll index cf83530bcbd..058d66b1496 100644 --- a/python/ql/src/semmle/python/dataflow/new/internal/DataFlowImpl2.qll +++ b/python/ql/src/semmle/python/dataflow/new/internal/DataFlowImpl2.qll @@ -385,7 +385,7 @@ private module Stage1 { */ pragma[nomagic] private predicate fwdFlowIsEntered(DataFlowCall call, Cc cc, Configuration config) { - exists(ArgumentNodeExt arg | + exists(ArgNode arg | fwdFlow(arg, cc, config) and viableParamArg(call, _, arg) ) @@ -512,24 +512,22 @@ private module Stage1 { pragma[nomagic] predicate viableParamArgNodeCandFwd1( - DataFlowCall call, ParameterNodeExt p, ArgumentNodeExt arg, Configuration config + DataFlowCall call, ParamNode p, ArgNode arg, Configuration config ) { viableParamArg(call, p, arg) and fwdFlow(arg, config) } pragma[nomagic] - private predicate revFlowIn( - DataFlowCall call, ArgumentNodeExt arg, boolean toReturn, Configuration config - ) { - exists(ParameterNodeExt p | + private predicate revFlowIn(DataFlowCall call, ArgNode arg, boolean toReturn, Configuration config) { + exists(ParamNode p | revFlow(p, toReturn, config) and viableParamArgNodeCandFwd1(call, p, arg, config) ) } pragma[nomagic] - private predicate revFlowInToReturn(DataFlowCall call, ArgumentNodeExt arg, Configuration config) { + private predicate revFlowInToReturn(DataFlowCall call, ArgNode arg, Configuration config) { revFlowIn(call, arg, true, config) } @@ -594,9 +592,7 @@ private module Stage1 { * Holds if flow may enter through `p` and reach a return node making `p` a * candidate for the origin of a summary. */ - predicate parameterMayFlowThrough( - ParameterNodeExt p, DataFlowCallable c, Ap ap, Configuration config - ) { + predicate parameterMayFlowThrough(ParamNode p, DataFlowCallable c, Ap ap, Configuration config) { exists(ReturnKindExt kind | throughFlowNodeCand(p, config) and returnFlowCallableNodeCand(c, kind, config) and @@ -662,7 +658,7 @@ private predicate flowOutOfCallNodeCand1( pragma[nomagic] private predicate viableParamArgNodeCand1( - DataFlowCall call, ParameterNodeExt p, ArgumentNodeExt arg, Configuration config + DataFlowCall call, ParamNode p, ArgNode arg, Configuration config ) { Stage1::viableParamArgNodeCandFwd1(call, p, arg, config) and Stage1::revFlow(arg, config) @@ -674,7 +670,7 @@ private predicate viableParamArgNodeCand1( */ pragma[nomagic] private predicate flowIntoCallNodeCand1( - DataFlowCall call, ArgumentNodeExt arg, ParameterNodeExt p, Configuration config + DataFlowCall call, ArgNode arg, ParamNode p, Configuration config ) { viableParamArgNodeCand1(call, p, arg, config) and Stage1::revFlow(p, config) and @@ -734,8 +730,7 @@ private predicate flowOutOfCallNodeCand1( */ pragma[nomagic] private predicate flowIntoCallNodeCand1( - DataFlowCall call, ArgumentNodeExt arg, ParameterNodeExt p, boolean allowsFieldFlow, - Configuration config + DataFlowCall call, ArgNode arg, ParamNode p, boolean allowsFieldFlow, Configuration config ) { flowIntoCallNodeCand1(call, arg, p, config) and exists(int b, int j | @@ -943,10 +938,10 @@ private module Stage2 { pragma[nomagic] private predicate fwdFlowIn( - DataFlowCall call, ParameterNodeExt p, Cc outercc, Cc innercc, ApOption argAp, Ap ap, + DataFlowCall call, ParamNode p, Cc outercc, Cc innercc, ApOption argAp, Ap ap, Configuration config ) { - exists(ArgumentNodeExt arg, boolean allowsFieldFlow | + exists(ArgNode arg, boolean allowsFieldFlow | fwdFlow(arg, outercc, argAp, ap, config) and flowIntoCall(call, arg, p, allowsFieldFlow, config) and innercc = getCallContextCall(call, getNodeEnclosingCallable(p), outercc) @@ -991,7 +986,7 @@ private module Stage2 { private predicate fwdFlowIsEntered( DataFlowCall call, Cc cc, ApOption argAp, Ap ap, Configuration config ) { - exists(ParameterNodeExt p | + exists(ParamNode p | fwdFlowIn(call, p, cc, _, argAp, ap, config) and PrevStage::parameterMayFlowThrough(p, _, getApprox(ap), config) ) @@ -1132,10 +1127,9 @@ private module Stage2 { pragma[nomagic] private predicate revFlowIn( - DataFlowCall call, ArgumentNodeExt arg, boolean toReturn, ApOption returnAp, Ap ap, - Configuration config + DataFlowCall call, ArgNode arg, boolean toReturn, ApOption returnAp, Ap ap, Configuration config ) { - exists(ParameterNodeExt p, boolean allowsFieldFlow | + exists(ParamNode p, boolean allowsFieldFlow | revFlow(p, toReturn, returnAp, ap, config) and flowIntoCall(call, arg, p, allowsFieldFlow, config) | @@ -1145,7 +1139,7 @@ private module Stage2 { pragma[nomagic] private predicate revFlowInToReturn( - DataFlowCall call, ArgumentNodeExt arg, Ap returnAp, Ap ap, Configuration config + DataFlowCall call, ArgNode arg, Ap returnAp, Ap ap, Configuration config ) { revFlowIn(call, arg, true, apSome(returnAp), ap, config) } @@ -1198,15 +1192,13 @@ private module Stage2 { pragma[noinline] private predicate parameterFlow( - ParameterNodeExt p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config + ParamNode p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config ) { revFlow(p, true, apSome(ap0), ap, config) and c = getNodeEnclosingCallable(p) } - predicate parameterMayFlowThrough( - ParameterNodeExt p, DataFlowCallable c, Ap ap, Configuration config - ) { + predicate parameterMayFlowThrough(ParamNode p, DataFlowCallable c, Ap ap, Configuration config) { exists(ReturnNodeExt ret, Ap ap0, ReturnKindExt kind, int pos | parameterFlow(p, ap, ap0, c, config) and c = getNodeEnclosingCallable(ret) and @@ -1246,8 +1238,7 @@ private predicate flowOutOfCallNodeCand2( pragma[nomagic] private predicate flowIntoCallNodeCand2( - DataFlowCall call, ArgumentNodeExt node1, ParameterNodeExt node2, boolean allowsFieldFlow, - Configuration config + DataFlowCall call, ArgNode node1, ParamNode node2, boolean allowsFieldFlow, Configuration config ) { flowIntoCallNodeCand1(call, node1, node2, allowsFieldFlow, config) and Stage2::revFlow(node2, pragma[only_bind_into](config)) and @@ -1276,7 +1267,7 @@ private module LocalFlowBigStep { config.isSource(node) or jumpStep(_, node, config) or additionalJumpStep(_, node, config) or - node instanceof ParameterNodeExt or + node instanceof ParamNode or node instanceof OutNodeExt or store(_, _, node, _) or read(_, _, node) or @@ -1586,10 +1577,10 @@ private module Stage3 { pragma[nomagic] private predicate fwdFlowIn( - DataFlowCall call, ParameterNodeExt p, Cc outercc, Cc innercc, ApOption argAp, Ap ap, + DataFlowCall call, ParamNode p, Cc outercc, Cc innercc, ApOption argAp, Ap ap, Configuration config ) { - exists(ArgumentNodeExt arg, boolean allowsFieldFlow | + exists(ArgNode arg, boolean allowsFieldFlow | fwdFlow(arg, outercc, argAp, ap, config) and flowIntoCall(call, arg, p, allowsFieldFlow, config) and innercc = getCallContextCall(call, getNodeEnclosingCallable(p), outercc) @@ -1634,7 +1625,7 @@ private module Stage3 { private predicate fwdFlowIsEntered( DataFlowCall call, Cc cc, ApOption argAp, Ap ap, Configuration config ) { - exists(ParameterNodeExt p | + exists(ParamNode p | fwdFlowIn(call, p, cc, _, argAp, ap, config) and PrevStage::parameterMayFlowThrough(p, _, unbindApa(getApprox(ap)), config) ) @@ -1775,10 +1766,9 @@ private module Stage3 { pragma[nomagic] private predicate revFlowIn( - DataFlowCall call, ArgumentNodeExt arg, boolean toReturn, ApOption returnAp, Ap ap, - Configuration config + DataFlowCall call, ArgNode arg, boolean toReturn, ApOption returnAp, Ap ap, Configuration config ) { - exists(ParameterNodeExt p, boolean allowsFieldFlow | + exists(ParamNode p, boolean allowsFieldFlow | revFlow(p, toReturn, returnAp, ap, config) and flowIntoCall(call, arg, p, allowsFieldFlow, config) | @@ -1788,7 +1778,7 @@ private module Stage3 { pragma[nomagic] private predicate revFlowInToReturn( - DataFlowCall call, ArgumentNodeExt arg, Ap returnAp, Ap ap, Configuration config + DataFlowCall call, ArgNode arg, Ap returnAp, Ap ap, Configuration config ) { revFlowIn(call, arg, true, apSome(returnAp), ap, config) } @@ -1841,15 +1831,13 @@ private module Stage3 { pragma[noinline] private predicate parameterFlow( - ParameterNodeExt p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config + ParamNode p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config ) { revFlow(p, true, apSome(ap0), ap, config) and c = getNodeEnclosingCallable(p) } - predicate parameterMayFlowThrough( - ParameterNodeExt p, DataFlowCallable c, Ap ap, Configuration config - ) { + predicate parameterMayFlowThrough(ParamNode p, DataFlowCallable c, Ap ap, Configuration config) { exists(ReturnNodeExt ret, Ap ap0, ReturnKindExt kind, int pos | parameterFlow(p, ap, ap0, c, config) and c = getNodeEnclosingCallable(ret) and @@ -2160,8 +2148,7 @@ private module Stage4 { pragma[nomagic] private predicate flowIntoCall( - DataFlowCall call, ArgumentNodeExt node1, ParameterNodeExt node2, boolean allowsFieldFlow, - Configuration config + DataFlowCall call, ArgNode node1, ParamNode node2, boolean allowsFieldFlow, Configuration config ) { flowIntoCallNodeCand2(call, node1, node2, allowsFieldFlow, config) and PrevStage::revFlow(node2, _, _, _, pragma[only_bind_into](config)) and @@ -2305,10 +2292,10 @@ private module Stage4 { pragma[nomagic] private predicate fwdFlowIn( - DataFlowCall call, ParameterNodeExt p, Cc outercc, Cc innercc, ApOption argAp, Ap ap, + DataFlowCall call, ParamNode p, Cc outercc, Cc innercc, ApOption argAp, Ap ap, Configuration config ) { - exists(ArgumentNodeExt arg, boolean allowsFieldFlow | + exists(ArgNode arg, boolean allowsFieldFlow | fwdFlow(arg, outercc, argAp, ap, config) and flowIntoCall(call, arg, p, allowsFieldFlow, config) and innercc = getCallContextCall(call, getNodeEnclosingCallable(p), outercc) @@ -2353,7 +2340,7 @@ private module Stage4 { private predicate fwdFlowIsEntered( DataFlowCall call, Cc cc, ApOption argAp, Ap ap, Configuration config ) { - exists(ParameterNodeExt p | + exists(ParamNode p | fwdFlowIn(call, p, cc, _, argAp, ap, config) and PrevStage::parameterMayFlowThrough(p, _, unbindApa(getApprox(ap)), config) ) @@ -2494,10 +2481,9 @@ private module Stage4 { pragma[nomagic] private predicate revFlowIn( - DataFlowCall call, ArgumentNodeExt arg, boolean toReturn, ApOption returnAp, Ap ap, - Configuration config + DataFlowCall call, ArgNode arg, boolean toReturn, ApOption returnAp, Ap ap, Configuration config ) { - exists(ParameterNodeExt p, boolean allowsFieldFlow | + exists(ParamNode p, boolean allowsFieldFlow | revFlow(p, toReturn, returnAp, ap, config) and flowIntoCall(call, arg, p, allowsFieldFlow, config) | @@ -2507,7 +2493,7 @@ private module Stage4 { pragma[nomagic] private predicate revFlowInToReturn( - DataFlowCall call, ArgumentNodeExt arg, Ap returnAp, Ap ap, Configuration config + DataFlowCall call, ArgNode arg, Ap returnAp, Ap ap, Configuration config ) { revFlowIn(call, arg, true, apSome(returnAp), ap, config) } @@ -2560,15 +2546,13 @@ private module Stage4 { pragma[noinline] private predicate parameterFlow( - ParameterNodeExt p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config + ParamNode p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config ) { revFlow(p, true, apSome(ap0), ap, config) and c = getNodeEnclosingCallable(p) } - predicate parameterMayFlowThrough( - ParameterNodeExt p, DataFlowCallable c, Ap ap, Configuration config - ) { + predicate parameterMayFlowThrough(ParamNode p, DataFlowCallable c, Ap ap, Configuration config) { exists(ReturnNodeExt ret, Ap ap0, ReturnKindExt kind, int pos | parameterFlow(p, ap, ap0, c, config) and c = getNodeEnclosingCallable(ret) and @@ -2613,7 +2597,7 @@ private predicate nodeMayUseSummary(Node n, AccessPathApprox apa, Configuration private newtype TSummaryCtx = TSummaryCtxNone() or - TSummaryCtxSome(ParameterNodeExt p, AccessPath ap) { + TSummaryCtxSome(ParamNode p, AccessPath ap) { Stage4::parameterMayFlowThrough(p, _, ap.getApprox(), _) } @@ -2634,7 +2618,7 @@ private class SummaryCtxNone extends SummaryCtx, TSummaryCtxNone { /** A summary context from which a flow summary can be generated. */ private class SummaryCtxSome extends SummaryCtx, TSummaryCtxSome { - private ParameterNodeExt p; + private ParamNode p; private AccessPath ap; SummaryCtxSome() { this = TSummaryCtxSome(p, ap) } @@ -3242,7 +3226,7 @@ pragma[noinline] private predicate pathIntoArg( PathNodeMid mid, int i, CallContext cc, DataFlowCall call, AccessPath ap, AccessPathApprox apa ) { - exists(ArgumentNodeExt arg | + exists(ArgNode arg | arg = mid.getNode() and cc = mid.getCallContext() and arg.argumentOf(call, i) and @@ -3255,7 +3239,7 @@ pragma[noinline] private predicate parameterCand( DataFlowCallable callable, int i, AccessPathApprox apa, Configuration config ) { - exists(ParameterNodeExt p | + exists(ParamNode p | Stage4::revFlow(p, _, _, apa, config) and p.isParameterOf(callable, i) ) @@ -3279,7 +3263,7 @@ private predicate pathIntoCallable0( * respectively. */ private predicate pathIntoCallable( - PathNodeMid mid, ParameterNodeExt p, CallContext outercc, CallContextCall innercc, SummaryCtx sc, + PathNodeMid mid, ParamNode p, CallContext outercc, CallContextCall innercc, SummaryCtx sc, DataFlowCall call ) { exists(int i, DataFlowCallable callable, AccessPath ap | @@ -3575,7 +3559,7 @@ private module FlowExploration { private newtype TSummaryCtx1 = TSummaryCtx1None() or - TSummaryCtx1Param(ParameterNodeExt p) + TSummaryCtx1Param(ParamNode p) private newtype TSummaryCtx2 = TSummaryCtx2None() or @@ -3931,7 +3915,7 @@ private module FlowExploration { PartialPathNodeFwd mid, int i, CallContext cc, DataFlowCall call, PartialAccessPath ap, Configuration config ) { - exists(ArgumentNodeExt arg | + exists(ArgNode arg | arg = mid.getNode() and cc = mid.getCallContext() and arg.argumentOf(call, i) and @@ -3950,7 +3934,7 @@ private module FlowExploration { } private predicate partialPathIntoCallable( - PartialPathNodeFwd mid, ParameterNodeExt p, CallContext outercc, CallContextCall innercc, + PartialPathNodeFwd mid, ParamNode p, CallContext outercc, CallContextCall innercc, TSummaryCtx1 sc1, TSummaryCtx2 sc2, DataFlowCall call, PartialAccessPath ap, Configuration config ) { @@ -3987,7 +3971,7 @@ private module FlowExploration { DataFlowCall call, PartialPathNodeFwd mid, ReturnKindExt kind, CallContext cc, PartialAccessPath ap, Configuration config ) { - exists(ParameterNodeExt p, CallContext innercc, TSummaryCtx1 sc1, TSummaryCtx2 sc2 | + exists(ParamNode p, CallContext innercc, TSummaryCtx1 sc1, TSummaryCtx2 sc2 | partialPathIntoCallable(mid, p, cc, innercc, sc1, sc2, call, _, config) and paramFlowsThroughInPartialPath(kind, innercc, sc1, sc2, ap, config) ) @@ -4044,7 +4028,7 @@ private module FlowExploration { apConsRev(ap, c, ap0, config) ) or - exists(ParameterNodeExt p | + exists(ParamNode p | mid.getNode() = p and viableParamArg(_, p, node) and sc1 = mid.getSummaryCtx1() and @@ -4122,7 +4106,7 @@ private module FlowExploration { int pos, TRevSummaryCtx1Some sc1, TRevSummaryCtx2Some sc2, RevPartialAccessPath ap, Configuration config ) { - exists(PartialPathNodeRev mid, ParameterNodeExt p | + exists(PartialPathNodeRev mid, ParamNode p | mid.getNode() = p and p.isParameterOf(_, pos) and sc1 = mid.getSummaryCtx1() and @@ -4145,7 +4129,7 @@ private module FlowExploration { pragma[nomagic] private predicate revPartialPathThroughCallable( - PartialPathNodeRev mid, ArgumentNodeExt node, RevPartialAccessPath ap, Configuration config + PartialPathNodeRev mid, ArgNode node, RevPartialAccessPath ap, Configuration config ) { exists(DataFlowCall call, int pos | revPartialPathThroughCallable0(call, mid, pos, ap, config) and diff --git a/python/ql/src/semmle/python/dataflow/new/internal/DataFlowImpl3.qll b/python/ql/src/semmle/python/dataflow/new/internal/DataFlowImpl3.qll index cf83530bcbd..058d66b1496 100644 --- a/python/ql/src/semmle/python/dataflow/new/internal/DataFlowImpl3.qll +++ b/python/ql/src/semmle/python/dataflow/new/internal/DataFlowImpl3.qll @@ -385,7 +385,7 @@ private module Stage1 { */ pragma[nomagic] private predicate fwdFlowIsEntered(DataFlowCall call, Cc cc, Configuration config) { - exists(ArgumentNodeExt arg | + exists(ArgNode arg | fwdFlow(arg, cc, config) and viableParamArg(call, _, arg) ) @@ -512,24 +512,22 @@ private module Stage1 { pragma[nomagic] predicate viableParamArgNodeCandFwd1( - DataFlowCall call, ParameterNodeExt p, ArgumentNodeExt arg, Configuration config + DataFlowCall call, ParamNode p, ArgNode arg, Configuration config ) { viableParamArg(call, p, arg) and fwdFlow(arg, config) } pragma[nomagic] - private predicate revFlowIn( - DataFlowCall call, ArgumentNodeExt arg, boolean toReturn, Configuration config - ) { - exists(ParameterNodeExt p | + private predicate revFlowIn(DataFlowCall call, ArgNode arg, boolean toReturn, Configuration config) { + exists(ParamNode p | revFlow(p, toReturn, config) and viableParamArgNodeCandFwd1(call, p, arg, config) ) } pragma[nomagic] - private predicate revFlowInToReturn(DataFlowCall call, ArgumentNodeExt arg, Configuration config) { + private predicate revFlowInToReturn(DataFlowCall call, ArgNode arg, Configuration config) { revFlowIn(call, arg, true, config) } @@ -594,9 +592,7 @@ private module Stage1 { * Holds if flow may enter through `p` and reach a return node making `p` a * candidate for the origin of a summary. */ - predicate parameterMayFlowThrough( - ParameterNodeExt p, DataFlowCallable c, Ap ap, Configuration config - ) { + predicate parameterMayFlowThrough(ParamNode p, DataFlowCallable c, Ap ap, Configuration config) { exists(ReturnKindExt kind | throughFlowNodeCand(p, config) and returnFlowCallableNodeCand(c, kind, config) and @@ -662,7 +658,7 @@ private predicate flowOutOfCallNodeCand1( pragma[nomagic] private predicate viableParamArgNodeCand1( - DataFlowCall call, ParameterNodeExt p, ArgumentNodeExt arg, Configuration config + DataFlowCall call, ParamNode p, ArgNode arg, Configuration config ) { Stage1::viableParamArgNodeCandFwd1(call, p, arg, config) and Stage1::revFlow(arg, config) @@ -674,7 +670,7 @@ private predicate viableParamArgNodeCand1( */ pragma[nomagic] private predicate flowIntoCallNodeCand1( - DataFlowCall call, ArgumentNodeExt arg, ParameterNodeExt p, Configuration config + DataFlowCall call, ArgNode arg, ParamNode p, Configuration config ) { viableParamArgNodeCand1(call, p, arg, config) and Stage1::revFlow(p, config) and @@ -734,8 +730,7 @@ private predicate flowOutOfCallNodeCand1( */ pragma[nomagic] private predicate flowIntoCallNodeCand1( - DataFlowCall call, ArgumentNodeExt arg, ParameterNodeExt p, boolean allowsFieldFlow, - Configuration config + DataFlowCall call, ArgNode arg, ParamNode p, boolean allowsFieldFlow, Configuration config ) { flowIntoCallNodeCand1(call, arg, p, config) and exists(int b, int j | @@ -943,10 +938,10 @@ private module Stage2 { pragma[nomagic] private predicate fwdFlowIn( - DataFlowCall call, ParameterNodeExt p, Cc outercc, Cc innercc, ApOption argAp, Ap ap, + DataFlowCall call, ParamNode p, Cc outercc, Cc innercc, ApOption argAp, Ap ap, Configuration config ) { - exists(ArgumentNodeExt arg, boolean allowsFieldFlow | + exists(ArgNode arg, boolean allowsFieldFlow | fwdFlow(arg, outercc, argAp, ap, config) and flowIntoCall(call, arg, p, allowsFieldFlow, config) and innercc = getCallContextCall(call, getNodeEnclosingCallable(p), outercc) @@ -991,7 +986,7 @@ private module Stage2 { private predicate fwdFlowIsEntered( DataFlowCall call, Cc cc, ApOption argAp, Ap ap, Configuration config ) { - exists(ParameterNodeExt p | + exists(ParamNode p | fwdFlowIn(call, p, cc, _, argAp, ap, config) and PrevStage::parameterMayFlowThrough(p, _, getApprox(ap), config) ) @@ -1132,10 +1127,9 @@ private module Stage2 { pragma[nomagic] private predicate revFlowIn( - DataFlowCall call, ArgumentNodeExt arg, boolean toReturn, ApOption returnAp, Ap ap, - Configuration config + DataFlowCall call, ArgNode arg, boolean toReturn, ApOption returnAp, Ap ap, Configuration config ) { - exists(ParameterNodeExt p, boolean allowsFieldFlow | + exists(ParamNode p, boolean allowsFieldFlow | revFlow(p, toReturn, returnAp, ap, config) and flowIntoCall(call, arg, p, allowsFieldFlow, config) | @@ -1145,7 +1139,7 @@ private module Stage2 { pragma[nomagic] private predicate revFlowInToReturn( - DataFlowCall call, ArgumentNodeExt arg, Ap returnAp, Ap ap, Configuration config + DataFlowCall call, ArgNode arg, Ap returnAp, Ap ap, Configuration config ) { revFlowIn(call, arg, true, apSome(returnAp), ap, config) } @@ -1198,15 +1192,13 @@ private module Stage2 { pragma[noinline] private predicate parameterFlow( - ParameterNodeExt p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config + ParamNode p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config ) { revFlow(p, true, apSome(ap0), ap, config) and c = getNodeEnclosingCallable(p) } - predicate parameterMayFlowThrough( - ParameterNodeExt p, DataFlowCallable c, Ap ap, Configuration config - ) { + predicate parameterMayFlowThrough(ParamNode p, DataFlowCallable c, Ap ap, Configuration config) { exists(ReturnNodeExt ret, Ap ap0, ReturnKindExt kind, int pos | parameterFlow(p, ap, ap0, c, config) and c = getNodeEnclosingCallable(ret) and @@ -1246,8 +1238,7 @@ private predicate flowOutOfCallNodeCand2( pragma[nomagic] private predicate flowIntoCallNodeCand2( - DataFlowCall call, ArgumentNodeExt node1, ParameterNodeExt node2, boolean allowsFieldFlow, - Configuration config + DataFlowCall call, ArgNode node1, ParamNode node2, boolean allowsFieldFlow, Configuration config ) { flowIntoCallNodeCand1(call, node1, node2, allowsFieldFlow, config) and Stage2::revFlow(node2, pragma[only_bind_into](config)) and @@ -1276,7 +1267,7 @@ private module LocalFlowBigStep { config.isSource(node) or jumpStep(_, node, config) or additionalJumpStep(_, node, config) or - node instanceof ParameterNodeExt or + node instanceof ParamNode or node instanceof OutNodeExt or store(_, _, node, _) or read(_, _, node) or @@ -1586,10 +1577,10 @@ private module Stage3 { pragma[nomagic] private predicate fwdFlowIn( - DataFlowCall call, ParameterNodeExt p, Cc outercc, Cc innercc, ApOption argAp, Ap ap, + DataFlowCall call, ParamNode p, Cc outercc, Cc innercc, ApOption argAp, Ap ap, Configuration config ) { - exists(ArgumentNodeExt arg, boolean allowsFieldFlow | + exists(ArgNode arg, boolean allowsFieldFlow | fwdFlow(arg, outercc, argAp, ap, config) and flowIntoCall(call, arg, p, allowsFieldFlow, config) and innercc = getCallContextCall(call, getNodeEnclosingCallable(p), outercc) @@ -1634,7 +1625,7 @@ private module Stage3 { private predicate fwdFlowIsEntered( DataFlowCall call, Cc cc, ApOption argAp, Ap ap, Configuration config ) { - exists(ParameterNodeExt p | + exists(ParamNode p | fwdFlowIn(call, p, cc, _, argAp, ap, config) and PrevStage::parameterMayFlowThrough(p, _, unbindApa(getApprox(ap)), config) ) @@ -1775,10 +1766,9 @@ private module Stage3 { pragma[nomagic] private predicate revFlowIn( - DataFlowCall call, ArgumentNodeExt arg, boolean toReturn, ApOption returnAp, Ap ap, - Configuration config + DataFlowCall call, ArgNode arg, boolean toReturn, ApOption returnAp, Ap ap, Configuration config ) { - exists(ParameterNodeExt p, boolean allowsFieldFlow | + exists(ParamNode p, boolean allowsFieldFlow | revFlow(p, toReturn, returnAp, ap, config) and flowIntoCall(call, arg, p, allowsFieldFlow, config) | @@ -1788,7 +1778,7 @@ private module Stage3 { pragma[nomagic] private predicate revFlowInToReturn( - DataFlowCall call, ArgumentNodeExt arg, Ap returnAp, Ap ap, Configuration config + DataFlowCall call, ArgNode arg, Ap returnAp, Ap ap, Configuration config ) { revFlowIn(call, arg, true, apSome(returnAp), ap, config) } @@ -1841,15 +1831,13 @@ private module Stage3 { pragma[noinline] private predicate parameterFlow( - ParameterNodeExt p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config + ParamNode p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config ) { revFlow(p, true, apSome(ap0), ap, config) and c = getNodeEnclosingCallable(p) } - predicate parameterMayFlowThrough( - ParameterNodeExt p, DataFlowCallable c, Ap ap, Configuration config - ) { + predicate parameterMayFlowThrough(ParamNode p, DataFlowCallable c, Ap ap, Configuration config) { exists(ReturnNodeExt ret, Ap ap0, ReturnKindExt kind, int pos | parameterFlow(p, ap, ap0, c, config) and c = getNodeEnclosingCallable(ret) and @@ -2160,8 +2148,7 @@ private module Stage4 { pragma[nomagic] private predicate flowIntoCall( - DataFlowCall call, ArgumentNodeExt node1, ParameterNodeExt node2, boolean allowsFieldFlow, - Configuration config + DataFlowCall call, ArgNode node1, ParamNode node2, boolean allowsFieldFlow, Configuration config ) { flowIntoCallNodeCand2(call, node1, node2, allowsFieldFlow, config) and PrevStage::revFlow(node2, _, _, _, pragma[only_bind_into](config)) and @@ -2305,10 +2292,10 @@ private module Stage4 { pragma[nomagic] private predicate fwdFlowIn( - DataFlowCall call, ParameterNodeExt p, Cc outercc, Cc innercc, ApOption argAp, Ap ap, + DataFlowCall call, ParamNode p, Cc outercc, Cc innercc, ApOption argAp, Ap ap, Configuration config ) { - exists(ArgumentNodeExt arg, boolean allowsFieldFlow | + exists(ArgNode arg, boolean allowsFieldFlow | fwdFlow(arg, outercc, argAp, ap, config) and flowIntoCall(call, arg, p, allowsFieldFlow, config) and innercc = getCallContextCall(call, getNodeEnclosingCallable(p), outercc) @@ -2353,7 +2340,7 @@ private module Stage4 { private predicate fwdFlowIsEntered( DataFlowCall call, Cc cc, ApOption argAp, Ap ap, Configuration config ) { - exists(ParameterNodeExt p | + exists(ParamNode p | fwdFlowIn(call, p, cc, _, argAp, ap, config) and PrevStage::parameterMayFlowThrough(p, _, unbindApa(getApprox(ap)), config) ) @@ -2494,10 +2481,9 @@ private module Stage4 { pragma[nomagic] private predicate revFlowIn( - DataFlowCall call, ArgumentNodeExt arg, boolean toReturn, ApOption returnAp, Ap ap, - Configuration config + DataFlowCall call, ArgNode arg, boolean toReturn, ApOption returnAp, Ap ap, Configuration config ) { - exists(ParameterNodeExt p, boolean allowsFieldFlow | + exists(ParamNode p, boolean allowsFieldFlow | revFlow(p, toReturn, returnAp, ap, config) and flowIntoCall(call, arg, p, allowsFieldFlow, config) | @@ -2507,7 +2493,7 @@ private module Stage4 { pragma[nomagic] private predicate revFlowInToReturn( - DataFlowCall call, ArgumentNodeExt arg, Ap returnAp, Ap ap, Configuration config + DataFlowCall call, ArgNode arg, Ap returnAp, Ap ap, Configuration config ) { revFlowIn(call, arg, true, apSome(returnAp), ap, config) } @@ -2560,15 +2546,13 @@ private module Stage4 { pragma[noinline] private predicate parameterFlow( - ParameterNodeExt p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config + ParamNode p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config ) { revFlow(p, true, apSome(ap0), ap, config) and c = getNodeEnclosingCallable(p) } - predicate parameterMayFlowThrough( - ParameterNodeExt p, DataFlowCallable c, Ap ap, Configuration config - ) { + predicate parameterMayFlowThrough(ParamNode p, DataFlowCallable c, Ap ap, Configuration config) { exists(ReturnNodeExt ret, Ap ap0, ReturnKindExt kind, int pos | parameterFlow(p, ap, ap0, c, config) and c = getNodeEnclosingCallable(ret) and @@ -2613,7 +2597,7 @@ private predicate nodeMayUseSummary(Node n, AccessPathApprox apa, Configuration private newtype TSummaryCtx = TSummaryCtxNone() or - TSummaryCtxSome(ParameterNodeExt p, AccessPath ap) { + TSummaryCtxSome(ParamNode p, AccessPath ap) { Stage4::parameterMayFlowThrough(p, _, ap.getApprox(), _) } @@ -2634,7 +2618,7 @@ private class SummaryCtxNone extends SummaryCtx, TSummaryCtxNone { /** A summary context from which a flow summary can be generated. */ private class SummaryCtxSome extends SummaryCtx, TSummaryCtxSome { - private ParameterNodeExt p; + private ParamNode p; private AccessPath ap; SummaryCtxSome() { this = TSummaryCtxSome(p, ap) } @@ -3242,7 +3226,7 @@ pragma[noinline] private predicate pathIntoArg( PathNodeMid mid, int i, CallContext cc, DataFlowCall call, AccessPath ap, AccessPathApprox apa ) { - exists(ArgumentNodeExt arg | + exists(ArgNode arg | arg = mid.getNode() and cc = mid.getCallContext() and arg.argumentOf(call, i) and @@ -3255,7 +3239,7 @@ pragma[noinline] private predicate parameterCand( DataFlowCallable callable, int i, AccessPathApprox apa, Configuration config ) { - exists(ParameterNodeExt p | + exists(ParamNode p | Stage4::revFlow(p, _, _, apa, config) and p.isParameterOf(callable, i) ) @@ -3279,7 +3263,7 @@ private predicate pathIntoCallable0( * respectively. */ private predicate pathIntoCallable( - PathNodeMid mid, ParameterNodeExt p, CallContext outercc, CallContextCall innercc, SummaryCtx sc, + PathNodeMid mid, ParamNode p, CallContext outercc, CallContextCall innercc, SummaryCtx sc, DataFlowCall call ) { exists(int i, DataFlowCallable callable, AccessPath ap | @@ -3575,7 +3559,7 @@ private module FlowExploration { private newtype TSummaryCtx1 = TSummaryCtx1None() or - TSummaryCtx1Param(ParameterNodeExt p) + TSummaryCtx1Param(ParamNode p) private newtype TSummaryCtx2 = TSummaryCtx2None() or @@ -3931,7 +3915,7 @@ private module FlowExploration { PartialPathNodeFwd mid, int i, CallContext cc, DataFlowCall call, PartialAccessPath ap, Configuration config ) { - exists(ArgumentNodeExt arg | + exists(ArgNode arg | arg = mid.getNode() and cc = mid.getCallContext() and arg.argumentOf(call, i) and @@ -3950,7 +3934,7 @@ private module FlowExploration { } private predicate partialPathIntoCallable( - PartialPathNodeFwd mid, ParameterNodeExt p, CallContext outercc, CallContextCall innercc, + PartialPathNodeFwd mid, ParamNode p, CallContext outercc, CallContextCall innercc, TSummaryCtx1 sc1, TSummaryCtx2 sc2, DataFlowCall call, PartialAccessPath ap, Configuration config ) { @@ -3987,7 +3971,7 @@ private module FlowExploration { DataFlowCall call, PartialPathNodeFwd mid, ReturnKindExt kind, CallContext cc, PartialAccessPath ap, Configuration config ) { - exists(ParameterNodeExt p, CallContext innercc, TSummaryCtx1 sc1, TSummaryCtx2 sc2 | + exists(ParamNode p, CallContext innercc, TSummaryCtx1 sc1, TSummaryCtx2 sc2 | partialPathIntoCallable(mid, p, cc, innercc, sc1, sc2, call, _, config) and paramFlowsThroughInPartialPath(kind, innercc, sc1, sc2, ap, config) ) @@ -4044,7 +4028,7 @@ private module FlowExploration { apConsRev(ap, c, ap0, config) ) or - exists(ParameterNodeExt p | + exists(ParamNode p | mid.getNode() = p and viableParamArg(_, p, node) and sc1 = mid.getSummaryCtx1() and @@ -4122,7 +4106,7 @@ private module FlowExploration { int pos, TRevSummaryCtx1Some sc1, TRevSummaryCtx2Some sc2, RevPartialAccessPath ap, Configuration config ) { - exists(PartialPathNodeRev mid, ParameterNodeExt p | + exists(PartialPathNodeRev mid, ParamNode p | mid.getNode() = p and p.isParameterOf(_, pos) and sc1 = mid.getSummaryCtx1() and @@ -4145,7 +4129,7 @@ private module FlowExploration { pragma[nomagic] private predicate revPartialPathThroughCallable( - PartialPathNodeRev mid, ArgumentNodeExt node, RevPartialAccessPath ap, Configuration config + PartialPathNodeRev mid, ArgNode node, RevPartialAccessPath ap, Configuration config ) { exists(DataFlowCall call, int pos | revPartialPathThroughCallable0(call, mid, pos, ap, config) and diff --git a/python/ql/src/semmle/python/dataflow/new/internal/DataFlowImpl4.qll b/python/ql/src/semmle/python/dataflow/new/internal/DataFlowImpl4.qll index cf83530bcbd..058d66b1496 100644 --- a/python/ql/src/semmle/python/dataflow/new/internal/DataFlowImpl4.qll +++ b/python/ql/src/semmle/python/dataflow/new/internal/DataFlowImpl4.qll @@ -385,7 +385,7 @@ private module Stage1 { */ pragma[nomagic] private predicate fwdFlowIsEntered(DataFlowCall call, Cc cc, Configuration config) { - exists(ArgumentNodeExt arg | + exists(ArgNode arg | fwdFlow(arg, cc, config) and viableParamArg(call, _, arg) ) @@ -512,24 +512,22 @@ private module Stage1 { pragma[nomagic] predicate viableParamArgNodeCandFwd1( - DataFlowCall call, ParameterNodeExt p, ArgumentNodeExt arg, Configuration config + DataFlowCall call, ParamNode p, ArgNode arg, Configuration config ) { viableParamArg(call, p, arg) and fwdFlow(arg, config) } pragma[nomagic] - private predicate revFlowIn( - DataFlowCall call, ArgumentNodeExt arg, boolean toReturn, Configuration config - ) { - exists(ParameterNodeExt p | + private predicate revFlowIn(DataFlowCall call, ArgNode arg, boolean toReturn, Configuration config) { + exists(ParamNode p | revFlow(p, toReturn, config) and viableParamArgNodeCandFwd1(call, p, arg, config) ) } pragma[nomagic] - private predicate revFlowInToReturn(DataFlowCall call, ArgumentNodeExt arg, Configuration config) { + private predicate revFlowInToReturn(DataFlowCall call, ArgNode arg, Configuration config) { revFlowIn(call, arg, true, config) } @@ -594,9 +592,7 @@ private module Stage1 { * Holds if flow may enter through `p` and reach a return node making `p` a * candidate for the origin of a summary. */ - predicate parameterMayFlowThrough( - ParameterNodeExt p, DataFlowCallable c, Ap ap, Configuration config - ) { + predicate parameterMayFlowThrough(ParamNode p, DataFlowCallable c, Ap ap, Configuration config) { exists(ReturnKindExt kind | throughFlowNodeCand(p, config) and returnFlowCallableNodeCand(c, kind, config) and @@ -662,7 +658,7 @@ private predicate flowOutOfCallNodeCand1( pragma[nomagic] private predicate viableParamArgNodeCand1( - DataFlowCall call, ParameterNodeExt p, ArgumentNodeExt arg, Configuration config + DataFlowCall call, ParamNode p, ArgNode arg, Configuration config ) { Stage1::viableParamArgNodeCandFwd1(call, p, arg, config) and Stage1::revFlow(arg, config) @@ -674,7 +670,7 @@ private predicate viableParamArgNodeCand1( */ pragma[nomagic] private predicate flowIntoCallNodeCand1( - DataFlowCall call, ArgumentNodeExt arg, ParameterNodeExt p, Configuration config + DataFlowCall call, ArgNode arg, ParamNode p, Configuration config ) { viableParamArgNodeCand1(call, p, arg, config) and Stage1::revFlow(p, config) and @@ -734,8 +730,7 @@ private predicate flowOutOfCallNodeCand1( */ pragma[nomagic] private predicate flowIntoCallNodeCand1( - DataFlowCall call, ArgumentNodeExt arg, ParameterNodeExt p, boolean allowsFieldFlow, - Configuration config + DataFlowCall call, ArgNode arg, ParamNode p, boolean allowsFieldFlow, Configuration config ) { flowIntoCallNodeCand1(call, arg, p, config) and exists(int b, int j | @@ -943,10 +938,10 @@ private module Stage2 { pragma[nomagic] private predicate fwdFlowIn( - DataFlowCall call, ParameterNodeExt p, Cc outercc, Cc innercc, ApOption argAp, Ap ap, + DataFlowCall call, ParamNode p, Cc outercc, Cc innercc, ApOption argAp, Ap ap, Configuration config ) { - exists(ArgumentNodeExt arg, boolean allowsFieldFlow | + exists(ArgNode arg, boolean allowsFieldFlow | fwdFlow(arg, outercc, argAp, ap, config) and flowIntoCall(call, arg, p, allowsFieldFlow, config) and innercc = getCallContextCall(call, getNodeEnclosingCallable(p), outercc) @@ -991,7 +986,7 @@ private module Stage2 { private predicate fwdFlowIsEntered( DataFlowCall call, Cc cc, ApOption argAp, Ap ap, Configuration config ) { - exists(ParameterNodeExt p | + exists(ParamNode p | fwdFlowIn(call, p, cc, _, argAp, ap, config) and PrevStage::parameterMayFlowThrough(p, _, getApprox(ap), config) ) @@ -1132,10 +1127,9 @@ private module Stage2 { pragma[nomagic] private predicate revFlowIn( - DataFlowCall call, ArgumentNodeExt arg, boolean toReturn, ApOption returnAp, Ap ap, - Configuration config + DataFlowCall call, ArgNode arg, boolean toReturn, ApOption returnAp, Ap ap, Configuration config ) { - exists(ParameterNodeExt p, boolean allowsFieldFlow | + exists(ParamNode p, boolean allowsFieldFlow | revFlow(p, toReturn, returnAp, ap, config) and flowIntoCall(call, arg, p, allowsFieldFlow, config) | @@ -1145,7 +1139,7 @@ private module Stage2 { pragma[nomagic] private predicate revFlowInToReturn( - DataFlowCall call, ArgumentNodeExt arg, Ap returnAp, Ap ap, Configuration config + DataFlowCall call, ArgNode arg, Ap returnAp, Ap ap, Configuration config ) { revFlowIn(call, arg, true, apSome(returnAp), ap, config) } @@ -1198,15 +1192,13 @@ private module Stage2 { pragma[noinline] private predicate parameterFlow( - ParameterNodeExt p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config + ParamNode p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config ) { revFlow(p, true, apSome(ap0), ap, config) and c = getNodeEnclosingCallable(p) } - predicate parameterMayFlowThrough( - ParameterNodeExt p, DataFlowCallable c, Ap ap, Configuration config - ) { + predicate parameterMayFlowThrough(ParamNode p, DataFlowCallable c, Ap ap, Configuration config) { exists(ReturnNodeExt ret, Ap ap0, ReturnKindExt kind, int pos | parameterFlow(p, ap, ap0, c, config) and c = getNodeEnclosingCallable(ret) and @@ -1246,8 +1238,7 @@ private predicate flowOutOfCallNodeCand2( pragma[nomagic] private predicate flowIntoCallNodeCand2( - DataFlowCall call, ArgumentNodeExt node1, ParameterNodeExt node2, boolean allowsFieldFlow, - Configuration config + DataFlowCall call, ArgNode node1, ParamNode node2, boolean allowsFieldFlow, Configuration config ) { flowIntoCallNodeCand1(call, node1, node2, allowsFieldFlow, config) and Stage2::revFlow(node2, pragma[only_bind_into](config)) and @@ -1276,7 +1267,7 @@ private module LocalFlowBigStep { config.isSource(node) or jumpStep(_, node, config) or additionalJumpStep(_, node, config) or - node instanceof ParameterNodeExt or + node instanceof ParamNode or node instanceof OutNodeExt or store(_, _, node, _) or read(_, _, node) or @@ -1586,10 +1577,10 @@ private module Stage3 { pragma[nomagic] private predicate fwdFlowIn( - DataFlowCall call, ParameterNodeExt p, Cc outercc, Cc innercc, ApOption argAp, Ap ap, + DataFlowCall call, ParamNode p, Cc outercc, Cc innercc, ApOption argAp, Ap ap, Configuration config ) { - exists(ArgumentNodeExt arg, boolean allowsFieldFlow | + exists(ArgNode arg, boolean allowsFieldFlow | fwdFlow(arg, outercc, argAp, ap, config) and flowIntoCall(call, arg, p, allowsFieldFlow, config) and innercc = getCallContextCall(call, getNodeEnclosingCallable(p), outercc) @@ -1634,7 +1625,7 @@ private module Stage3 { private predicate fwdFlowIsEntered( DataFlowCall call, Cc cc, ApOption argAp, Ap ap, Configuration config ) { - exists(ParameterNodeExt p | + exists(ParamNode p | fwdFlowIn(call, p, cc, _, argAp, ap, config) and PrevStage::parameterMayFlowThrough(p, _, unbindApa(getApprox(ap)), config) ) @@ -1775,10 +1766,9 @@ private module Stage3 { pragma[nomagic] private predicate revFlowIn( - DataFlowCall call, ArgumentNodeExt arg, boolean toReturn, ApOption returnAp, Ap ap, - Configuration config + DataFlowCall call, ArgNode arg, boolean toReturn, ApOption returnAp, Ap ap, Configuration config ) { - exists(ParameterNodeExt p, boolean allowsFieldFlow | + exists(ParamNode p, boolean allowsFieldFlow | revFlow(p, toReturn, returnAp, ap, config) and flowIntoCall(call, arg, p, allowsFieldFlow, config) | @@ -1788,7 +1778,7 @@ private module Stage3 { pragma[nomagic] private predicate revFlowInToReturn( - DataFlowCall call, ArgumentNodeExt arg, Ap returnAp, Ap ap, Configuration config + DataFlowCall call, ArgNode arg, Ap returnAp, Ap ap, Configuration config ) { revFlowIn(call, arg, true, apSome(returnAp), ap, config) } @@ -1841,15 +1831,13 @@ private module Stage3 { pragma[noinline] private predicate parameterFlow( - ParameterNodeExt p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config + ParamNode p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config ) { revFlow(p, true, apSome(ap0), ap, config) and c = getNodeEnclosingCallable(p) } - predicate parameterMayFlowThrough( - ParameterNodeExt p, DataFlowCallable c, Ap ap, Configuration config - ) { + predicate parameterMayFlowThrough(ParamNode p, DataFlowCallable c, Ap ap, Configuration config) { exists(ReturnNodeExt ret, Ap ap0, ReturnKindExt kind, int pos | parameterFlow(p, ap, ap0, c, config) and c = getNodeEnclosingCallable(ret) and @@ -2160,8 +2148,7 @@ private module Stage4 { pragma[nomagic] private predicate flowIntoCall( - DataFlowCall call, ArgumentNodeExt node1, ParameterNodeExt node2, boolean allowsFieldFlow, - Configuration config + DataFlowCall call, ArgNode node1, ParamNode node2, boolean allowsFieldFlow, Configuration config ) { flowIntoCallNodeCand2(call, node1, node2, allowsFieldFlow, config) and PrevStage::revFlow(node2, _, _, _, pragma[only_bind_into](config)) and @@ -2305,10 +2292,10 @@ private module Stage4 { pragma[nomagic] private predicate fwdFlowIn( - DataFlowCall call, ParameterNodeExt p, Cc outercc, Cc innercc, ApOption argAp, Ap ap, + DataFlowCall call, ParamNode p, Cc outercc, Cc innercc, ApOption argAp, Ap ap, Configuration config ) { - exists(ArgumentNodeExt arg, boolean allowsFieldFlow | + exists(ArgNode arg, boolean allowsFieldFlow | fwdFlow(arg, outercc, argAp, ap, config) and flowIntoCall(call, arg, p, allowsFieldFlow, config) and innercc = getCallContextCall(call, getNodeEnclosingCallable(p), outercc) @@ -2353,7 +2340,7 @@ private module Stage4 { private predicate fwdFlowIsEntered( DataFlowCall call, Cc cc, ApOption argAp, Ap ap, Configuration config ) { - exists(ParameterNodeExt p | + exists(ParamNode p | fwdFlowIn(call, p, cc, _, argAp, ap, config) and PrevStage::parameterMayFlowThrough(p, _, unbindApa(getApprox(ap)), config) ) @@ -2494,10 +2481,9 @@ private module Stage4 { pragma[nomagic] private predicate revFlowIn( - DataFlowCall call, ArgumentNodeExt arg, boolean toReturn, ApOption returnAp, Ap ap, - Configuration config + DataFlowCall call, ArgNode arg, boolean toReturn, ApOption returnAp, Ap ap, Configuration config ) { - exists(ParameterNodeExt p, boolean allowsFieldFlow | + exists(ParamNode p, boolean allowsFieldFlow | revFlow(p, toReturn, returnAp, ap, config) and flowIntoCall(call, arg, p, allowsFieldFlow, config) | @@ -2507,7 +2493,7 @@ private module Stage4 { pragma[nomagic] private predicate revFlowInToReturn( - DataFlowCall call, ArgumentNodeExt arg, Ap returnAp, Ap ap, Configuration config + DataFlowCall call, ArgNode arg, Ap returnAp, Ap ap, Configuration config ) { revFlowIn(call, arg, true, apSome(returnAp), ap, config) } @@ -2560,15 +2546,13 @@ private module Stage4 { pragma[noinline] private predicate parameterFlow( - ParameterNodeExt p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config + ParamNode p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config ) { revFlow(p, true, apSome(ap0), ap, config) and c = getNodeEnclosingCallable(p) } - predicate parameterMayFlowThrough( - ParameterNodeExt p, DataFlowCallable c, Ap ap, Configuration config - ) { + predicate parameterMayFlowThrough(ParamNode p, DataFlowCallable c, Ap ap, Configuration config) { exists(ReturnNodeExt ret, Ap ap0, ReturnKindExt kind, int pos | parameterFlow(p, ap, ap0, c, config) and c = getNodeEnclosingCallable(ret) and @@ -2613,7 +2597,7 @@ private predicate nodeMayUseSummary(Node n, AccessPathApprox apa, Configuration private newtype TSummaryCtx = TSummaryCtxNone() or - TSummaryCtxSome(ParameterNodeExt p, AccessPath ap) { + TSummaryCtxSome(ParamNode p, AccessPath ap) { Stage4::parameterMayFlowThrough(p, _, ap.getApprox(), _) } @@ -2634,7 +2618,7 @@ private class SummaryCtxNone extends SummaryCtx, TSummaryCtxNone { /** A summary context from which a flow summary can be generated. */ private class SummaryCtxSome extends SummaryCtx, TSummaryCtxSome { - private ParameterNodeExt p; + private ParamNode p; private AccessPath ap; SummaryCtxSome() { this = TSummaryCtxSome(p, ap) } @@ -3242,7 +3226,7 @@ pragma[noinline] private predicate pathIntoArg( PathNodeMid mid, int i, CallContext cc, DataFlowCall call, AccessPath ap, AccessPathApprox apa ) { - exists(ArgumentNodeExt arg | + exists(ArgNode arg | arg = mid.getNode() and cc = mid.getCallContext() and arg.argumentOf(call, i) and @@ -3255,7 +3239,7 @@ pragma[noinline] private predicate parameterCand( DataFlowCallable callable, int i, AccessPathApprox apa, Configuration config ) { - exists(ParameterNodeExt p | + exists(ParamNode p | Stage4::revFlow(p, _, _, apa, config) and p.isParameterOf(callable, i) ) @@ -3279,7 +3263,7 @@ private predicate pathIntoCallable0( * respectively. */ private predicate pathIntoCallable( - PathNodeMid mid, ParameterNodeExt p, CallContext outercc, CallContextCall innercc, SummaryCtx sc, + PathNodeMid mid, ParamNode p, CallContext outercc, CallContextCall innercc, SummaryCtx sc, DataFlowCall call ) { exists(int i, DataFlowCallable callable, AccessPath ap | @@ -3575,7 +3559,7 @@ private module FlowExploration { private newtype TSummaryCtx1 = TSummaryCtx1None() or - TSummaryCtx1Param(ParameterNodeExt p) + TSummaryCtx1Param(ParamNode p) private newtype TSummaryCtx2 = TSummaryCtx2None() or @@ -3931,7 +3915,7 @@ private module FlowExploration { PartialPathNodeFwd mid, int i, CallContext cc, DataFlowCall call, PartialAccessPath ap, Configuration config ) { - exists(ArgumentNodeExt arg | + exists(ArgNode arg | arg = mid.getNode() and cc = mid.getCallContext() and arg.argumentOf(call, i) and @@ -3950,7 +3934,7 @@ private module FlowExploration { } private predicate partialPathIntoCallable( - PartialPathNodeFwd mid, ParameterNodeExt p, CallContext outercc, CallContextCall innercc, + PartialPathNodeFwd mid, ParamNode p, CallContext outercc, CallContextCall innercc, TSummaryCtx1 sc1, TSummaryCtx2 sc2, DataFlowCall call, PartialAccessPath ap, Configuration config ) { @@ -3987,7 +3971,7 @@ private module FlowExploration { DataFlowCall call, PartialPathNodeFwd mid, ReturnKindExt kind, CallContext cc, PartialAccessPath ap, Configuration config ) { - exists(ParameterNodeExt p, CallContext innercc, TSummaryCtx1 sc1, TSummaryCtx2 sc2 | + exists(ParamNode p, CallContext innercc, TSummaryCtx1 sc1, TSummaryCtx2 sc2 | partialPathIntoCallable(mid, p, cc, innercc, sc1, sc2, call, _, config) and paramFlowsThroughInPartialPath(kind, innercc, sc1, sc2, ap, config) ) @@ -4044,7 +4028,7 @@ private module FlowExploration { apConsRev(ap, c, ap0, config) ) or - exists(ParameterNodeExt p | + exists(ParamNode p | mid.getNode() = p and viableParamArg(_, p, node) and sc1 = mid.getSummaryCtx1() and @@ -4122,7 +4106,7 @@ private module FlowExploration { int pos, TRevSummaryCtx1Some sc1, TRevSummaryCtx2Some sc2, RevPartialAccessPath ap, Configuration config ) { - exists(PartialPathNodeRev mid, ParameterNodeExt p | + exists(PartialPathNodeRev mid, ParamNode p | mid.getNode() = p and p.isParameterOf(_, pos) and sc1 = mid.getSummaryCtx1() and @@ -4145,7 +4129,7 @@ private module FlowExploration { pragma[nomagic] private predicate revPartialPathThroughCallable( - PartialPathNodeRev mid, ArgumentNodeExt node, RevPartialAccessPath ap, Configuration config + PartialPathNodeRev mid, ArgNode node, RevPartialAccessPath ap, Configuration config ) { exists(DataFlowCall call, int pos | revPartialPathThroughCallable0(call, mid, pos, ap, config) and diff --git a/python/ql/src/semmle/python/dataflow/new/internal/DataFlowImplCommon.qll b/python/ql/src/semmle/python/dataflow/new/internal/DataFlowImplCommon.qll index b5ceabc605d..b8e08408911 100644 --- a/python/ql/src/semmle/python/dataflow/new/internal/DataFlowImplCommon.qll +++ b/python/ql/src/semmle/python/dataflow/new/internal/DataFlowImplCommon.qll @@ -35,24 +35,22 @@ predicate accessPathCostLimits(int apLimit, int tupleLimit) { * calls. For this reason, we cannot reuse the code from `DataFlowImpl.qll` directly. */ private module LambdaFlow { - private predicate viableParamNonLambda(DataFlowCall call, int i, ParameterNodeExt p) { + private predicate viableParamNonLambda(DataFlowCall call, int i, ParamNode p) { p.isParameterOf(viableCallable(call), i) } - private predicate viableParamLambda(DataFlowCall call, int i, ParameterNodeExt p) { + private predicate viableParamLambda(DataFlowCall call, int i, ParamNode p) { p.isParameterOf(viableCallableLambda(call, _), i) } - private predicate viableParamArgNonLambda( - DataFlowCall call, ParameterNodeExt p, ArgumentNodeExt arg - ) { + private predicate viableParamArgNonLambda(DataFlowCall call, ParamNode p, ArgNode arg) { exists(int i | viableParamNonLambda(call, i, p) and arg.argumentOf(call, i) ) } - private predicate viableParamArgLambda(DataFlowCall call, ParameterNodeExt p, ArgumentNodeExt arg) { + private predicate viableParamArgLambda(DataFlowCall call, ParamNode p, ArgNode arg) { exists(int i | viableParamLambda(call, i, p) and arg.argumentOf(call, i) @@ -120,7 +118,7 @@ private module LambdaFlow { boolean toJump, DataFlowCallOption lastCall ) { revLambdaFlow0(lambdaCall, kind, node, t, toReturn, toJump, lastCall) and - if castNode(node) or node instanceof ArgumentNodeExt or node instanceof ReturnNode + if castNode(node) or node instanceof ArgNode or node instanceof ReturnNode then compatibleTypes(t, getNodeDataFlowType(node)) else any() } @@ -178,7 +176,7 @@ private module LambdaFlow { ) or // flow into a callable - exists(ParameterNodeExt p, DataFlowCallOption lastCall0, DataFlowCall call | + exists(ParamNode p, DataFlowCallOption lastCall0, DataFlowCall call | revLambdaFlowIn(lambdaCall, kind, p, t, toJump, lastCall0) and ( if lastCall0 = TDataFlowCallNone() and toJump = false @@ -229,8 +227,8 @@ private module LambdaFlow { pragma[nomagic] predicate revLambdaFlowIn( - DataFlowCall lambdaCall, LambdaCallKind kind, ParameterNodeExt p, DataFlowType t, - boolean toJump, DataFlowCallOption lastCall + DataFlowCall lambdaCall, LambdaCallKind kind, ParamNode p, DataFlowType t, boolean toJump, + DataFlowCallOption lastCall ) { revLambdaFlow(lambdaCall, kind, p, t, false, toJump, lastCall) } @@ -276,7 +274,7 @@ private module Cached { predicate outNodeExt(Node n) { n instanceof OutNode or - n.(PostUpdateNode).getPreUpdateNode() instanceof ArgumentNodeExt + n.(PostUpdateNode).getPreUpdateNode() instanceof ArgNode } cached @@ -286,7 +284,7 @@ private module Cached { OutNodeExt getAnOutNodeExt(DataFlowCall call, ReturnKindExt k) { result = getAnOutNode(call, k.(ValueReturnKind).getKind()) or - exists(ArgumentNodeExt arg | + exists(ArgNode arg | result.(PostUpdateNode).getPreUpdateNode() = arg and arg.argumentOf(call, k.(ParamUpdateReturnKind).getPosition()) ) @@ -296,7 +294,7 @@ private module Cached { predicate returnNodeExt(Node n, ReturnKindExt k) { k = TValueReturn(n.(ReturnNode).getKind()) or - exists(ParameterNodeExt p, int pos | + exists(ParamNode p, int pos | parameterValueFlowsToPreUpdate(p, n) and p.isParameterOf(_, pos) and k = TParamUpdate(pos) @@ -309,7 +307,7 @@ private module Cached { cached predicate castingNode(Node n) { castNode(n) or - n instanceof ParameterNodeExt or + n instanceof ParamNode or n instanceof OutNodeExt or // For reads, `x.f`, we want to check that the tracked type after the read (which // is obtained by popping the head of the access path stack) is compatible with @@ -346,7 +344,7 @@ private module Cached { * The instance parameter is considered to have index `-1`. */ pragma[nomagic] - private predicate viableParam(DataFlowCall call, int i, ParameterNodeExt p) { + private predicate viableParam(DataFlowCall call, int i, ParamNode p) { p.isParameterOf(viableCallableExt(call), i) } @@ -355,7 +353,7 @@ private module Cached { * dispatch into account. */ cached - predicate viableParamArg(DataFlowCall call, ParameterNodeExt p, ArgumentNodeExt arg) { + predicate viableParamArg(DataFlowCall call, ParamNode p, ArgNode arg) { exists(int i | viableParam(call, i, p) and arg.argumentOf(call, i) and @@ -397,7 +395,7 @@ private module Cached { * `read` indicates whether it is contents of `p` that can flow to `node`. */ pragma[nomagic] - private predicate parameterValueFlowCand(ParameterNodeExt p, Node node, boolean read) { + private predicate parameterValueFlowCand(ParamNode p, Node node, boolean read) { p = node and read = false or @@ -415,27 +413,25 @@ private module Cached { ) or // flow through: no prior read - exists(ArgumentNodeExt arg | + exists(ArgNode arg | parameterValueFlowArgCand(p, arg, false) and argumentValueFlowsThroughCand(arg, node, read) ) or // flow through: no read inside method - exists(ArgumentNodeExt arg | + exists(ArgNode arg | parameterValueFlowArgCand(p, arg, read) and argumentValueFlowsThroughCand(arg, node, false) ) } pragma[nomagic] - private predicate parameterValueFlowArgCand( - ParameterNodeExt p, ArgumentNodeExt arg, boolean read - ) { + private predicate parameterValueFlowArgCand(ParamNode p, ArgNode arg, boolean read) { parameterValueFlowCand(p, arg, read) } pragma[nomagic] - predicate parameterValueFlowsToPreUpdateCand(ParameterNodeExt p, PostUpdateNode n) { + predicate parameterValueFlowsToPreUpdateCand(ParamNode p, PostUpdateNode n) { parameterValueFlowCand(p, n.getPreUpdateNode(), false) } @@ -447,7 +443,7 @@ private module Cached { * `read` indicates whether it is contents of `p` that can flow to the return * node. */ - predicate parameterValueFlowReturnCand(ParameterNodeExt p, ReturnKind kind, boolean read) { + predicate parameterValueFlowReturnCand(ParamNode p, ReturnKind kind, boolean read) { exists(ReturnNode ret | parameterValueFlowCand(p, ret, read) and kind = ret.getKind() @@ -456,9 +452,9 @@ private module Cached { pragma[nomagic] private predicate argumentValueFlowsThroughCand0( - DataFlowCall call, ArgumentNodeExt arg, ReturnKind kind, boolean read + DataFlowCall call, ArgNode arg, ReturnKind kind, boolean read ) { - exists(ParameterNodeExt param | viableParamArg(call, param, arg) | + exists(ParamNode param | viableParamArg(call, param, arg) | parameterValueFlowReturnCand(param, kind, read) ) } @@ -469,14 +465,14 @@ private module Cached { * * `read` indicates whether it is contents of `arg` that can flow to `out`. */ - predicate argumentValueFlowsThroughCand(ArgumentNodeExt arg, Node out, boolean read) { + predicate argumentValueFlowsThroughCand(ArgNode arg, Node out, boolean read) { exists(DataFlowCall call, ReturnKind kind | argumentValueFlowsThroughCand0(call, arg, kind, read) and out = getAnOutNode(call, kind) ) } - predicate cand(ParameterNodeExt p, Node n) { + predicate cand(ParamNode p, Node n) { parameterValueFlowCand(p, n, _) and ( parameterValueFlowReturnCand(p, _, _) @@ -503,7 +499,7 @@ private module Cached { * If a read step was taken, then `read` captures the `Content`, the * container type, and the content type. */ - predicate parameterValueFlow(ParameterNodeExt p, Node node, ReadStepTypesOption read) { + predicate parameterValueFlow(ParamNode p, Node node, ReadStepTypesOption read) { parameterValueFlow0(p, node, read) and if node instanceof CastingNode then @@ -517,7 +513,7 @@ private module Cached { } pragma[nomagic] - private predicate parameterValueFlow0(ParameterNodeExt p, Node node, ReadStepTypesOption read) { + private predicate parameterValueFlow0(ParamNode p, Node node, ReadStepTypesOption read) { p = node and Cand::cand(p, _) and read = TReadStepTypesNone() @@ -542,34 +538,32 @@ private module Cached { pragma[nomagic] private predicate parameterValueFlow0_0( - ReadStepTypesOption mustBeNone, ParameterNodeExt p, Node node, ReadStepTypesOption read + ReadStepTypesOption mustBeNone, ParamNode p, Node node, ReadStepTypesOption read ) { // flow through: no prior read - exists(ArgumentNodeExt arg | + exists(ArgNode arg | parameterValueFlowArg(p, arg, mustBeNone) and argumentValueFlowsThrough(arg, read, node) ) or // flow through: no read inside method - exists(ArgumentNodeExt arg | + exists(ArgNode arg | parameterValueFlowArg(p, arg, read) and argumentValueFlowsThrough(arg, mustBeNone, node) ) } pragma[nomagic] - private predicate parameterValueFlowArg( - ParameterNodeExt p, ArgumentNodeExt arg, ReadStepTypesOption read - ) { + private predicate parameterValueFlowArg(ParamNode p, ArgNode arg, ReadStepTypesOption read) { parameterValueFlow(p, arg, read) and Cand::argumentValueFlowsThroughCand(arg, _, _) } pragma[nomagic] private predicate argumentValueFlowsThrough0( - DataFlowCall call, ArgumentNodeExt arg, ReturnKind kind, ReadStepTypesOption read + DataFlowCall call, ArgNode arg, ReturnKind kind, ReadStepTypesOption read ) { - exists(ParameterNodeExt param | viableParamArg(call, param, arg) | + exists(ParamNode param | viableParamArg(call, param, arg) | parameterValueFlowReturn(param, kind, read) ) } @@ -583,7 +577,7 @@ private module Cached { * container type, and the content type. */ pragma[nomagic] - predicate argumentValueFlowsThrough(ArgumentNodeExt arg, ReadStepTypesOption read, Node out) { + predicate argumentValueFlowsThrough(ArgNode arg, ReadStepTypesOption read, Node out) { exists(DataFlowCall call, ReturnKind kind | argumentValueFlowsThrough0(call, arg, kind, read) and out = getAnOutNode(call, kind) @@ -603,7 +597,7 @@ private module Cached { * value-preserving steps and a single read step, not taking call * contexts into account, thus representing a getter-step. */ - predicate getterStep(ArgumentNodeExt arg, Content c, Node out) { + predicate getterStep(ArgNode arg, Content c, Node out) { argumentValueFlowsThrough(arg, TReadStepTypesSome(_, c, _), out) } @@ -616,7 +610,7 @@ private module Cached { * container type, and the content type. */ private predicate parameterValueFlowReturn( - ParameterNodeExt p, ReturnKind kind, ReadStepTypesOption read + ParamNode p, ReturnKind kind, ReadStepTypesOption read ) { exists(ReturnNode ret | parameterValueFlow(p, ret, read) and @@ -722,7 +716,7 @@ private module Cached { * Holds if `p` can flow to the pre-update node associated with post-update * node `n`, in the same callable, using only value-preserving steps. */ - private predicate parameterValueFlowsToPreUpdate(ParameterNodeExt p, PostUpdateNode n) { + private predicate parameterValueFlowsToPreUpdate(ParamNode p, PostUpdateNode n) { parameterValueFlow(p, n.getPreUpdateNode(), TReadStepTypesNone()) } @@ -778,8 +772,8 @@ private module Cached { // Does the language-specific simpleLocalFlowStep already model flow // from function input to output? fromPre = getAnOutNode(c, _) and - toPre.(ArgumentNodeExt).argumentOf(c, _) and - simpleLocalFlowStep(toPre.(ArgumentNodeExt), fromPre) + toPre.(ArgNode).argumentOf(c, _) and + simpleLocalFlowStep(toPre.(ArgNode), fromPre) ) or argumentValueFlowsThrough(toPre, TReadStepTypesNone(), fromPre) @@ -827,7 +821,7 @@ private module Cached { cached newtype TReturnKindExt = TValueReturn(ReturnKind kind) or - TParamUpdate(int pos) { exists(ParameterNodeExt p | p.isParameterOf(_, pos)) } + TParamUpdate(int pos) { exists(ParamNode p | p.isParameterOf(_, pos)) } cached newtype TBooleanOption = @@ -942,7 +936,7 @@ class CallContextSomeCall extends CallContextCall, TSomeCall { override string toString() { result = "CcSomeCall" } override predicate relevantFor(DataFlowCallable callable) { - exists(ParameterNodeExt p | getNodeEnclosingCallable(p) = callable) + exists(ParamNode p | getNodeEnclosingCallable(p) = callable) } override predicate matchesCall(DataFlowCall call) { any() } @@ -1005,8 +999,8 @@ LocalCallContext getLocalCallContext(CallContext ctx, DataFlowCallable callable) * The value of a parameter at function entry, viewed as a node in a data * flow graph. */ -class ParameterNodeExt extends Node { - ParameterNodeExt() { parameterNode(this, _, _) } +class ParamNode extends Node { + ParamNode() { parameterNode(this, _, _) } /** * Holds if this node is the parameter of callable `c` at the specified @@ -1016,8 +1010,8 @@ class ParameterNodeExt extends Node { } /** A data-flow node that represents a call argument. */ -class ArgumentNodeExt extends Node { - ArgumentNodeExt() { argumentNode(this, _, _) } +class ArgNode extends Node { + ArgNode() { argumentNode(this, _, _) } /** Holds if this argument occurs at the given position in the given call. */ final predicate argumentOf(DataFlowCall call, int pos) { argumentNode(this, call, pos) } From 3e21f479a99e3bd5c85299755cc632c1c9da5383 Mon Sep 17 00:00:00 2001 From: Mathias Vorreiter Pedersen Date: Tue, 11 May 2021 14:58:48 +0200 Subject: [PATCH 111/168] C++: Add change-note. --- cpp/change-notes/2021-03-11-overflow-abs.md | 2 ++ 1 file changed, 2 insertions(+) create mode 100644 cpp/change-notes/2021-03-11-overflow-abs.md diff --git a/cpp/change-notes/2021-03-11-overflow-abs.md b/cpp/change-notes/2021-03-11-overflow-abs.md new file mode 100644 index 00000000000..66854412f72 --- /dev/null +++ b/cpp/change-notes/2021-03-11-overflow-abs.md @@ -0,0 +1,2 @@ +lgtm +* The `cpp/tainted-arithmetic`, `cpp/arithmetic-with-extreme-values`, and `cpp/uncontrolled-arithmetic` queries now recognize more functions as returning the absolute value of their input. As a result, they produce fewer false positives. From 56b1f15ddaaeb5b53267293933b3850058678f6c Mon Sep 17 00:00:00 2001 From: Jonathan Leitschuh Date: Fri, 30 Apr 2021 19:18:35 -0400 Subject: [PATCH 112/168] [Java] Add taint tracking through Jackson deserialization --- .../jackson/JacksonSerializability.qll | 19 ++++++++ .../dataflow/taint-jackson/Test.java | 38 +++++++++++++-- .../dataflow/taint-jackson/dataFlow.expected | 48 ------------------- .../dataflow/taint-jackson/dataFlow.ql | 29 ++++++++--- .../jackson/databind/ObjectMapper.java | 4 ++ .../jackson/databind/ObjectReader.java | 45 +++++++++++++++++ 6 files changed, 125 insertions(+), 58 deletions(-) create mode 100644 java/ql/test/stubs/jackson-databind-2.10/com/fasterxml/jackson/databind/ObjectReader.java diff --git a/java/ql/src/semmle/code/java/frameworks/jackson/JacksonSerializability.qll b/java/ql/src/semmle/code/java/frameworks/jackson/JacksonSerializability.qll index 3356e31d965..c028af71d8d 100644 --- a/java/ql/src/semmle/code/java/frameworks/jackson/JacksonSerializability.qll +++ b/java/ql/src/semmle/code/java/frameworks/jackson/JacksonSerializability.qll @@ -50,6 +50,15 @@ library class JacksonWriteValueMethod extends Method, TaintPreservingCallable { } } +library class JacksonReadValueMethod extends Method, TaintPreservingCallable { + JacksonReadValueMethod() { + getDeclaringType().hasQualifiedName("com.fasterxml.jackson.databind", "ObjectReader") and + hasName("readValue") + } + + override predicate returnsTaintFrom(int arg) { arg = 0 } +} + /** A type whose values are explicitly serialized in a call to a Jackson method. */ library class ExplicitlyWrittenJacksonSerializableType extends JacksonSerializableType { ExplicitlyWrittenJacksonSerializableType() { @@ -135,6 +144,16 @@ class JacksonDeserializableField extends DeserializableField { } } +class JacksonDeserializableFieldAccess extends FieldAccess { + JacksonDeserializableFieldAccess() { getField() instanceof JacksonDeserializableField } +} + +class JacksonDeseializedTaintStep extends AdditionalTaintStep { + override predicate step(DataFlow::Node node1, DataFlow::Node node2) { + node2.asExpr().(JacksonDeserializableFieldAccess).getQualifier() = node1.asExpr() + } +} + /** * A call to the `addMixInAnnotations` or `addMixIn` Jackson method. * diff --git a/java/ql/test/library-tests/dataflow/taint-jackson/Test.java b/java/ql/test/library-tests/dataflow/taint-jackson/Test.java index 2caf9e4ee80..16b223ed2e8 100644 --- a/java/ql/test/library-tests/dataflow/taint-jackson/Test.java +++ b/java/ql/test/library-tests/dataflow/taint-jackson/Test.java @@ -8,28 +8,44 @@ import com.fasterxml.jackson.core.JsonFactory; import com.fasterxml.jackson.core.JsonGenerator; import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.ObjectWriter; +import com.fasterxml.jackson.databind.ObjectReader; class Test { + public static class Potato { + private String name; + + private String getName() { + return name; + } + } + public static String taint() { return "tainted"; } + public static void sink(Object any) {} + public static void jacksonObjectMapper() throws java.io.FileNotFoundException, java.io.UnsupportedEncodingException { String s = taint(); ObjectMapper om = new ObjectMapper(); File file = new File("testFile"); om.writeValue(file, s); + sink(file); //$hasTaintFlow OutputStream out = new FileOutputStream(file); om.writeValue(out, s); + sink(file); //$hasTaintFlow Writer writer = new StringWriter(); om.writeValue(writer, s); + sink(writer); //$hasTaintFlow JsonGenerator generator = new JsonFactory().createGenerator(new StringWriter()); om.writeValue(generator, s); + sink(generator); //$hasTaintFlow String t = om.writeValueAsString(s); - System.out.println(t); + sink(t); //$hasTaintFlow byte[] bs = om.writeValueAsBytes(s); String reconstructed = new String(bs, "utf-8"); - System.out.println(reconstructed); + sink(bs); //$hasTaintFlow + sink(reconstructed); //$hasTaintFlow } public static void jacksonObjectWriter() throws java.io.FileNotFoundException, java.io.UnsupportedEncodingException { @@ -37,16 +53,30 @@ class Test { ObjectWriter ow = new ObjectWriter(); File file = new File("testFile"); ow.writeValue(file, s); + sink(file); //$hasTaintFlow OutputStream out = new FileOutputStream(file); ow.writeValue(out, s); + sink(out); //$hasTaintFlow Writer writer = new StringWriter(); ow.writeValue(writer, s); + sink(writer); //$hasTaintFlow JsonGenerator generator = new JsonFactory().createGenerator(new StringWriter()); ow.writeValue(generator, s); + sink(generator); //$hasTaintFlow String t = ow.writeValueAsString(s); - System.out.println(t); + sink(t); //$hasTaintFlow byte[] bs = ow.writeValueAsBytes(s); String reconstructed = new String(bs, "utf-8"); - System.out.println(reconstructed); + sink(bs); //$hasTaintFlow + sink(reconstructed); //$hasTaintFlow + } + + public static void jacksonObjectReader() throws java.io.IOException { + String s = taint(); + ObjectMapper om = new ObjectMapper(); + ObjectReader reader = om.readerFor(Potato.class); + sink(reader.readValue(s)); //$hasTaintFlow + sink(reader.readValue(s, Potato.class).name); //$hasTaintFlow + sink(reader.readValue(s, Potato.class).getName()); //$hasTaintFlow } } diff --git a/java/ql/test/library-tests/dataflow/taint-jackson/dataFlow.expected b/java/ql/test/library-tests/dataflow/taint-jackson/dataFlow.expected index 122b21d50fe..e69de29bb2d 100644 --- a/java/ql/test/library-tests/dataflow/taint-jackson/dataFlow.expected +++ b/java/ql/test/library-tests/dataflow/taint-jackson/dataFlow.expected @@ -1,48 +0,0 @@ -| ../../../stubs/jackson-databind-2.10/com/fasterxml/jackson/databind/ObjectMapper.java:10:43:10:54 | value | -| ../../../stubs/jackson-databind-2.10/com/fasterxml/jackson/databind/ObjectMapper.java:13:73:13:84 | value | -| ../../../stubs/jackson-databind-2.10/com/fasterxml/jackson/databind/ObjectMapper.java:16:44:16:55 | value | -| ../../../stubs/jackson-databind-2.10/com/fasterxml/jackson/databind/ObjectMapper.java:19:36:19:47 | value | -| ../../../stubs/jackson-databind-2.10/com/fasterxml/jackson/databind/ObjectMapper.java:22:35:22:46 | value | -| ../../../stubs/jackson-databind-2.10/com/fasterxml/jackson/databind/ObjectMapper.java:26:36:26:47 | value | -| ../../../stubs/jackson-databind-2.10/com/fasterxml/jackson/databind/ObjectWriter.java:10:43:10:54 | value | -| ../../../stubs/jackson-databind-2.10/com/fasterxml/jackson/databind/ObjectWriter.java:13:73:13:84 | value | -| ../../../stubs/jackson-databind-2.10/com/fasterxml/jackson/databind/ObjectWriter.java:16:44:16:55 | value | -| ../../../stubs/jackson-databind-2.10/com/fasterxml/jackson/databind/ObjectWriter.java:19:36:19:47 | value | -| ../../../stubs/jackson-databind-2.10/com/fasterxml/jackson/databind/ObjectWriter.java:22:35:22:46 | value | -| ../../../stubs/jackson-databind-2.10/com/fasterxml/jackson/databind/ObjectWriter.java:26:36:26:47 | value | -| Test.java:18:14:18:20 | taint(...) | -| Test.java:21:17:21:20 | file [post update] | -| Test.java:21:23:21:23 | s | -| Test.java:22:43:22:46 | file | -| Test.java:23:17:23:19 | out [post update] | -| Test.java:23:22:23:22 | s | -| Test.java:25:17:25:22 | writer [post update] | -| Test.java:25:25:25:25 | s | -| Test.java:27:17:27:25 | generator [post update] | -| Test.java:27:28:27:28 | s | -| Test.java:28:14:28:37 | writeValueAsString(...) | -| Test.java:28:36:28:36 | s | -| Test.java:29:22:29:22 | t | -| Test.java:30:15:30:37 | writeValueAsBytes(...) | -| Test.java:30:36:30:36 | s | -| Test.java:31:26:31:48 | new String(...) | -| Test.java:31:37:31:38 | bs | -| Test.java:32:22:32:34 | reconstructed | -| Test.java:36:14:36:20 | taint(...) | -| Test.java:39:17:39:20 | file [post update] | -| Test.java:39:23:39:23 | s | -| Test.java:40:43:40:46 | file | -| Test.java:41:17:41:19 | out [post update] | -| Test.java:41:22:41:22 | s | -| Test.java:43:17:43:22 | writer [post update] | -| Test.java:43:25:43:25 | s | -| Test.java:45:17:45:25 | generator [post update] | -| Test.java:45:28:45:28 | s | -| Test.java:46:14:46:37 | writeValueAsString(...) | -| Test.java:46:36:46:36 | s | -| Test.java:47:22:47:22 | t | -| Test.java:48:15:48:37 | writeValueAsBytes(...) | -| Test.java:48:36:48:36 | s | -| Test.java:49:26:49:48 | new String(...) | -| Test.java:49:37:49:38 | bs | -| Test.java:50:22:50:34 | reconstructed | diff --git a/java/ql/test/library-tests/dataflow/taint-jackson/dataFlow.ql b/java/ql/test/library-tests/dataflow/taint-jackson/dataFlow.ql index 333cf485f07..3ee51339b7b 100644 --- a/java/ql/test/library-tests/dataflow/taint-jackson/dataFlow.ql +++ b/java/ql/test/library-tests/dataflow/taint-jackson/dataFlow.ql @@ -1,17 +1,34 @@ +import java import semmle.code.java.dataflow.DataFlow import semmle.code.java.dataflow.TaintTracking import semmle.code.java.dataflow.FlowSources +import TestUtilities.InlineExpectationsTest class Conf extends TaintTracking::Configuration { Conf() { this = "qltest:dataflow:jackson" } - override predicate isSource(DataFlow::Node source) { - source.asExpr().(MethodAccess).getMethod().hasName("taint") + override predicate isSource(DataFlow::Node n) { + n.asExpr().(MethodAccess).getMethod().hasName("taint") + or + n instanceof RemoteFlowSource } - override predicate isSink(DataFlow::Node sink) { any() } + override predicate isSink(DataFlow::Node n) { + exists(MethodAccess ma | ma.getMethod().hasName("sink") | n.asExpr() = ma.getAnArgument()) + } } -from DataFlow::Node source, DataFlow::Node sink, Conf config -where config.hasFlow(source, sink) -select sink +class HasFlowTest extends InlineExpectationsTest { + HasFlowTest() { this = "HasFlowTest" } + + override string getARelevantTag() { result = "hasTaintFlow" } + + override predicate hasActualResult(Location location, string element, string tag, string value) { + tag = "hasTaintFlow" and + exists(DataFlow::Node src, DataFlow::Node sink, Conf conf | conf.hasFlow(src, sink) | + sink.getLocation() = location and + element = sink.toString() and + value = "" + ) + } +} \ No newline at end of file diff --git a/java/ql/test/stubs/jackson-databind-2.10/com/fasterxml/jackson/databind/ObjectMapper.java b/java/ql/test/stubs/jackson-databind-2.10/com/fasterxml/jackson/databind/ObjectMapper.java index 455e0c0d309..af6218e4146 100644 --- a/java/ql/test/stubs/jackson-databind-2.10/com/fasterxml/jackson/databind/ObjectMapper.java +++ b/java/ql/test/stubs/jackson-databind-2.10/com/fasterxml/jackson/databind/ObjectMapper.java @@ -26,4 +26,8 @@ public class ObjectMapper { public String writeValueAsString(Object value) { return null; } + + public ObjectReader readerFor(Class type) { + return null; + } } diff --git a/java/ql/test/stubs/jackson-databind-2.10/com/fasterxml/jackson/databind/ObjectReader.java b/java/ql/test/stubs/jackson-databind-2.10/com/fasterxml/jackson/databind/ObjectReader.java new file mode 100644 index 00000000000..1916730da36 --- /dev/null +++ b/java/ql/test/stubs/jackson-databind-2.10/com/fasterxml/jackson/databind/ObjectReader.java @@ -0,0 +1,45 @@ +package com.fasterxml.jackson.databind; + +import java.io.*; + +public class ObjectReader { + public ObjectReader forType(Class valueType) { + return null; + } + + public T readValue(String src) { + return null; + } + + public T readValue(String src, Class valueType) throws IOException { + return null; + } + + public T readValue(byte[] content) throws IOException { + return null; + } + + public T readValue(byte[] content, Class valueType) throws IOException { + return null; + } + + public T readValue(File src) throws IOException { + return null; + } + + public T readValue(InputStream src) throws IOException { + return null; + } + + public T readValue(InputStream src, Class valueType) throws IOException { + return null; + } + + public T readValue(Reader src) throws IOException { + return null; + } + + public T readValue(Reader src, Class valueType) throws IOException { + return null; + } +} \ No newline at end of file From d0638db6e72836399061b506d029ad8a7d0e2f9d Mon Sep 17 00:00:00 2001 From: Jonathan Leitschuh Date: Mon, 3 May 2021 12:02:40 -0400 Subject: [PATCH 113/168] [Java] Add data flow through Iterator deserializers for Jackson --- .../jackson/JacksonSerializability.qll | 31 ++++++++++++---- .../dataflow/taint-jackson/Test.java | 15 ++++++++ .../jackson/databind/MappingIterator.java | 28 ++++++++++++++ .../jackson/databind/ObjectReader.java | 37 +++++++++++++++++++ 4 files changed, 104 insertions(+), 7 deletions(-) create mode 100644 java/ql/test/stubs/jackson-databind-2.10/com/fasterxml/jackson/databind/MappingIterator.java diff --git a/java/ql/src/semmle/code/java/frameworks/jackson/JacksonSerializability.qll b/java/ql/src/semmle/code/java/frameworks/jackson/JacksonSerializability.qll index c028af71d8d..1cfe34d7167 100644 --- a/java/ql/src/semmle/code/java/frameworks/jackson/JacksonSerializability.qll +++ b/java/ql/src/semmle/code/java/frameworks/jackson/JacksonSerializability.qll @@ -28,7 +28,7 @@ abstract class JacksonSerializableType extends Type { } * A method used for serializing objects using Jackson. The final parameter is the object to be * serialized. */ -library class JacksonWriteValueMethod extends Method, TaintPreservingCallable { +private class JacksonWriteValueMethod extends Method, TaintPreservingCallable { JacksonWriteValueMethod() { ( getDeclaringType().hasQualifiedName("com.fasterxml.jackson.databind", "ObjectWriter") or @@ -50,17 +50,17 @@ library class JacksonWriteValueMethod extends Method, TaintPreservingCallable { } } -library class JacksonReadValueMethod extends Method, TaintPreservingCallable { +private class JacksonReadValueMethod extends Method, TaintPreservingCallable { JacksonReadValueMethod() { getDeclaringType().hasQualifiedName("com.fasterxml.jackson.databind", "ObjectReader") and - hasName("readValue") + hasName(["readValue", "readValues"]) } override predicate returnsTaintFrom(int arg) { arg = 0 } } /** A type whose values are explicitly serialized in a call to a Jackson method. */ -library class ExplicitlyWrittenJacksonSerializableType extends JacksonSerializableType { +private class ExplicitlyWrittenJacksonSerializableType extends JacksonSerializableType { ExplicitlyWrittenJacksonSerializableType() { exists(MethodAccess ma | // A call to a Jackson write method... @@ -71,8 +71,20 @@ library class ExplicitlyWrittenJacksonSerializableType extends JacksonSerializab } } +/** A type whose values are explicitly deserialized in a call to a Jackson method. */ +private class ExplicitlyReadJacksonSerializableType extends JacksonDeserializableType { + ExplicitlyReadJacksonSerializableType() { + exists(MethodAccess ma | + // A call to a Jackson write method... + ma.getMethod() instanceof JacksonReadValueMethod and + // ...where `this` is used in the final argument, indicating that this type will be deserialized. + usesType(ma.getArgument(ma.getNumArgument() - 1).getType(), this) + ) + } +} + /** A type used in a `JacksonSerializableField` declaration. */ -library class FieldReferencedJacksonSerializableType extends JacksonSerializableType { +private class FieldReferencedJacksonSerializableType extends JacksonSerializableType { FieldReferencedJacksonSerializableType() { exists(JacksonSerializableField f | usesType(f.getType(), this)) } @@ -105,7 +117,7 @@ private class TypeLiteralToJacksonDatabindFlowConfiguration extends DataFlow5::C } /** A type whose values are explicitly deserialized in a call to a Jackson method. */ -library class ExplicitlyReadJacksonDeserializableType extends JacksonDeserializableType { +private class ExplicitlyReadJacksonDeserializableType extends JacksonDeserializableType { ExplicitlyReadJacksonDeserializableType() { exists(TypeLiteralToJacksonDatabindFlowConfiguration conf | usesType(conf.getSourceWithFlowToJacksonDatabind().getTypeName().getType(), this) @@ -114,7 +126,7 @@ library class ExplicitlyReadJacksonDeserializableType extends JacksonDeserializa } /** A type used in a `JacksonDeserializableField` declaration. */ -library class FieldReferencedJacksonDeSerializableType extends JacksonDeserializableType { +private class FieldReferencedJacksonDeSerializableType extends JacksonDeserializableType { FieldReferencedJacksonDeSerializableType() { exists(JacksonDeserializableField f | usesType(f.getType(), this)) } @@ -144,10 +156,15 @@ class JacksonDeserializableField extends DeserializableField { } } +/** A call to a field that may be deserialized using the Jackson JSON framework. */ class JacksonDeserializableFieldAccess extends FieldAccess { JacksonDeserializableFieldAccess() { getField() instanceof JacksonDeserializableField } } +/** + * When an object is deserialized by the Jackson JSON framework using a tainted input source, + * the fields that the framework deserialized are themselves tainted input data. + */ class JacksonDeseializedTaintStep extends AdditionalTaintStep { override predicate step(DataFlow::Node node1, DataFlow::Node node2) { node2.asExpr().(JacksonDeserializableFieldAccess).getQualifier() = node1.asExpr() diff --git a/java/ql/test/library-tests/dataflow/taint-jackson/Test.java b/java/ql/test/library-tests/dataflow/taint-jackson/Test.java index 16b223ed2e8..26f9182339a 100644 --- a/java/ql/test/library-tests/dataflow/taint-jackson/Test.java +++ b/java/ql/test/library-tests/dataflow/taint-jackson/Test.java @@ -3,6 +3,7 @@ import java.io.FileOutputStream; import java.io.OutputStream; import java.io.StringWriter; import java.io.Writer; +import java.util.Iterator; import com.fasterxml.jackson.core.JsonFactory; import com.fasterxml.jackson.core.JsonGenerator; @@ -79,4 +80,18 @@ class Test { sink(reader.readValue(s, Potato.class).name); //$hasTaintFlow sink(reader.readValue(s, Potato.class).getName()); //$hasTaintFlow } + + public static void jacksonObjectReaderIterable() throws java.io.IOException { + String s = taint(); + ObjectMapper om = new ObjectMapper(); + ObjectReader reader = om.readerFor(Potato.class); + sink(reader.readValues(s)); //$hasTaintFlow + Iterator pIterator = reader.readValues(s, Potato.class); + while(pIterator.hasNext()) { + Potato p = pIterator.next(); + sink(p); //$hasTaintFlow + sink(p.name); //$hasTaintFlow + sink(p.getName()); //$hasTaintFlow + } + } } diff --git a/java/ql/test/stubs/jackson-databind-2.10/com/fasterxml/jackson/databind/MappingIterator.java b/java/ql/test/stubs/jackson-databind-2.10/com/fasterxml/jackson/databind/MappingIterator.java new file mode 100644 index 00000000000..72e08df2719 --- /dev/null +++ b/java/ql/test/stubs/jackson-databind-2.10/com/fasterxml/jackson/databind/MappingIterator.java @@ -0,0 +1,28 @@ +package com.fasterxml.jackson.databind; + +import java.io.Closeable; +import java.io.IOException; +import java.util.*; + +public class MappingIterator implements Iterator, Closeable { + + @Override + public boolean hasNext() { + return false; + } + + @Override + public T next() { + return null; + } + + @Override + public void remove() { + + } + + @Override + public void close() throws IOException { + + } +} \ No newline at end of file diff --git a/java/ql/test/stubs/jackson-databind-2.10/com/fasterxml/jackson/databind/ObjectReader.java b/java/ql/test/stubs/jackson-databind-2.10/com/fasterxml/jackson/databind/ObjectReader.java index 1916730da36..875c07dace3 100644 --- a/java/ql/test/stubs/jackson-databind-2.10/com/fasterxml/jackson/databind/ObjectReader.java +++ b/java/ql/test/stubs/jackson-databind-2.10/com/fasterxml/jackson/databind/ObjectReader.java @@ -42,4 +42,41 @@ public class ObjectReader { public T readValue(Reader src, Class valueType) throws IOException { return null; } + + public MappingIterator readValues(String src) { + return null; + } + + public MappingIterator readValues(String src, Class valueType) throws IOException { + return null; + } + + public MappingIterator readValues(byte[] content) throws IOException { + return null; + } + + public MappingIterator readValues(byte[] content, Class valueType) throws IOException { + return null; + } + + public MappingIterator readValues(File src) throws IOException { + return null; + } + + public MappingIterator readValues(InputStream src) throws IOException { + return null; + } + + public MappingIterator readValues(InputStream src, Class valueType) throws IOException { + return null; + } + + public MappingIterator readValues(Reader src) throws IOException { + return null; + } + + public MappingIterator readValues(Reader src, Class valueType) throws IOException { + return null; + } + } \ No newline at end of file From d0b0b767a28d98ce40d3c2fddee477608757cdcd Mon Sep 17 00:00:00 2001 From: Jonathan Leitschuh Date: Mon, 3 May 2021 12:23:33 -0400 Subject: [PATCH 114/168] Apply suggestions from code review Co-authored-by: Marcono1234 --- .../code/java/frameworks/jackson/JacksonSerializability.qll | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/java/ql/src/semmle/code/java/frameworks/jackson/JacksonSerializability.qll b/java/ql/src/semmle/code/java/frameworks/jackson/JacksonSerializability.qll index 1cfe34d7167..2ddb12e828d 100644 --- a/java/ql/src/semmle/code/java/frameworks/jackson/JacksonSerializability.qll +++ b/java/ql/src/semmle/code/java/frameworks/jackson/JacksonSerializability.qll @@ -75,7 +75,7 @@ private class ExplicitlyWrittenJacksonSerializableType extends JacksonSerializab private class ExplicitlyReadJacksonSerializableType extends JacksonDeserializableType { ExplicitlyReadJacksonSerializableType() { exists(MethodAccess ma | - // A call to a Jackson write method... + // A call to a Jackson read method... ma.getMethod() instanceof JacksonReadValueMethod and // ...where `this` is used in the final argument, indicating that this type will be deserialized. usesType(ma.getArgument(ma.getNumArgument() - 1).getType(), this) @@ -126,8 +126,8 @@ private class ExplicitlyReadJacksonDeserializableType extends JacksonDeserializa } /** A type used in a `JacksonDeserializableField` declaration. */ -private class FieldReferencedJacksonDeSerializableType extends JacksonDeserializableType { - FieldReferencedJacksonDeSerializableType() { +private class FieldReferencedJacksonDeserializableType extends JacksonDeserializableType { + FieldReferencedJacksonDeserializableType() { exists(JacksonDeserializableField f | usesType(f.getType(), this)) } } From b871f48c509b960c75847903300f7cbf7664fb6a Mon Sep 17 00:00:00 2001 From: Jonathan Leitschuh Date: Mon, 3 May 2021 12:40:48 -0400 Subject: [PATCH 115/168] [Java] Add release note to Jackson change --- .../change-notes/2021-05-03-jackson-dataflow-deserialization.md | 2 ++ 1 file changed, 2 insertions(+) create mode 100644 java/change-notes/2021-05-03-jackson-dataflow-deserialization.md diff --git a/java/change-notes/2021-05-03-jackson-dataflow-deserialization.md b/java/change-notes/2021-05-03-jackson-dataflow-deserialization.md new file mode 100644 index 00000000000..475f8dbee4f --- /dev/null +++ b/java/change-notes/2021-05-03-jackson-dataflow-deserialization.md @@ -0,0 +1,2 @@ +lgtm,codescanning +* Increase coverage of dataflow through Jackson JSON deserialized objects. From 83d527ed190331f4ecdb165539423922ee1f704a Mon Sep 17 00:00:00 2001 From: Jonathan Leitschuh Date: Mon, 3 May 2021 13:39:54 -0400 Subject: [PATCH 116/168] Apply suggestions from code review Co-authored-by: Marcono1234 --- .../code/java/frameworks/jackson/JacksonSerializability.qll | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/java/ql/src/semmle/code/java/frameworks/jackson/JacksonSerializability.qll b/java/ql/src/semmle/code/java/frameworks/jackson/JacksonSerializability.qll index 2ddb12e828d..0b75aff2b24 100644 --- a/java/ql/src/semmle/code/java/frameworks/jackson/JacksonSerializability.qll +++ b/java/ql/src/semmle/code/java/frameworks/jackson/JacksonSerializability.qll @@ -165,7 +165,7 @@ class JacksonDeserializableFieldAccess extends FieldAccess { * When an object is deserialized by the Jackson JSON framework using a tainted input source, * the fields that the framework deserialized are themselves tainted input data. */ -class JacksonDeseializedTaintStep extends AdditionalTaintStep { +class JacksonDeserializedTaintStep extends AdditionalTaintStep { override predicate step(DataFlow::Node node1, DataFlow::Node node2) { node2.asExpr().(JacksonDeserializableFieldAccess).getQualifier() = node1.asExpr() } From e97bad3b3395d3a6143b515bba1b12c344a881d2 Mon Sep 17 00:00:00 2001 From: Jonathan Leitschuh Date: Tue, 4 May 2021 10:58:20 -0400 Subject: [PATCH 117/168] Support field access data flow for JacksonDeserializedTaintStep --- .../code/java/frameworks/jackson/JacksonSerializability.qll | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/java/ql/src/semmle/code/java/frameworks/jackson/JacksonSerializability.qll b/java/ql/src/semmle/code/java/frameworks/jackson/JacksonSerializability.qll index 0b75aff2b24..8cc70c5f36a 100644 --- a/java/ql/src/semmle/code/java/frameworks/jackson/JacksonSerializability.qll +++ b/java/ql/src/semmle/code/java/frameworks/jackson/JacksonSerializability.qll @@ -167,7 +167,7 @@ class JacksonDeserializableFieldAccess extends FieldAccess { */ class JacksonDeserializedTaintStep extends AdditionalTaintStep { override predicate step(DataFlow::Node node1, DataFlow::Node node2) { - node2.asExpr().(JacksonDeserializableFieldAccess).getQualifier() = node1.asExpr() + DataFlow::getFieldQualifier(node2.asExpr().(JacksonDeserializableFieldAccess)) = node1 } } From bacc3ef5b3699f03bff628e8ca1196c0f7095342 Mon Sep 17 00:00:00 2001 From: Jonathan Leitschuh Date: Mon, 10 May 2021 17:20:14 -0400 Subject: [PATCH 118/168] [Java] Jackson add support for 2 step deserialization taint flow --- .../semmle/code/java/dataflow/ExternalFlow.qll | 1 + .../frameworks/jackson/JacksonSerializability.qll | 11 +++++++++++ .../dataflow/taint-jackson/Test.java | 15 +++++++++++++++ .../com/fasterxml/jackson/databind/JsonNode.java | 4 +++- .../fasterxml/jackson/databind/ObjectMapper.java | 8 ++++++++ 5 files changed, 38 insertions(+), 1 deletion(-) diff --git a/java/ql/src/semmle/code/java/dataflow/ExternalFlow.qll b/java/ql/src/semmle/code/java/dataflow/ExternalFlow.qll index df529b49efb..1ec81d1e3ac 100644 --- a/java/ql/src/semmle/code/java/dataflow/ExternalFlow.qll +++ b/java/ql/src/semmle/code/java/dataflow/ExternalFlow.qll @@ -77,6 +77,7 @@ private module Frameworks { private import semmle.code.java.frameworks.ApacheHttp private import semmle.code.java.frameworks.apache.Lang private import semmle.code.java.frameworks.guava.Guava + private import semmle.code.java.frameworks.jackson.JacksonSerializability private import semmle.code.java.security.ResponseSplitting private import semmle.code.java.security.XSS private import semmle.code.java.security.LdapInjection diff --git a/java/ql/src/semmle/code/java/frameworks/jackson/JacksonSerializability.qll b/java/ql/src/semmle/code/java/frameworks/jackson/JacksonSerializability.qll index 8cc70c5f36a..ad35a802d43 100644 --- a/java/ql/src/semmle/code/java/frameworks/jackson/JacksonSerializability.qll +++ b/java/ql/src/semmle/code/java/frameworks/jackson/JacksonSerializability.qll @@ -9,6 +9,7 @@ import semmle.code.java.Reflection import semmle.code.java.dataflow.DataFlow import semmle.code.java.dataflow.DataFlow5 import semmle.code.java.dataflow.FlowSteps +private import semmle.code.java.dataflow.ExternalFlow /** * A `@com.fasterxml.jackson.annotation.JsonIgnore` annoation. @@ -275,3 +276,13 @@ class JacksonMixedInCallable extends Callable { ) } } + +private class JacksonModel extends SummaryModelCsv { + override predicate row(string row) { + row = + [ + "com.fasterxml.jackson.databind;ObjectMapper;true;valueToTree;;;Argument[0];ReturnValue;taint", + "com.fasterxml.jackson.databind;ObjectMapper;true;convertValue;;;Argument[0];ReturnValue;taint" + ] + } +} diff --git a/java/ql/test/library-tests/dataflow/taint-jackson/Test.java b/java/ql/test/library-tests/dataflow/taint-jackson/Test.java index 26f9182339a..3be85336e26 100644 --- a/java/ql/test/library-tests/dataflow/taint-jackson/Test.java +++ b/java/ql/test/library-tests/dataflow/taint-jackson/Test.java @@ -4,9 +4,12 @@ import java.io.OutputStream; import java.io.StringWriter; import java.io.Writer; import java.util.Iterator; +import java.util.HashMap; +import java.util.Map; import com.fasterxml.jackson.core.JsonFactory; import com.fasterxml.jackson.core.JsonGenerator; +import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.ObjectWriter; import com.fasterxml.jackson.databind.ObjectReader; @@ -94,4 +97,16 @@ class Test { sink(p.getName()); //$hasTaintFlow } } + + public static void jacksonTwoStepDeserialization() throws java.io.IOException { + String s = taint(); + Map taintedParams = new HashMap<>(); + taintedParams.put("name", s); + ObjectMapper om = new ObjectMapper(); + JsonNode jn = om.valueToTree(taintedParams); + sink(jn); //$hasTaintFlow + Potato p = om.convertValue(jn, Potato.class); + sink(p); //$hasTaintFlow + sink(p.getName()); //$hasTaintFlow + } } diff --git a/java/ql/test/stubs/jackson-databind-2.10/com/fasterxml/jackson/databind/JsonNode.java b/java/ql/test/stubs/jackson-databind-2.10/com/fasterxml/jackson/databind/JsonNode.java index a26eb2592c6..ad3da15a26c 100644 --- a/java/ql/test/stubs/jackson-databind-2.10/com/fasterxml/jackson/databind/JsonNode.java +++ b/java/ql/test/stubs/jackson-databind-2.10/com/fasterxml/jackson/databind/JsonNode.java @@ -1,6 +1,8 @@ package com.fasterxml.jackson.databind; -public class JsonNode { +import java.util.*; + +public abstract class JsonNode implements Iterable { public JsonNode() { } } \ No newline at end of file diff --git a/java/ql/test/stubs/jackson-databind-2.10/com/fasterxml/jackson/databind/ObjectMapper.java b/java/ql/test/stubs/jackson-databind-2.10/com/fasterxml/jackson/databind/ObjectMapper.java index af6218e4146..71dc99a351d 100644 --- a/java/ql/test/stubs/jackson-databind-2.10/com/fasterxml/jackson/databind/ObjectMapper.java +++ b/java/ql/test/stubs/jackson-databind-2.10/com/fasterxml/jackson/databind/ObjectMapper.java @@ -30,4 +30,12 @@ public class ObjectMapper { public ObjectReader readerFor(Class type) { return null; } + + public T valueToTree(Object fromValue) throws IllegalArgumentException { + return null; + } + + public T convertValue(Object fromValue, Class toValueType) throws IllegalArgumentException { + return null; + } } From 5a68ac88efdf272c2f3e6dd9a9c5393b582bbcec Mon Sep 17 00:00:00 2001 From: Jonathan Leitschuh Date: Tue, 11 May 2021 10:48:22 -0400 Subject: [PATCH 119/168] Cleanup Jackson logic after code review --- .../jackson/JacksonSerializability.qll | 19 +++++++------------ .../dataflow/taint-jackson/dataFlow.ql | 2 +- .../fasterxml/jackson/databind/JsonNode.java | 2 +- .../jackson/databind/MappingIterator.java | 2 +- .../jackson/databind/ObjectReader.java | 2 +- 5 files changed, 11 insertions(+), 16 deletions(-) diff --git a/java/ql/src/semmle/code/java/frameworks/jackson/JacksonSerializability.qll b/java/ql/src/semmle/code/java/frameworks/jackson/JacksonSerializability.qll index ad35a802d43..09fd419642e 100644 --- a/java/ql/src/semmle/code/java/frameworks/jackson/JacksonSerializability.qll +++ b/java/ql/src/semmle/code/java/frameworks/jackson/JacksonSerializability.qll @@ -72,18 +72,6 @@ private class ExplicitlyWrittenJacksonSerializableType extends JacksonSerializab } } -/** A type whose values are explicitly deserialized in a call to a Jackson method. */ -private class ExplicitlyReadJacksonSerializableType extends JacksonDeserializableType { - ExplicitlyReadJacksonSerializableType() { - exists(MethodAccess ma | - // A call to a Jackson read method... - ma.getMethod() instanceof JacksonReadValueMethod and - // ...where `this` is used in the final argument, indicating that this type will be deserialized. - usesType(ma.getArgument(ma.getNumArgument() - 1).getType(), this) - ) - } -} - /** A type used in a `JacksonSerializableField` declaration. */ private class FieldReferencedJacksonSerializableType extends JacksonSerializableType { FieldReferencedJacksonSerializableType() { @@ -123,6 +111,13 @@ private class ExplicitlyReadJacksonDeserializableType extends JacksonDeserializa exists(TypeLiteralToJacksonDatabindFlowConfiguration conf | usesType(conf.getSourceWithFlowToJacksonDatabind().getTypeName().getType(), this) ) + or + exists(MethodAccess ma | + // A call to a Jackson read method... + ma.getMethod() instanceof JacksonReadValueMethod and + // ...where `this` is used in the final argument, indicating that this type will be deserialized. + usesType(ma.getArgument(ma.getNumArgument() - 1).getType(), this) + ) } } diff --git a/java/ql/test/library-tests/dataflow/taint-jackson/dataFlow.ql b/java/ql/test/library-tests/dataflow/taint-jackson/dataFlow.ql index 3ee51339b7b..0836906530b 100644 --- a/java/ql/test/library-tests/dataflow/taint-jackson/dataFlow.ql +++ b/java/ql/test/library-tests/dataflow/taint-jackson/dataFlow.ql @@ -31,4 +31,4 @@ class HasFlowTest extends InlineExpectationsTest { value = "" ) } -} \ No newline at end of file +} diff --git a/java/ql/test/stubs/jackson-databind-2.10/com/fasterxml/jackson/databind/JsonNode.java b/java/ql/test/stubs/jackson-databind-2.10/com/fasterxml/jackson/databind/JsonNode.java index ad3da15a26c..b04572cd4da 100644 --- a/java/ql/test/stubs/jackson-databind-2.10/com/fasterxml/jackson/databind/JsonNode.java +++ b/java/ql/test/stubs/jackson-databind-2.10/com/fasterxml/jackson/databind/JsonNode.java @@ -5,4 +5,4 @@ import java.util.*; public abstract class JsonNode implements Iterable { public JsonNode() { } -} \ No newline at end of file +} diff --git a/java/ql/test/stubs/jackson-databind-2.10/com/fasterxml/jackson/databind/MappingIterator.java b/java/ql/test/stubs/jackson-databind-2.10/com/fasterxml/jackson/databind/MappingIterator.java index 72e08df2719..ac427ef01c9 100644 --- a/java/ql/test/stubs/jackson-databind-2.10/com/fasterxml/jackson/databind/MappingIterator.java +++ b/java/ql/test/stubs/jackson-databind-2.10/com/fasterxml/jackson/databind/MappingIterator.java @@ -25,4 +25,4 @@ public class MappingIterator implements Iterator, Closeable { public void close() throws IOException { } -} \ No newline at end of file +} diff --git a/java/ql/test/stubs/jackson-databind-2.10/com/fasterxml/jackson/databind/ObjectReader.java b/java/ql/test/stubs/jackson-databind-2.10/com/fasterxml/jackson/databind/ObjectReader.java index 875c07dace3..f067a3e95a4 100644 --- a/java/ql/test/stubs/jackson-databind-2.10/com/fasterxml/jackson/databind/ObjectReader.java +++ b/java/ql/test/stubs/jackson-databind-2.10/com/fasterxml/jackson/databind/ObjectReader.java @@ -79,4 +79,4 @@ public class ObjectReader { return null; } -} \ No newline at end of file +} From e7cd6c997208b742129bd72c0715a27790130211 Mon Sep 17 00:00:00 2001 From: luchua-bc Date: Tue, 11 May 2021 16:56:12 +0000 Subject: [PATCH 120/168] Optimize the query --- .../Security/CWE/CWE-094/JythonInjection.java | 6 ++-- .../Security/CWE/CWE-094/JythonInjection.ql | 35 +++++++++---------- .../security/CWE-094/JythonInjection.java | 4 +-- 3 files changed, 22 insertions(+), 23 deletions(-) diff --git a/java/ql/src/experimental/Security/CWE/CWE-094/JythonInjection.java b/java/ql/src/experimental/Security/CWE/CWE-094/JythonInjection.java index fca518443d1..5c1796e1f60 100644 --- a/java/ql/src/experimental/Security/CWE/CWE-094/JythonInjection.java +++ b/java/ql/src/experimental/Security/CWE/CWE-094/JythonInjection.java @@ -1,3 +1,5 @@ +import org.python.util.PythonInterpreter; + public class JythonInjection extends HttpServlet { protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { response.setContentType("text/plain"); @@ -10,7 +12,7 @@ public class JythonInjection extends HttpServlet { interpreter.setOut(out); interpreter.setErr(out); - // BAD: allow arbitrary Jython expression to execute + // BAD: allow execution of arbitrary Python code interpreter.exec(code); out.flush(); @@ -32,7 +34,7 @@ public class JythonInjection extends HttpServlet { try { interpreter = new PythonInterpreter(); - // BAD: allow arbitrary Jython expression to evaluate + // BAD: allow execution of arbitrary Python code PyObject py = interpreter.eval(code); response.getWriter().print(py.toString()); diff --git a/java/ql/src/experimental/Security/CWE/CWE-094/JythonInjection.ql b/java/ql/src/experimental/Security/CWE/CWE-094/JythonInjection.ql index 088c33e00fd..9991c0901dc 100644 --- a/java/ql/src/experimental/Security/CWE/CWE-094/JythonInjection.ql +++ b/java/ql/src/experimental/Security/CWE/CWE-094/JythonInjection.ql @@ -11,6 +11,7 @@ import java import semmle.code.java.dataflow.FlowSources +import semmle.code.java.frameworks.spring.SpringController import DataFlow::PathGraph /** The class `org.python.util.PythonInterpreter`. */ @@ -22,12 +23,7 @@ class PythonInterpreter extends RefType { class InterpretExprMethod extends Method { InterpretExprMethod() { this.getDeclaringType().getAnAncestor*() instanceof PythonInterpreter and - ( - getName().matches("exec%") or - hasName("eval") or - hasName("compile") or - getName().matches("run%") - ) + getName().matches(["exec%", "run%", "eval", "compile"]) } } @@ -48,14 +44,14 @@ predicate runCode(MethodAccess ma, Expr sink) { class LoadClassMethod extends Method { LoadClassMethod() { this.getDeclaringType().getAnAncestor*() instanceof BytecodeLoader and - ( - hasName("makeClass") or - hasName("makeCode") - ) + hasName(["makeClass", "makeCode"]) } } -/** Holds if a Java class file is loaded. */ +/** + * Holds if `ma` is a call to a class-loading method, and `sink` is the byte array + * representing the class to be loaded. + */ predicate loadClass(MethodAccess ma, Expr sink) { exists(Method m, int i | m = ma.getMethod() | m instanceof LoadClassMethod and @@ -69,7 +65,7 @@ class Py extends RefType { Py() { this.hasQualifiedName("org.python.core", "Py") } } -/** A method that compiles code with `Py`. */ +/** A method declared on class `Py` or one of its descendants that compiles Python code. */ class PyCompileMethod extends Method { PyCompileMethod() { this.getDeclaringType().getAnAncestor*() instanceof Py and @@ -85,7 +81,7 @@ predicate compile(MethodAccess ma, Expr sink) { ) } -/** Sink of an expression loaded by Jython. */ +/** An expression loaded by Jython. */ class CodeInjectionSink extends DataFlow::ExprNode { CodeInjectionSink() { runCode(_, this.getExpr()) or @@ -103,17 +99,18 @@ class CodeInjectionSink extends DataFlow::ExprNode { class CodeInjectionConfiguration extends TaintTracking::Configuration { CodeInjectionConfiguration() { this = "CodeInjectionConfiguration" } - override predicate isSource(DataFlow::Node source) { - source instanceof RemoteFlowSource - or - source instanceof LocalUserInput - } + override predicate isSource(DataFlow::Node source) { source instanceof RemoteFlowSource } override predicate isSink(DataFlow::Node sink) { sink instanceof CodeInjectionSink } override predicate isAdditionalTaintStep(DataFlow::Node node1, DataFlow::Node node2) { // @RequestBody MyQueryObj query; interpreter.exec(query.getInterpreterCode()); - exists(MethodAccess ma | ma.getQualifier() = node1.asExpr() and ma = node2.asExpr()) + exists(MethodAccess ma | + ma.getMethod().getDeclaringType().getASubtype*() instanceof SpringUntrustedDataType and + not ma.getMethod().getDeclaringType() instanceof TypeObject and + ma.getQualifier() = node1.asExpr() and + ma = node2.asExpr() + ) } } diff --git a/java/ql/test/experimental/query-tests/security/CWE-094/JythonInjection.java b/java/ql/test/experimental/query-tests/security/CWE-094/JythonInjection.java index 682e8af5113..f9b29fec6cc 100644 --- a/java/ql/test/experimental/query-tests/security/CWE-094/JythonInjection.java +++ b/java/ql/test/experimental/query-tests/security/CWE-094/JythonInjection.java @@ -22,7 +22,7 @@ public class JythonInjection extends HttpServlet { super(); } - // BAD: allow arbitrary Jython expression to execute + // BAD: allow execution of arbitrary Python code protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { response.setContentType("text/plain"); String code = request.getParameter("code"); @@ -47,7 +47,7 @@ public class JythonInjection extends HttpServlet { } } - // BAD: allow arbitrary Jython expression to evaluate + // BAD: allow execution of arbitrary Python code protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { response.setContentType("text/plain"); String code = request.getParameter("code"); From 948f1d8e344935c7b550450813e61f4df415ba6e Mon Sep 17 00:00:00 2001 From: Mathias Vorreiter Pedersen Date: Tue, 11 May 2021 19:43:21 +0200 Subject: [PATCH 121/168] C++: Add testcase with INTMAX_MIN. --- .../Security/CWE/CWE-190/semmle/tainted/test5.cpp | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/cpp/ql/test/query-tests/Security/CWE/CWE-190/semmle/tainted/test5.cpp b/cpp/ql/test/query-tests/Security/CWE/CWE-190/semmle/tainted/test5.cpp index 92eb71ad541..2ee675be6b5 100644 --- a/cpp/ql/test/query-tests/Security/CWE/CWE-190/semmle/tainted/test5.cpp +++ b/cpp/ql/test/query-tests/Security/CWE/CWE-190/semmle/tainted/test5.cpp @@ -29,4 +29,14 @@ void useTaintedIntWithGuard() { if(imaxabs(tainted) <= 100) { int product = tainted * tainted; // GOOD: can't underflow/overflow } +} + +#define INTMAX_MIN (-0x7fffffffffffffff - 1) + +void useTaintedIntWithGuardIntMaxMin() { + intmax_t tainted = getTaintedInt(); + + if(imaxabs(tainted) <= INTMAX_MIN) { + int product = tainted * tainted; // BAD: imaxabs(INTMAX_MIN) == INTMAX_MIN [NOT DETECTED] + } } \ No newline at end of file From 8e371fd05a842648d78762238c4f2b8cd5c531f4 Mon Sep 17 00:00:00 2001 From: Tamas Vajk Date: Tue, 11 May 2021 21:54:05 +0200 Subject: [PATCH 122/168] Adjust expected IR test file --- csharp/ql/test/experimental/ir/ir/raw_ir.expected | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/csharp/ql/test/experimental/ir/ir/raw_ir.expected b/csharp/ql/test/experimental/ir/ir/raw_ir.expected index 646cd0f26f9..c598a8778d9 100644 --- a/csharp/ql/test/experimental/ir/ir/raw_ir.expected +++ b/csharp/ql/test/experimental/ir/ir/raw_ir.expected @@ -613,13 +613,13 @@ foreach.cs: # 5| r5_28(glval) = PointerAdd[4] : r5_1, r5_27 # 5| r5_29(Int32) = Constant[7] : # 5| mu5_30(Int32) = Store[?] : &:r5_28, r5_29 -# 7| r7_1(glval) = VariableAddress[#temp7:9] : +# 7| r7_1(glval) = VariableAddress[#temp7:9] : # 7| r7_2(glval) = VariableAddress[a_array] : # 7| r7_3(Int32[]) = Load[a_array] : &:r7_2, ~m? # 7| r7_4() = FunctionAddress[GetEnumerator] : # 7| r7_5(IEnumerator) = Call[GetEnumerator] : func:r7_4, this:r7_3 # 7| mu7_6() = ^CallSideEffect : ~m? -# 7| mu7_7(IEnumerator) = Store[#temp7:9] : &:r7_1, r7_5 +# 7| mu7_7(Boolean) = Store[#temp7:9] : &:r7_1, r7_5 #-----| Goto -> Block 1 # 7| Block 1 From 961467e06e4142ca71d144ee5c1bb7ca993b706b Mon Sep 17 00:00:00 2001 From: Tom Hvitved Date: Wed, 12 May 2021 10:15:04 +0200 Subject: [PATCH 123/168] C#: Always pass `/p:UseSharedCompilation=false` to `dotnet build` in auto builder --- .../BuildScripts.cs | 44 ++++----------- .../Semmle.Autobuild.CSharp/DotNetRule.cs | 56 ++++--------------- 2 files changed, 21 insertions(+), 79 deletions(-) diff --git a/csharp/autobuilder/Semmle.Autobuild.CSharp.Tests/BuildScripts.cs b/csharp/autobuilder/Semmle.Autobuild.CSharp.Tests/BuildScripts.cs index 99ad4c8f963..197edc2c162 100644 --- a/csharp/autobuilder/Semmle.Autobuild.CSharp.Tests/BuildScripts.cs +++ b/csharp/autobuilder/Semmle.Autobuild.CSharp.Tests/BuildScripts.cs @@ -415,7 +415,7 @@ namespace Semmle.Autobuild.CSharp.Tests actions.RunProcess["cmd.exe /C dotnet --info"] = 0; actions.RunProcess[@"cmd.exe /C dotnet clean C:\Project\test.csproj"] = 0; actions.RunProcess[@"cmd.exe /C dotnet restore C:\Project\test.csproj"] = 0; - actions.RunProcess[@"cmd.exe /C C:\odasa\tools\odasa index --auto dotnet build --no-incremental C:\Project\test.csproj"] = 0; + actions.RunProcess[@"cmd.exe /C C:\odasa\tools\odasa index --auto dotnet build --no-incremental /p:UseSharedCompilation=false C:\Project\test.csproj"] = 0; actions.FileExists["csharp.log"] = true; actions.FileExists[@"C:\Project\test.csproj"] = true; actions.GetEnvironmentVariable["CODEQL_EXTRACTOR_CSHARP_TRAP_DIR"] = ""; @@ -439,9 +439,6 @@ namespace Semmle.Autobuild.CSharp.Tests [Fact] public void TestLinuxCSharpAutoBuilder() { - actions.RunProcess["dotnet --list-runtimes"] = 0; - actions.RunProcessOut["dotnet --list-runtimes"] = @"Microsoft.AspNetCore.App 2.2.5 [/usr/local/share/dotnet/shared/Microsoft.AspNetCore.App] -Microsoft.NETCore.App 2.2.5 [/usr/local/share/dotnet/shared/Microsoft.NETCore.App]"; actions.RunProcess["dotnet --info"] = 0; actions.RunProcess[@"dotnet clean C:\Project/test.csproj"] = 0; actions.RunProcess[@"dotnet restore C:\Project/test.csproj"] = 0; @@ -463,7 +460,7 @@ Microsoft.NETCore.App 2.2.5 [/usr/local/share/dotnet/shared/Microsoft.NETCore.Ap actions.LoadXml[@"C:\Project/test.csproj"] = xml; var autobuilder = CreateAutoBuilder(false); - TestAutobuilderScript(autobuilder, 0, 5); + TestAutobuilderScript(autobuilder, 0, 4); } [Fact] @@ -603,8 +600,6 @@ Microsoft.NETCore.App 2.2.5 [/usr/local/share/dotnet/shared/Microsoft.NETCore.Ap [Fact] public void TestLinuxBuildCommand() { - actions.RunProcess["dotnet --list-runtimes"] = 1; - actions.RunProcessOut["dotnet --list-runtimes"] = ""; actions.RunProcess[@"C:\odasa/tools/odasa index --auto ""./build.sh --skip-tests"""] = 0; actions.FileExists["csharp.log"] = true; actions.GetEnvironmentVariable["CODEQL_EXTRACTOR_CSHARP_TRAP_DIR"] = ""; @@ -615,7 +610,7 @@ Microsoft.NETCore.App 2.2.5 [/usr/local/share/dotnet/shared/Microsoft.NETCore.Ap SkipVsWhere(); var autobuilder = CreateAutoBuilder(false, buildCommand: "./build.sh --skip-tests"); - TestAutobuilderScript(autobuilder, 0, 2); + TestAutobuilderScript(autobuilder, 0, 1); } [Fact] @@ -626,14 +621,12 @@ Microsoft.NETCore.App 2.2.5 [/usr/local/share/dotnet/shared/Microsoft.NETCore.Ap actions.GetEnvironmentVariable["CODEQL_EXTRACTOR_CSHARP_TRAP_DIR"] = ""; actions.GetEnvironmentVariable["CODEQL_EXTRACTOR_CSHARP_SOURCE_ARCHIVE_DIR"] = ""; actions.RunProcess[@"/bin/chmod u+x C:\Project/build/build.sh"] = 0; - actions.RunProcess["dotnet --list-runtimes"] = 1; - actions.RunProcessOut["dotnet --list-runtimes"] = ""; actions.RunProcess[@"C:\odasa/tools/odasa index --auto C:\Project/build/build.sh"] = 0; actions.RunProcessWorkingDirectory[@"C:\odasa/tools/odasa index --auto C:\Project/build/build.sh"] = @"C:\Project/build"; actions.FileExists["csharp.log"] = true; var autobuilder = CreateAutoBuilder(false); - TestAutobuilderScript(autobuilder, 0, 3); + TestAutobuilderScript(autobuilder, 0, 2); } [Fact] @@ -645,14 +638,12 @@ Microsoft.NETCore.App 2.2.5 [/usr/local/share/dotnet/shared/Microsoft.NETCore.Ap actions.GetEnvironmentVariable["CODEQL_EXTRACTOR_CSHARP_SOURCE_ARCHIVE_DIR"] = ""; actions.RunProcess[@"/bin/chmod u+x C:\Project/build.sh"] = 0; - actions.RunProcess["dotnet --list-runtimes"] = 1; - actions.RunProcessOut["dotnet --list-runtimes"] = ""; actions.RunProcess[@"C:\odasa/tools/odasa index --auto C:\Project/build.sh"] = 0; actions.RunProcessWorkingDirectory[@"C:\odasa/tools/odasa index --auto C:\Project/build.sh"] = @"C:\Project"; actions.FileExists["csharp.log"] = false; var autobuilder = CreateAutoBuilder(false); - TestAutobuilderScript(autobuilder, 1, 3); + TestAutobuilderScript(autobuilder, 1, 2); } [Fact] @@ -664,14 +655,12 @@ Microsoft.NETCore.App 2.2.5 [/usr/local/share/dotnet/shared/Microsoft.NETCore.Ap actions.GetEnvironmentVariable["CODEQL_EXTRACTOR_CSHARP_SOURCE_ARCHIVE_DIR"] = ""; actions.RunProcess[@"/bin/chmod u+x C:\Project/build.sh"] = 0; - actions.RunProcess["dotnet --list-runtimes"] = 1; - actions.RunProcessOut["dotnet --list-runtimes"] = ""; actions.RunProcess[@"C:\odasa/tools/odasa index --auto C:\Project/build.sh"] = 5; actions.RunProcessWorkingDirectory[@"C:\odasa/tools/odasa index --auto C:\Project/build.sh"] = @"C:\Project"; actions.FileExists["csharp.log"] = true; var autobuilder = CreateAutoBuilder(false); - TestAutobuilderScript(autobuilder, 1, 3); + TestAutobuilderScript(autobuilder, 1, 2); } [Fact] @@ -872,9 +861,6 @@ Microsoft.NETCore.App 2.2.5 [/usr/local/share/dotnet/shared/Microsoft.NETCore.Ap [Fact] public void TestSkipNugetDotnet() { - actions.RunProcess["dotnet --list-runtimes"] = 0; - actions.RunProcessOut["dotnet --list-runtimes"] = @"Microsoft.AspNetCore.App 2.1.3 [/usr/local/share/dotnet/shared/Microsoft.AspNetCore.App] -Microsoft.NETCore.App 2.1.3 [/usr/local/share/dotnet/shared/Microsoft.NETCore.App]"; actions.RunProcess["dotnet --info"] = 0; actions.RunProcess[@"dotnet clean C:\Project/test.csproj"] = 0; actions.RunProcess[@"dotnet restore C:\Project/test.csproj"] = 0; @@ -896,7 +882,7 @@ Microsoft.NETCore.App 2.1.3 [/usr/local/share/dotnet/shared/Microsoft.NETCore.Ap actions.LoadXml[@"C:\Project/test.csproj"] = xml; var autobuilder = CreateAutoBuilder(false, dotnetArguments: "--no-restore"); // nugetRestore=false does not work for now. - TestAutobuilderScript(autobuilder, 0, 5); + TestAutobuilderScript(autobuilder, 0, 4); } [Fact] @@ -907,13 +893,10 @@ Microsoft.NETCore.App 2.1.3 [/usr/local/share/dotnet/shared/Microsoft.NETCore.Ap actions.RunProcess[@"chmod u+x dotnet-install.sh"] = 0; actions.RunProcess[@"./dotnet-install.sh --channel release --version 2.1.3 --install-dir C:\Project/.dotnet"] = 0; actions.RunProcess[@"rm dotnet-install.sh"] = 0; - actions.RunProcess[@"C:\Project/.dotnet/dotnet --list-runtimes"] = 0; - actions.RunProcessOut[@"C:\Project/.dotnet/dotnet --list-runtimes"] = @"Microsoft.AspNetCore.App 3.0.0 [/usr/local/share/dotnet/shared/Microsoft.AspNetCore.App] -Microsoft.NETCore.App 3.0.0 [/usr/local/share/dotnet/shared/Microsoft.NETCore.App]"; actions.RunProcess[@"C:\Project/.dotnet/dotnet --info"] = 0; actions.RunProcess[@"C:\Project/.dotnet/dotnet clean C:\Project/test.csproj"] = 0; actions.RunProcess[@"C:\Project/.dotnet/dotnet restore C:\Project/test.csproj"] = 0; - actions.RunProcess[@"C:\odasa/tools/odasa index --auto C:\Project/.dotnet/dotnet build --no-incremental C:\Project/test.csproj"] = 0; + actions.RunProcess[@"C:\odasa/tools/odasa index --auto C:\Project/.dotnet/dotnet build --no-incremental /p:UseSharedCompilation=false C:\Project/test.csproj"] = 0; actions.FileExists["csharp.log"] = true; actions.FileExists["test.csproj"] = true; actions.GetEnvironmentVariable["CODEQL_EXTRACTOR_CSHARP_TRAP_DIR"] = ""; @@ -933,7 +916,7 @@ Microsoft.NETCore.App 3.0.0 [/usr/local/share/dotnet/shared/Microsoft.NETCore.Ap actions.DownloadFiles.Add(("https://dot.net/v1/dotnet-install.sh", "dotnet-install.sh")); var autobuilder = CreateAutoBuilder(false, dotnetVersion: "2.1.3"); - TestAutobuilderScript(autobuilder, 0, 9); + TestAutobuilderScript(autobuilder, 0, 8); } [Fact] @@ -945,11 +928,6 @@ Microsoft.NETCore.App 3.0.0 [/usr/local/share/dotnet/shared/Microsoft.NETCore.Ap actions.RunProcess[@"chmod u+x dotnet-install.sh"] = 0; actions.RunProcess[@"./dotnet-install.sh --channel release --version 2.1.3 --install-dir C:\Project/.dotnet"] = 0; actions.RunProcess[@"rm dotnet-install.sh"] = 0; - actions.RunProcess[@"C:\Project/.dotnet/dotnet --list-runtimes"] = 0; - actions.RunProcessOut[@"C:\Project/.dotnet/dotnet --list-runtimes"] = @"Microsoft.AspNetCore.App 2.1.3 [/usr/local/share/dotnet/shared/Microsoft.AspNetCore.App] -Microsoft.AspNetCore.App 2.1.4 [/usr/local/share/dotnet/shared/Microsoft.AspNetCore.App] -Microsoft.NETCore.App 2.1.3 [/usr/local/share/dotnet/shared/Microsoft.NETCore.App] -Microsoft.NETCore.App 2.1.4 [/usr/local/share/dotnet/shared/Microsoft.NETCore.App]"; actions.RunProcess[@"C:\Project/.dotnet/dotnet --info"] = 0; actions.RunProcess[@"C:\Project/.dotnet/dotnet clean C:\Project/test.csproj"] = 0; actions.RunProcess[@"C:\Project/.dotnet/dotnet restore C:\Project/test.csproj"] = 0; @@ -973,7 +951,7 @@ Microsoft.NETCore.App 2.1.4 [/usr/local/share/dotnet/shared/Microsoft.NETCore.Ap actions.DownloadFiles.Add(("https://dot.net/v1/dotnet-install.sh", "dotnet-install.sh")); var autobuilder = CreateAutoBuilder(false, dotnetVersion: "2.1.3"); - TestAutobuilderScript(autobuilder, 0, 9); + TestAutobuilderScript(autobuilder, 0, 8); } private void TestDotnetVersionWindows(Action action, int commandsRun) @@ -984,7 +962,7 @@ Microsoft.NETCore.App 2.1.4 [/usr/local/share/dotnet/shared/Microsoft.NETCore.Ap actions.RunProcess[@"cmd.exe /C C:\Project\.dotnet\dotnet --info"] = 0; actions.RunProcess[@"cmd.exe /C C:\Project\.dotnet\dotnet clean C:\Project\test.csproj"] = 0; actions.RunProcess[@"cmd.exe /C C:\Project\.dotnet\dotnet restore C:\Project\test.csproj"] = 0; - actions.RunProcess[@"cmd.exe /C C:\odasa\tools\odasa index --auto C:\Project\.dotnet\dotnet build --no-incremental C:\Project\test.csproj"] = 0; + actions.RunProcess[@"cmd.exe /C C:\odasa\tools\odasa index --auto C:\Project\.dotnet\dotnet build --no-incremental /p:UseSharedCompilation=false C:\Project\test.csproj"] = 0; actions.FileExists["csharp.log"] = true; actions.FileExists[@"C:\Project\test.csproj"] = true; actions.GetEnvironmentVariable["CODEQL_EXTRACTOR_CSHARP_TRAP_DIR"] = ""; diff --git a/csharp/autobuilder/Semmle.Autobuild.CSharp/DotNetRule.cs b/csharp/autobuilder/Semmle.Autobuild.CSharp/DotNetRule.cs index a456c9407db..955775d0f66 100644 --- a/csharp/autobuilder/Semmle.Autobuild.CSharp/DotNetRule.cs +++ b/csharp/autobuilder/Semmle.Autobuild.CSharp/DotNetRule.cs @@ -36,7 +36,7 @@ namespace Semmle.Autobuild.CSharp builder.Log(Severity.Info, "Attempting to build using .NET Core"); } - return WithDotNet(builder, (dotNetPath, environment, compatibleClr) => + return WithDotNet(builder, (dotNetPath, environment) => { var ret = GetInfoCommand(builder.Actions, dotNetPath, environment); foreach (var projectOrSolution in builder.ProjectsOrSolutionsToBuild) @@ -49,7 +49,7 @@ namespace Semmle.Autobuild.CSharp restoreCommand.QuoteArgument(projectOrSolution.FullPath); var restore = restoreCommand.Script; - var build = GetBuildScript(builder, dotNetPath, environment, compatibleClr, projectOrSolution.FullPath); + var build = GetBuildScript(builder, dotNetPath, environment, projectOrSolution.FullPath); ret &= BuildScript.Try(clean) & BuildScript.Try(restore) & build; } @@ -57,7 +57,7 @@ namespace Semmle.Autobuild.CSharp }); } - private static BuildScript WithDotNet(Autobuilder builder, Func?, bool, BuildScript> f) + private static BuildScript WithDotNet(Autobuilder builder, Func?, BuildScript> f) { var installDir = builder.Actions.PathCombine(builder.Options.RootDirectory, ".dotnet"); var installScript = DownloadDotNet(builder, installDir); @@ -81,35 +81,10 @@ namespace Semmle.Autobuild.CSharp env = null; } - // The CLR tracer is always compatible on Windows - if (builder.Actions.IsWindows()) - return f(installDir, env, true); - - // The CLR tracer is only compatible on .NET Core >= 3 on Linux and macOS (see - // https://github.com/dotnet/coreclr/issues/19622) - return BuildScript.Bind(GetInstalledRuntimesScript(builder.Actions, installDir, env), (runtimes, runtimesRet) => - { - var compatibleClr = false; - if (runtimesRet == 0) - { - var minimumVersion = new Version(3, 0); - var regex = new Regex(@"Microsoft\.NETCore\.App (\d\.\d\.\d)"); - compatibleClr = runtimes - .Select(runtime => regex.Match(runtime)) - .Where(m => m.Success) - .Select(m => m.Groups[1].Value) - .Any(m => Version.TryParse(m, out var v) && v >= minimumVersion); - } - - if (!compatibleClr) - { - if (env is null) - env = new Dictionary(); - env.Add("UseSharedCompilation", "false"); - } - - return f(installDir, env, compatibleClr); - }); + if (env is null) + env = new Dictionary(); + env.Add("UseSharedCompilation", "false"); + return f(installDir, env); }); } @@ -122,7 +97,7 @@ namespace Semmle.Autobuild.CSharp /// are needed). /// public static BuildScript WithDotNet(Autobuilder builder, Func?, BuildScript> f) - => WithDotNet(builder, (_1, env, _2) => f(env)); + => WithDotNet(builder, (_1, env) => f(env)); /// /// Returns a script for downloading relevant versions of the @@ -259,14 +234,6 @@ namespace Semmle.Autobuild.CSharp return restore; } - private static BuildScript GetInstalledRuntimesScript(IBuildActions actions, string? dotNetPath, IDictionary? environment) - { - var listSdks = new CommandBuilder(actions, environment: environment, silent: true). - RunCommand(DotNetCommand(actions, dotNetPath)). - Argument("--list-runtimes"); - return listSdks.Script; - } - /// /// Gets the `dotnet build` script. /// @@ -276,17 +243,14 @@ namespace Semmle.Autobuild.CSharp /// hence the need for CLR tracing), by adding a /// `/p:UseSharedCompilation=false` argument. /// - private static BuildScript GetBuildScript(Autobuilder builder, string? dotNetPath, IDictionary? environment, bool compatibleClr, string projOrSln) + private static BuildScript GetBuildScript(Autobuilder builder, string? dotNetPath, IDictionary? environment, string projOrSln) { var build = new CommandBuilder(builder.Actions, null, environment); var script = builder.MaybeIndex(build, DotNetCommand(builder.Actions, dotNetPath)). Argument("build"). Argument("--no-incremental"); - return compatibleClr ? - script.Argument(builder.Options.DotNetArguments). - QuoteArgument(projOrSln). - Script : + return script.Argument("/p:UseSharedCompilation=false"). Argument(builder.Options.DotNetArguments). QuoteArgument(projOrSln). From bf4d88175c9727414780b04802fa0e1ed8e92e71 Mon Sep 17 00:00:00 2001 From: Sebastian Bauersfeld Date: Wed, 12 May 2021 16:40:00 +0700 Subject: [PATCH 124/168] Consider boxed booleans to avoid false positives for XXE.ql --- java/ql/src/semmle/code/java/security/XmlParsers.qll | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/java/ql/src/semmle/code/java/security/XmlParsers.qll b/java/ql/src/semmle/code/java/security/XmlParsers.qll index 685c5754fc9..5e2eb1caf8a 100644 --- a/java/ql/src/semmle/code/java/security/XmlParsers.qll +++ b/java/ql/src/semmle/code/java/security/XmlParsers.qll @@ -36,7 +36,10 @@ abstract class ParserConfig extends MethodAccess { */ predicate disables(Expr e) { this.getArgument(0) = e and - this.getArgument(1).(BooleanLiteral).getBooleanValue() = false + ( + this.getArgument(1).(BooleanLiteral).getBooleanValue() = false or + this.getArgument(1).(FieldAccess).getField().hasQualifiedName("java.lang", "Boolean", "FALSE") + ) } /** @@ -44,7 +47,10 @@ abstract class ParserConfig extends MethodAccess { */ predicate enables(Expr e) { this.getArgument(0) = e and - this.getArgument(1).(BooleanLiteral).getBooleanValue() = true + ( + this.getArgument(1).(BooleanLiteral).getBooleanValue() = true or + this.getArgument(1).(FieldAccess).getField().hasQualifiedName("java.lang", "Boolean", "TRUE") + ) } } From b05512a9585def1ecb9fd2c35cfebbcc70ab6ef8 Mon Sep 17 00:00:00 2001 From: Sebastian Bauersfeld Date: Wed, 12 May 2021 16:58:24 +0700 Subject: [PATCH 125/168] Add change notes. --- java/change-notes/2021-05-12-xxe-fp-fix.md | 2 ++ 1 file changed, 2 insertions(+) create mode 100644 java/change-notes/2021-05-12-xxe-fp-fix.md diff --git a/java/change-notes/2021-05-12-xxe-fp-fix.md b/java/change-notes/2021-05-12-xxe-fp-fix.md new file mode 100644 index 00000000000..dd42bc71256 --- /dev/null +++ b/java/change-notes/2021-05-12-xxe-fp-fix.md @@ -0,0 +1,2 @@ +lgtm,codescanning +* The query "Resolving XML external entity in user-controlled data" (`java/xxe`) has been improved to report fewer false positives when a Builder / Factory (e.g. an `XMLInputFactory`) is configured safely by using a boxed boolean as second argument to one or more of its configuration methods. From 48b50f93c27c60ad41ebd112f6b699ac987503fa Mon Sep 17 00:00:00 2001 From: Jonathan Leitschuh Date: Wed, 12 May 2021 08:58:01 -0400 Subject: [PATCH 126/168] Update java/ql/src/semmle/code/java/frameworks/jackson/JacksonSerializability.qll Co-authored-by: Tony Torralba --- .../code/java/frameworks/jackson/JacksonSerializability.qll | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/java/ql/src/semmle/code/java/frameworks/jackson/JacksonSerializability.qll b/java/ql/src/semmle/code/java/frameworks/jackson/JacksonSerializability.qll index 09fd419642e..50266c377f8 100644 --- a/java/ql/src/semmle/code/java/frameworks/jackson/JacksonSerializability.qll +++ b/java/ql/src/semmle/code/java/frameworks/jackson/JacksonSerializability.qll @@ -53,7 +53,10 @@ private class JacksonWriteValueMethod extends Method, TaintPreservingCallable { private class JacksonReadValueMethod extends Method, TaintPreservingCallable { JacksonReadValueMethod() { - getDeclaringType().hasQualifiedName("com.fasterxml.jackson.databind", "ObjectReader") and + ( + getDeclaringType().hasQualifiedName("com.fasterxml.jackson.databind", "ObjectReader") or + getDeclaringType().hasQualifiedName("com.fasterxml.jackson.databind", "ObjectMapper") + ) and hasName(["readValue", "readValues"]) } From e94dab70b50ce64f692fb6e8db93149ce063f9d5 Mon Sep 17 00:00:00 2001 From: Mathias Vorreiter Pedersen Date: Wed, 12 May 2021 15:44:09 +0200 Subject: [PATCH 127/168] C++: Add sanitizers to cpp/uncontrolled-arithmetic. --- .../CWE/CWE-190/ArithmeticUncontrolled.ql | 111 +++++++++++++----- .../ArithmeticUncontrolled.expected | 46 -------- .../CWE/CWE-190/semmle/uncontrolled/test.c | 6 +- 3 files changed, 84 insertions(+), 79 deletions(-) diff --git a/cpp/ql/src/Security/CWE/CWE-190/ArithmeticUncontrolled.ql b/cpp/ql/src/Security/CWE/CWE-190/ArithmeticUncontrolled.ql index a4b0f131d14..6aad6cca7ce 100644 --- a/cpp/ql/src/Security/CWE/CWE-190/ArithmeticUncontrolled.ql +++ b/cpp/ql/src/Security/CWE/CWE-190/ArithmeticUncontrolled.ql @@ -15,34 +15,99 @@ import cpp import semmle.code.cpp.security.Overflow import semmle.code.cpp.security.Security import semmle.code.cpp.security.TaintTracking +import semmle.code.cpp.rangeanalysis.SimpleRangeAnalysis import TaintedWithPath -predicate isRandCall(FunctionCall fc) { fc.getTarget().getName() = "rand" } - -predicate isRandCallOrParent(Expr e) { - isRandCall(e) or - isRandCallOrParent(e.getAChild()) +predicate isUnboundedRandCall(FunctionCall fc) { + fc.getTarget().getName() = "rand" and not bounded(fc) } -predicate isRandValue(Expr e) { - isRandCall(e) +/** + * An operand `e` of a division expression (i.e., `e` is an operand of either a `DivExpr` or + * a `AssignDivExpr`) is bounded when `e` is the left-hand side of the division. + */ +pragma[inline] +predicate boundedDiv(Expr e, Expr left) { e = left } + +/** + * An operand `e` of a remainder expression `rem` (i.e., `rem` is either a `RemExpr` or + * an `AssignRemExpr`) with left-hand side `left` and right-ahnd side `right` is bounded + * when `e` is `left` and `right` is upper bounded by some number that is less than the maximum integer + * allowed by the result type of `rem`. + */ +pragma[inline] +predicate boundedRem(Expr e, Expr rem, Expr left, Expr right) { + e = left and + upperBound(right.getFullyConverted()) < exprMaxVal(rem.getFullyConverted()) +} + +/** + * An operand `e` of a bitwise and expression `andExpr` (i.e., `andExpr` is either an `BitwiseAndExpr` + * or an `AssignAndExpr`) with operands `operand1` and `operand2` is the operand that is not `e` is upper + * bounded by some number that is less than the maximum integer allowed by the result type of `andExpr`. + */ +pragma[inline] +predicate boundedBitwiseAnd(Expr e, Expr andExpr, Expr operand1, Expr operand2) { + operand1 != operand2 and + e = operand1 and + upperBound(operand2.getFullyConverted()) < exprMaxVal(andExpr.getFullyConverted()) +} + +/** + * Holds if `fc` is a part of the left operand of a binary operation that greatly reduces the range + * of possible values. + */ +predicate bounded(Expr e) { + // For `%` and `&` we require that `e` is bounded by a value that is strictly smaller than the + // maximum possible value of the result type of the operation. + // For example, the function call `rand()` is considered bounded in the following program: + // ``` + // int i = rand() % (UINT8_MAX + 1); + // ``` + // but not in: + // ``` + // unsigned char uc = rand() % (UINT8_MAX + 1); + // ``` + exists(RemExpr rem | boundedRem(e, rem, rem.getLeftOperand(), rem.getRightOperand())) + or + exists(AssignRemExpr rem | boundedRem(e, rem, rem.getLValue(), rem.getRValue())) + or + exists(BitwiseAndExpr andExpr | + boundedBitwiseAnd(e, andExpr, andExpr.getAnOperand(), andExpr.getAnOperand()) + ) + or + exists(AssignAndExpr andExpr | + boundedBitwiseAnd(e, andExpr, andExpr.getAnOperand(), andExpr.getAnOperand()) + ) + or + // Optimitically assume that a division always yields a much smaller value. + boundedDiv(e, any(DivExpr div).getLeftOperand()) + or + boundedDiv(e, any(AssignDivExpr div).getLValue()) +} + +predicate isUnboundedRandCallOrParent(Expr e) { + isUnboundedRandCall(e) + or + isUnboundedRandCallOrParent(e.getAChild()) +} + +predicate isUnboundedRandValue(Expr e) { + isUnboundedRandCall(e) or exists(MacroInvocation mi | e = mi.getExpr() and - isRandCallOrParent(e) + isUnboundedRandCallOrParent(e) ) } class SecurityOptionsArith extends SecurityOptions { override predicate isUserInput(Expr expr, string cause) { - isRandValue(expr) and - cause = "rand" and - not expr.getParent*() instanceof DivExpr + isUnboundedRandValue(expr) and + cause = "rand" } } -predicate isDiv(VariableAccess va) { exists(AssignDivExpr div | div.getLValue() = va) } - predicate missingGuard(VariableAccess va, string effect) { exists(Operation op | op.getAnOperand() = va | missingGuardAgainstUnderflow(op, va) and effect = "underflow" @@ -52,29 +117,15 @@ predicate missingGuard(VariableAccess va, string effect) { } class Configuration extends TaintTrackingConfiguration { - override predicate isSink(Element e) { - isDiv(e) - or - missingGuard(e, _) - } -} + override predicate isSink(Element e) { missingGuard(e, _) } -/** - * A value that undergoes division is likely to be bounded within a safe - * range. - */ -predicate guardedByAssignDiv(Expr origin) { - exists(VariableAccess va | - taintedWithPath(origin, va, _, _) and - isDiv(va) - ) + override predicate isBarrier(Expr e) { super.isBarrier(e) or bounded(e) } } from Expr origin, VariableAccess va, string effect, PathNode sourceNode, PathNode sinkNode where taintedWithPath(origin, va, sourceNode, sinkNode) and - missingGuard(va, effect) and - not guardedByAssignDiv(origin) + missingGuard(va, effect) select va, sourceNode, sinkNode, "$@ flows to here and is used in arithmetic, potentially causing an " + effect + ".", origin, "Uncontrolled value" diff --git a/cpp/ql/test/query-tests/Security/CWE/CWE-190/semmle/uncontrolled/ArithmeticUncontrolled.expected b/cpp/ql/test/query-tests/Security/CWE/CWE-190/semmle/uncontrolled/ArithmeticUncontrolled.expected index ca8dd38fc3b..097efb73b9f 100644 --- a/cpp/ql/test/query-tests/Security/CWE/CWE-190/semmle/uncontrolled/ArithmeticUncontrolled.expected +++ b/cpp/ql/test/query-tests/Security/CWE/CWE-190/semmle/uncontrolled/ArithmeticUncontrolled.expected @@ -7,30 +7,10 @@ edges | test.c:34:13:34:18 | call to rand | test.c:35:5:35:5 | r | | test.c:34:13:34:18 | call to rand | test.c:35:5:35:5 | r | | test.c:34:13:34:18 | call to rand | test.c:35:5:35:5 | r | -| test.c:39:13:39:21 | ... % ... | test.c:40:5:40:5 | r | -| test.c:39:13:39:21 | ... % ... | test.c:40:5:40:5 | r | -| test.c:39:13:39:21 | ... % ... | test.c:40:5:40:5 | r | -| test.c:39:13:39:21 | ... % ... | test.c:40:5:40:5 | r | | test.c:44:13:44:16 | call to rand | test.c:45:5:45:5 | r | | test.c:44:13:44:16 | call to rand | test.c:45:5:45:5 | r | | test.c:44:13:44:16 | call to rand | test.c:45:5:45:5 | r | | test.c:44:13:44:16 | call to rand | test.c:45:5:45:5 | r | -| test.c:54:13:54:16 | call to rand | test.c:56:5:56:5 | r | -| test.c:54:13:54:16 | call to rand | test.c:56:5:56:5 | r | -| test.c:54:13:54:16 | call to rand | test.c:56:5:56:5 | r | -| test.c:54:13:54:16 | call to rand | test.c:56:5:56:5 | r | -| test.c:60:13:60:16 | call to rand | test.c:61:5:61:5 | r | -| test.c:60:13:60:16 | call to rand | test.c:61:5:61:5 | r | -| test.c:60:13:60:16 | call to rand | test.c:61:5:61:5 | r | -| test.c:60:13:60:16 | call to rand | test.c:61:5:61:5 | r | -| test.c:60:13:60:16 | call to rand | test.c:62:5:62:5 | r | -| test.c:60:13:60:16 | call to rand | test.c:62:5:62:5 | r | -| test.c:60:13:60:16 | call to rand | test.c:62:5:62:5 | r | -| test.c:60:13:60:16 | call to rand | test.c:62:5:62:5 | r | -| test.c:66:13:66:16 | call to rand | test.c:67:5:67:5 | r | -| test.c:66:13:66:16 | call to rand | test.c:67:5:67:5 | r | -| test.c:66:13:66:16 | call to rand | test.c:67:5:67:5 | r | -| test.c:66:13:66:16 | call to rand | test.c:67:5:67:5 | r | | test.c:75:13:75:19 | ... ^ ... | test.c:77:9:77:9 | r | | test.c:75:13:75:19 | ... ^ ... | test.c:77:9:77:9 | r | | test.c:75:13:75:19 | ... ^ ... | test.c:77:9:77:9 | r | @@ -67,34 +47,11 @@ nodes | test.c:35:5:35:5 | r | semmle.label | r | | test.c:35:5:35:5 | r | semmle.label | r | | test.c:35:5:35:5 | r | semmle.label | r | -| test.c:39:13:39:21 | ... % ... | semmle.label | ... % ... | -| test.c:39:13:39:21 | ... % ... | semmle.label | ... % ... | -| test.c:40:5:40:5 | r | semmle.label | r | -| test.c:40:5:40:5 | r | semmle.label | r | -| test.c:40:5:40:5 | r | semmle.label | r | | test.c:44:13:44:16 | call to rand | semmle.label | call to rand | | test.c:44:13:44:16 | call to rand | semmle.label | call to rand | | test.c:45:5:45:5 | r | semmle.label | r | | test.c:45:5:45:5 | r | semmle.label | r | | test.c:45:5:45:5 | r | semmle.label | r | -| test.c:54:13:54:16 | call to rand | semmle.label | call to rand | -| test.c:54:13:54:16 | call to rand | semmle.label | call to rand | -| test.c:56:5:56:5 | r | semmle.label | r | -| test.c:56:5:56:5 | r | semmle.label | r | -| test.c:56:5:56:5 | r | semmle.label | r | -| test.c:60:13:60:16 | call to rand | semmle.label | call to rand | -| test.c:60:13:60:16 | call to rand | semmle.label | call to rand | -| test.c:61:5:61:5 | r | semmle.label | r | -| test.c:61:5:61:5 | r | semmle.label | r | -| test.c:61:5:61:5 | r | semmle.label | r | -| test.c:62:5:62:5 | r | semmle.label | r | -| test.c:62:5:62:5 | r | semmle.label | r | -| test.c:62:5:62:5 | r | semmle.label | r | -| test.c:66:13:66:16 | call to rand | semmle.label | call to rand | -| test.c:66:13:66:16 | call to rand | semmle.label | call to rand | -| test.c:67:5:67:5 | r | semmle.label | r | -| test.c:67:5:67:5 | r | semmle.label | r | -| test.c:67:5:67:5 | r | semmle.label | r | | test.c:75:13:75:19 | ... ^ ... | semmle.label | ... ^ ... | | test.c:75:13:75:19 | ... ^ ... | semmle.label | ... ^ ... | | test.c:77:9:77:9 | r | semmle.label | r | @@ -133,10 +90,7 @@ nodes #select | test.c:21:17:21:17 | r | test.c:18:13:18:16 | call to rand | test.c:21:17:21:17 | r | $@ flows to here and is used in arithmetic, potentially causing an overflow. | test.c:18:13:18:16 | call to rand | Uncontrolled value | | test.c:35:5:35:5 | r | test.c:34:13:34:18 | call to rand | test.c:35:5:35:5 | r | $@ flows to here and is used in arithmetic, potentially causing an overflow. | test.c:34:13:34:18 | call to rand | Uncontrolled value | -| test.c:40:5:40:5 | r | test.c:39:13:39:21 | ... % ... | test.c:40:5:40:5 | r | $@ flows to here and is used in arithmetic, potentially causing an overflow. | test.c:39:13:39:21 | ... % ... | Uncontrolled value | | test.c:45:5:45:5 | r | test.c:44:13:44:16 | call to rand | test.c:45:5:45:5 | r | $@ flows to here and is used in arithmetic, potentially causing an overflow. | test.c:44:13:44:16 | call to rand | Uncontrolled value | -| test.c:56:5:56:5 | r | test.c:54:13:54:16 | call to rand | test.c:56:5:56:5 | r | $@ flows to here and is used in arithmetic, potentially causing an overflow. | test.c:54:13:54:16 | call to rand | Uncontrolled value | -| test.c:67:5:67:5 | r | test.c:66:13:66:16 | call to rand | test.c:67:5:67:5 | r | $@ flows to here and is used in arithmetic, potentially causing an overflow. | test.c:66:13:66:16 | call to rand | Uncontrolled value | | test.c:77:9:77:9 | r | test.c:75:13:75:19 | ... ^ ... | test.c:77:9:77:9 | r | $@ flows to here and is used in arithmetic, potentially causing an underflow. | test.c:75:13:75:19 | ... ^ ... | Uncontrolled value | | test.c:100:5:100:5 | r | test.c:99:14:99:19 | call to rand | test.c:100:5:100:5 | r | $@ flows to here and is used in arithmetic, potentially causing an underflow. | test.c:99:14:99:19 | call to rand | Uncontrolled value | | test.cpp:25:7:25:7 | r | test.cpp:8:9:8:12 | call to rand | test.cpp:25:7:25:7 | r | $@ flows to here and is used in arithmetic, potentially causing an overflow. | test.cpp:8:9:8:12 | call to rand | Uncontrolled value | diff --git a/cpp/ql/test/query-tests/Security/CWE/CWE-190/semmle/uncontrolled/test.c b/cpp/ql/test/query-tests/Security/CWE/CWE-190/semmle/uncontrolled/test.c index 2b67b499a3c..61f39a8e851 100644 --- a/cpp/ql/test/query-tests/Security/CWE/CWE-190/semmle/uncontrolled/test.c +++ b/cpp/ql/test/query-tests/Security/CWE/CWE-190/semmle/uncontrolled/test.c @@ -37,7 +37,7 @@ void randomTester() { { int r = RANDN(100); - r += 100; // GOOD: The return from RANDN is bounded [FALSE POSITIVE] + r += 100; // GOOD: The return from RANDN is bounded } { @@ -53,7 +53,7 @@ void randomTester() { { int r = rand(); r = r / 10; - r += 100; // GOOD [FALSE POSITIVE] + r += 100; // GOOD } { @@ -64,7 +64,7 @@ void randomTester() { { int r = rand() & 0xFF; - r += 100; // GOOD [FALSE POSITIVE] + r += 100; // GOOD } { From e0f78dde56f7f31719c91cd0367922046edaa2ab Mon Sep 17 00:00:00 2001 From: Erik Krogh Kristensen Date: Wed, 12 May 2021 16:23:37 +0200 Subject: [PATCH 128/168] make the axios error catch match the non-error case --- .../ql/src/semmle/javascript/frameworks/ClientRequests.qll | 3 +-- .../frameworks/ClientRequests/ClientRequests.expected | 4 +++- .../ql/test/library-tests/frameworks/ClientRequests/tst.js | 1 + 3 files changed, 5 insertions(+), 3 deletions(-) diff --git a/javascript/ql/src/semmle/javascript/frameworks/ClientRequests.qll b/javascript/ql/src/semmle/javascript/frameworks/ClientRequests.qll index 477f9354d03..bce5d6a36fc 100644 --- a/javascript/ql/src/semmle/javascript/frameworks/ClientRequests.qll +++ b/javascript/ql/src/semmle/javascript/frameworks/ClientRequests.qll @@ -271,8 +271,7 @@ module ClientRequest { or responseType = getResponseType() and promise = false and - result = - getReturn().getPromisedError().getMember("response").getMember("data").getAnImmediateUse() + result = getReturn().getPromisedError().getMember("response").getAnImmediateUse() } } diff --git a/javascript/ql/test/library-tests/frameworks/ClientRequests/ClientRequests.expected b/javascript/ql/test/library-tests/frameworks/ClientRequests/ClientRequests.expected index c6542eb009d..27a9fa10f72 100644 --- a/javascript/ql/test/library-tests/frameworks/ClientRequests/ClientRequests.expected +++ b/javascript/ql/test/library-tests/frameworks/ClientRequests/ClientRequests.expected @@ -298,4 +298,6 @@ test_getAResponseDataNode | tst.js:235:5:237:6 | needle. ... \\n }) | tst.js:235:73:235:76 | body | json | false | | tst.js:286:20:286:55 | new Web ... :8080') | tst.js:291:44:291:53 | event.data | json | false | | tst.js:296:5:299:6 | axios({ ... \\n }) | tst.js:296:5:299:6 | axios({ ... \\n }) | json | true | -| tst.js:296:5:299:6 | axios({ ... \\n }) | tst.js:303:26:303:42 | err.response.data | json | false | +| tst.js:296:5:299:6 | axios({ ... \\n }) | tst.js:302:28:302:39 | err.response | json | false | +| tst.js:296:5:299:6 | axios({ ... \\n }) | tst.js:303:26:303:37 | err.response | json | false | +| tst.js:296:5:299:6 | axios({ ... \\n }) | tst.js:304:27:304:38 | err.response | json | false | diff --git a/javascript/ql/test/library-tests/frameworks/ClientRequests/tst.js b/javascript/ql/test/library-tests/frameworks/ClientRequests/tst.js index f284ffaa407..40dcfc481f4 100644 --- a/javascript/ql/test/library-tests/frameworks/ClientRequests/tst.js +++ b/javascript/ql/test/library-tests/frameworks/ClientRequests/tst.js @@ -301,6 +301,7 @@ function moreAxios() { (err) => { const status = err.response.status; const data = err.response.data; + const agent = err.response.headers.useragent; } ); } \ No newline at end of file From 7d26aca793353c8564d5cac8715dbcecd87aa917 Mon Sep 17 00:00:00 2001 From: Mathias Vorreiter Pedersen Date: Wed, 12 May 2021 16:34:23 +0200 Subject: [PATCH 129/168] C++: Add change-note. --- cpp/change-notes/2021-12-05-uncontrolled-arithmetic.md | 2 ++ 1 file changed, 2 insertions(+) create mode 100644 cpp/change-notes/2021-12-05-uncontrolled-arithmetic.md diff --git a/cpp/change-notes/2021-12-05-uncontrolled-arithmetic.md b/cpp/change-notes/2021-12-05-uncontrolled-arithmetic.md new file mode 100644 index 00000000000..56fbc9a44ce --- /dev/null +++ b/cpp/change-notes/2021-12-05-uncontrolled-arithmetic.md @@ -0,0 +1,2 @@ +lgtm,codescanning +* The query "Uncontrolled arithmetic" (`cpp/uncontrolled-arithmetic`) has been improved to produce fewer false positives. From 34fbafafde8be89e1383b378c052cbcdcdcc7000 Mon Sep 17 00:00:00 2001 From: Erik Krogh Kristensen Date: Wed, 12 May 2021 22:34:44 +0200 Subject: [PATCH 130/168] remove redundant "put" case --- .../ql/src/semmle/javascript/frameworks/ClientRequests.qll | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/javascript/ql/src/semmle/javascript/frameworks/ClientRequests.qll b/javascript/ql/src/semmle/javascript/frameworks/ClientRequests.qll index bce5d6a36fc..18204c5b59b 100644 --- a/javascript/ql/src/semmle/javascript/frameworks/ClientRequests.qll +++ b/javascript/ql/src/semmle/javascript/frameworks/ClientRequests.qll @@ -242,7 +242,7 @@ module ClientRequest { method = "request" and result = getOptionArgument(0, "data") or - method = ["post", "put", "put"] and + method = ["post", "put"] and result = [getArgument(1), getOptionArgument(2, "data")] or result = getOptionArgument([0 .. 2], ["headers", "params"]) From effa2b162a77031186bf26114e2e6e0e6e847154 Mon Sep 17 00:00:00 2001 From: haby0 Date: Thu, 6 May 2021 12:05:26 +0800 Subject: [PATCH 131/168] Add spring url redirection detect --- .../CWE/CWE-601/SpringUrlRedirect.java | 46 +++++++ .../CWE/CWE-601/SpringUrlRedirect.qhelp | 37 +++++ .../Security/CWE/CWE-601/SpringUrlRedirect.ql | 42 ++++++ .../CWE/CWE-601/SpringUrlRedirect.qll | 91 ++++++++++++ .../CWE-601/SpringUrlRedirect.expected | 19 +++ .../security/CWE-601/SpringUrlRedirect.java | 52 +++++++ .../security/CWE-601/SpringUrlRedirect.qlref | 1 + .../query-tests/security/CWE-601/options | 1 + .../web/servlet/ModelAndView.java | 107 +++++++++++++++ .../org/springframework/web/servlet/View.java | 20 +++ .../servlet/view/AbstractUrlBasedView.java | 39 ++++++ .../web/servlet/view/RedirectView.java | 129 ++++++++++++++++++ 12 files changed, 584 insertions(+) create mode 100644 java/ql/src/experimental/Security/CWE/CWE-601/SpringUrlRedirect.java create mode 100644 java/ql/src/experimental/Security/CWE/CWE-601/SpringUrlRedirect.qhelp create mode 100644 java/ql/src/experimental/Security/CWE/CWE-601/SpringUrlRedirect.ql create mode 100644 java/ql/src/experimental/Security/CWE/CWE-601/SpringUrlRedirect.qll create mode 100644 java/ql/test/experimental/query-tests/security/CWE-601/SpringUrlRedirect.expected create mode 100644 java/ql/test/experimental/query-tests/security/CWE-601/SpringUrlRedirect.java create mode 100644 java/ql/test/experimental/query-tests/security/CWE-601/SpringUrlRedirect.qlref create mode 100644 java/ql/test/experimental/query-tests/security/CWE-601/options create mode 100644 java/ql/test/stubs/springframework-5.2.3/org/springframework/web/servlet/ModelAndView.java create mode 100644 java/ql/test/stubs/springframework-5.2.3/org/springframework/web/servlet/View.java create mode 100644 java/ql/test/stubs/springframework-5.2.3/org/springframework/web/servlet/view/AbstractUrlBasedView.java create mode 100644 java/ql/test/stubs/springframework-5.2.3/org/springframework/web/servlet/view/RedirectView.java diff --git a/java/ql/src/experimental/Security/CWE/CWE-601/SpringUrlRedirect.java b/java/ql/src/experimental/Security/CWE/CWE-601/SpringUrlRedirect.java new file mode 100644 index 00000000000..eba64aab6a8 --- /dev/null +++ b/java/ql/src/experimental/Security/CWE/CWE-601/SpringUrlRedirect.java @@ -0,0 +1,46 @@ +import javax.servlet.http.HttpServletResponse; +import org.springframework.stereotype.Controller; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.servlet.ModelAndView; +import org.springframework.web.servlet.view.RedirectView; + +@Controller +public class SpringUrlRedirect { + + private final static String VALID_REDIRECT = "http://127.0.0.1"; + + @GetMapping("url1") + public RedirectView bad1(String redirectUrl, HttpServletResponse response) throws Exception { + RedirectView rv = new RedirectView(); + rv.setUrl(redirectUrl); + return rv; + } + + @GetMapping("url2") + public String bad2(String redirectUrl) { + String url = "redirect:" + redirectUrl; + return url; + } + + @GetMapping("url3") + public RedirectView bad3(String redirectUrl) { + RedirectView rv = new RedirectView(redirectUrl); + return rv; + } + + @GetMapping("url4") + public ModelAndView bad4(String redirectUrl) { + return new ModelAndView("redirect:" + redirectUrl); + } + + @GetMapping("url5") + public RedirectView good1(String redirectUrl) { + RedirectView rv = new RedirectView(); + if (redirectUrl.startsWith(VALID_REDIRECT)){ + rv.setUrl(redirectUrl); + }else { + rv.setUrl(VALID_REDIRECT); + } + return rv; + } +} diff --git a/java/ql/src/experimental/Security/CWE/CWE-601/SpringUrlRedirect.qhelp b/java/ql/src/experimental/Security/CWE/CWE-601/SpringUrlRedirect.qhelp new file mode 100644 index 00000000000..6fe70dfb113 --- /dev/null +++ b/java/ql/src/experimental/Security/CWE/CWE-601/SpringUrlRedirect.qhelp @@ -0,0 +1,37 @@ + + + + + +

    Directly incorporating user input into a URL redirect request without validating the input +can facilitate phishing attacks. In these attacks, unsuspecting users can be redirected to a +malicious site that looks very similar to the real site they intend to visit, but which is +controlled by the attacker.

    + +
    + + +

    To guard against untrusted URL redirection, it is advisable to avoid putting user input +directly into a redirect URL. Instead, maintain a list of authorized +redirects on the server; then choose from that list based on the user input provided.

    + +
    + + +

    The following examples show the bad case and the good case respectively. +In bad1 method and bad2 method and bad3 method and +bad4 method, shows an HTTP request parameter being used directly in a URL redirect +without validating the input, which facilitates phishing attacks. In good1 method, +shows how to solve this problem by verifying whether the user input is a known fixed string beginning. +

    + + + +
    + +
  • A Guide To Spring Redirects: Spring Redirects.
  • +
  • Url redirection - attack and defense: Url Redirection.
  • +
    +
    diff --git a/java/ql/src/experimental/Security/CWE/CWE-601/SpringUrlRedirect.ql b/java/ql/src/experimental/Security/CWE/CWE-601/SpringUrlRedirect.ql new file mode 100644 index 00000000000..138bce57ac9 --- /dev/null +++ b/java/ql/src/experimental/Security/CWE/CWE-601/SpringUrlRedirect.ql @@ -0,0 +1,42 @@ +/** + * @name Spring url redirection from remote source + * @description Spring url redirection based on unvalidated user-input + * may cause redirection to malicious web sites. + * @kind path-problem + * @problem.severity error + * @precision high + * @id java/spring-unvalidated-url-redirection + * @tags security + * external/cwe-601 + */ + +import java +import SpringUrlRedirect +import semmle.code.java.dataflow.FlowSources +import DataFlow::PathGraph + +class SpringUrlRedirectFlowConfig extends TaintTracking::Configuration { + SpringUrlRedirectFlowConfig() { this = "SpringUrlRedirectFlowConfig" } + + override predicate isSource(DataFlow::Node source) { source instanceof RemoteFlowSource } + + override predicate isSink(DataFlow::Node sink) { sink instanceof SpringUrlRedirectSink } + + override predicate isSanitizerGuard(DataFlow::BarrierGuard guard) { + guard instanceof StartsWithSanitizer + } + + override predicate isSanitizer(DataFlow::Node node) { + // Exclude the case where the left side of the concatenated string is not `redirect:`. + // E.g: `String url = "/path?token=" + request.getParameter("token");` + exists(AddExpr ae | + ae.getRightOperand() = node.asExpr() and + not ae instanceof RedirectBuilderExpr + ) + } +} + +from DataFlow::PathNode source, DataFlow::PathNode sink, SpringUrlRedirectFlowConfig conf +where conf.hasFlowPath(source, sink) +select sink.getNode(), source, sink, "Potentially untrusted URL redirection due to $@.", + source.getNode(), "user-provided value" diff --git a/java/ql/src/experimental/Security/CWE/CWE-601/SpringUrlRedirect.qll b/java/ql/src/experimental/Security/CWE/CWE-601/SpringUrlRedirect.qll new file mode 100644 index 00000000000..0ea88e84673 --- /dev/null +++ b/java/ql/src/experimental/Security/CWE/CWE-601/SpringUrlRedirect.qll @@ -0,0 +1,91 @@ +import java +import DataFlow +import semmle.code.java.dataflow.FlowSources +import semmle.code.java.dataflow.DataFlow2 +import semmle.code.java.dataflow.TaintTracking +import semmle.code.java.frameworks.spring.SpringController + +class StartsWithSanitizer extends DataFlow::BarrierGuard { + StartsWithSanitizer() { + this.(MethodAccess).getMethod().hasName("startsWith") and + this.(MethodAccess).getMethod().getDeclaringType() instanceof TypeString and + this.(MethodAccess).getMethod().getNumberOfParameters() = 1 + } + + override predicate checks(Expr e, boolean branch) { + e = this.(MethodAccess).getQualifier() and branch = true + } +} + +/** + * A concatenate expression using the string `redirect:` on the left. + * + * E.g: `"redirect:" + redirectUrl` + */ +class RedirectBuilderExpr extends AddExpr { + RedirectBuilderExpr() { + this.getLeftOperand().(CompileTimeConstantExpr).getStringValue() = "redirect:" + } +} + +/** A URL redirection sink from spring controller method. */ +class SpringUrlRedirectSink extends DataFlow::Node { + SpringUrlRedirectSink() { + exists(RedirectBuilderExpr rbe | rbe.getRightOperand() = this.asExpr()) + or + exists(MethodAccess ma | + ma.getMethod().hasName("setUrl") and + ma.getMethod() + .getDeclaringType() + .hasQualifiedName("org.springframework.web.servlet.view", "AbstractUrlBasedView") and + ma.getArgument(0) = this.asExpr() and + exists(RedirectViewFlowConfig rvfc | rvfc.hasFlowToExpr(ma.getQualifier())) + ) + or + exists(ClassInstanceExpr cie | + cie.getConstructedType() + .hasQualifiedName("org.springframework.web.servlet.view", "RedirectView") and + cie.getArgument(0) = this.asExpr() + ) + or + exists(ClassInstanceExpr cie | + cie.getConstructedType().hasQualifiedName("org.springframework.web.servlet", "ModelAndView") and + cie.getArgument(0) = this.asExpr() and + exists(RedirectBuilderFlowConfig rstrbfc | rstrbfc.hasFlowToExpr(cie.getArgument(0))) + ) + } +} + +/** A data flow configuration tracing flow from remote sources to redirect builder expression. */ +private class RedirectBuilderFlowConfig extends DataFlow2::Configuration { + RedirectBuilderFlowConfig() { this = "RedirectBuilderFlowConfig" } + + override predicate isSource(DataFlow::Node src) { src instanceof RemoteFlowSource } + + override predicate isSink(DataFlow::Node sink) { + exists(RedirectBuilderExpr rbe | rbe.getRightOperand() = sink.asExpr()) + } +} + +/** A data flow configuration tracing flow from RedirectView object to calling setUrl method. */ +private class RedirectViewFlowConfig extends DataFlow2::Configuration { + RedirectViewFlowConfig() { this = "RedirectViewFlowConfig" } + + override predicate isSource(DataFlow::Node src) { + exists(ClassInstanceExpr cie | + cie.getConstructedType() + .hasQualifiedName("org.springframework.web.servlet.view", "RedirectView") and + cie = src.asExpr() + ) + } + + override predicate isSink(DataFlow::Node sink) { + exists(MethodAccess ma | + ma.getMethod().hasName("setUrl") and + ma.getMethod() + .getDeclaringType() + .hasQualifiedName("org.springframework.web.servlet.view", "AbstractUrlBasedView") and + ma.getQualifier() = sink.asExpr() + ) + } +} diff --git a/java/ql/test/experimental/query-tests/security/CWE-601/SpringUrlRedirect.expected b/java/ql/test/experimental/query-tests/security/CWE-601/SpringUrlRedirect.expected new file mode 100644 index 00000000000..fee0598bbee --- /dev/null +++ b/java/ql/test/experimental/query-tests/security/CWE-601/SpringUrlRedirect.expected @@ -0,0 +1,19 @@ +edges +| SpringUrlRedirect.java:13:30:13:47 | redirectUrl : String | SpringUrlRedirect.java:15:19:15:29 | redirectUrl | +| SpringUrlRedirect.java:20:24:20:41 | redirectUrl : String | SpringUrlRedirect.java:21:36:21:46 | redirectUrl | +| SpringUrlRedirect.java:26:30:26:47 | redirectUrl : String | SpringUrlRedirect.java:27:44:27:54 | redirectUrl | +| SpringUrlRedirect.java:32:30:32:47 | redirectUrl : String | SpringUrlRedirect.java:33:47:33:57 | redirectUrl | +nodes +| SpringUrlRedirect.java:13:30:13:47 | redirectUrl : String | semmle.label | redirectUrl : String | +| SpringUrlRedirect.java:15:19:15:29 | redirectUrl | semmle.label | redirectUrl | +| SpringUrlRedirect.java:20:24:20:41 | redirectUrl : String | semmle.label | redirectUrl : String | +| SpringUrlRedirect.java:21:36:21:46 | redirectUrl | semmle.label | redirectUrl | +| SpringUrlRedirect.java:26:30:26:47 | redirectUrl : String | semmle.label | redirectUrl : String | +| SpringUrlRedirect.java:27:44:27:54 | redirectUrl | semmle.label | redirectUrl | +| SpringUrlRedirect.java:32:30:32:47 | redirectUrl : String | semmle.label | redirectUrl : String | +| SpringUrlRedirect.java:33:47:33:57 | redirectUrl | semmle.label | redirectUrl | +#select +| SpringUrlRedirect.java:15:19:15:29 | redirectUrl | SpringUrlRedirect.java:13:30:13:47 | redirectUrl : String | SpringUrlRedirect.java:15:19:15:29 | redirectUrl | Potentially untrusted URL redirection due to $@. | SpringUrlRedirect.java:13:30:13:47 | redirectUrl | user-provided value | +| SpringUrlRedirect.java:21:36:21:46 | redirectUrl | SpringUrlRedirect.java:20:24:20:41 | redirectUrl : String | SpringUrlRedirect.java:21:36:21:46 | redirectUrl | Potentially untrusted URL redirection due to $@. | SpringUrlRedirect.java:20:24:20:41 | redirectUrl | user-provided value | +| SpringUrlRedirect.java:27:44:27:54 | redirectUrl | SpringUrlRedirect.java:26:30:26:47 | redirectUrl : String | SpringUrlRedirect.java:27:44:27:54 | redirectUrl | Potentially untrusted URL redirection due to $@. | SpringUrlRedirect.java:26:30:26:47 | redirectUrl | user-provided value | +| SpringUrlRedirect.java:33:47:33:57 | redirectUrl | SpringUrlRedirect.java:32:30:32:47 | redirectUrl : String | SpringUrlRedirect.java:33:47:33:57 | redirectUrl | Potentially untrusted URL redirection due to $@. | SpringUrlRedirect.java:32:30:32:47 | redirectUrl | user-provided value | diff --git a/java/ql/test/experimental/query-tests/security/CWE-601/SpringUrlRedirect.java b/java/ql/test/experimental/query-tests/security/CWE-601/SpringUrlRedirect.java new file mode 100644 index 00000000000..1438b0a63a1 --- /dev/null +++ b/java/ql/test/experimental/query-tests/security/CWE-601/SpringUrlRedirect.java @@ -0,0 +1,52 @@ +import javax.servlet.http.HttpServletResponse; +import org.springframework.stereotype.Controller; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.servlet.ModelAndView; +import org.springframework.web.servlet.view.RedirectView; + +@Controller +public class SpringUrlRedirect { + + private final static String VALID_REDIRECT = "http://127.0.0.1"; + + @GetMapping("url1") + public RedirectView bad1(String redirectUrl, HttpServletResponse response) throws Exception { + RedirectView rv = new RedirectView(); + rv.setUrl(redirectUrl); + return rv; + } + + @GetMapping("url2") + public String bad2(String redirectUrl) { + String url = "redirect:" + redirectUrl; + return url; + } + + @GetMapping("url3") + public RedirectView bad3(String redirectUrl) { + RedirectView rv = new RedirectView(redirectUrl); + return rv; + } + + @GetMapping("url4") + public ModelAndView bad4(String redirectUrl) { + return new ModelAndView("redirect:" + redirectUrl); + } + + @GetMapping("url5") + public RedirectView good1(String redirectUrl) { + RedirectView rv = new RedirectView(); + if (redirectUrl.startsWith(VALID_REDIRECT)){ + rv.setUrl(redirectUrl); + }else { + rv.setUrl(VALID_REDIRECT); + } + return rv; + } + + @GetMapping("url6") + public ModelAndView good2(String token) { + String url = "/edit?token=" + token; + return new ModelAndView("redirect:" + url); + } +} diff --git a/java/ql/test/experimental/query-tests/security/CWE-601/SpringUrlRedirect.qlref b/java/ql/test/experimental/query-tests/security/CWE-601/SpringUrlRedirect.qlref new file mode 100644 index 00000000000..418be1d307b --- /dev/null +++ b/java/ql/test/experimental/query-tests/security/CWE-601/SpringUrlRedirect.qlref @@ -0,0 +1 @@ +experimental/Security/CWE/CWE-601/SpringUrlRedirect.ql diff --git a/java/ql/test/experimental/query-tests/security/CWE-601/options b/java/ql/test/experimental/query-tests/security/CWE-601/options new file mode 100644 index 00000000000..a9289108747 --- /dev/null +++ b/java/ql/test/experimental/query-tests/security/CWE-601/options @@ -0,0 +1 @@ +//semmle-extractor-options: --javac-args -cp ${testdir}/../../../../stubs/servlet-api-2.4:${testdir}/../../../../stubs/springframework-5.2.3/ \ No newline at end of file diff --git a/java/ql/test/stubs/springframework-5.2.3/org/springframework/web/servlet/ModelAndView.java b/java/ql/test/stubs/springframework-5.2.3/org/springframework/web/servlet/ModelAndView.java new file mode 100644 index 00000000000..53e337d5053 --- /dev/null +++ b/java/ql/test/stubs/springframework-5.2.3/org/springframework/web/servlet/ModelAndView.java @@ -0,0 +1,107 @@ +package org.springframework.web.servlet; + +import java.util.Map; +import org.springframework.http.HttpStatus; +import org.springframework.lang.Nullable; + +public class ModelAndView { + @Nullable + private Object view; + @Nullable + private HttpStatus status; + private boolean cleared = false; + + public ModelAndView() { + } + + public ModelAndView(String viewName) { + this.view = viewName; + } + + public ModelAndView(View view) { + this.view = view; + } + + public ModelAndView(String viewName, @Nullable Map model) { } + + public ModelAndView(View view, @Nullable Map model) { } + + public ModelAndView(String viewName, HttpStatus status) { } + + public ModelAndView(@Nullable String viewName, @Nullable Map model, @Nullable HttpStatus status) { } + + public ModelAndView(String viewName, String modelName, Object modelObject) { } + + public ModelAndView(View view, String modelName, Object modelObject) { } + + public void setViewName(@Nullable String viewName) { + this.view = viewName; + } + + @Nullable + public String getViewName() { + return ""; + } + + public void setView(@Nullable View view) { } + + @Nullable + public View getView() { + return null; + } + + public boolean hasView() { + return true; + } + + public boolean isReference() { + return true; + } + + @Nullable + protected Map getModelInternal() { + return null; + } + + public Map getModel() { + return null; + } + + public void setStatus(@Nullable HttpStatus status) { } + + @Nullable + public HttpStatus getStatus() { + return this.status; + } + + public ModelAndView addObject(String attributeName, @Nullable Object attributeValue) { + return null; + } + + public ModelAndView addObject(Object attributeValue) { + return null; + } + + public ModelAndView addAllObjects(@Nullable Map modelMap) { + return null; + } + + public void clear() { } + + public boolean isEmpty() { + return true; + } + + public boolean wasCleared() { + return true; + } + + public String toString() { + return ""; + } + + private String formatView() { + return ""; + } +} + diff --git a/java/ql/test/stubs/springframework-5.2.3/org/springframework/web/servlet/View.java b/java/ql/test/stubs/springframework-5.2.3/org/springframework/web/servlet/View.java new file mode 100644 index 00000000000..b2281b8c250 --- /dev/null +++ b/java/ql/test/stubs/springframework-5.2.3/org/springframework/web/servlet/View.java @@ -0,0 +1,20 @@ +package org.springframework.web.servlet; + +import java.util.Map; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import org.springframework.lang.Nullable; + +public interface View { + String RESPONSE_STATUS_ATTRIBUTE = View.class.getName() + ".responseStatus"; + String PATH_VARIABLES = View.class.getName() + ".pathVariables"; + String SELECTED_CONTENT_TYPE = View.class.getName() + ".selectedContentType"; + + @Nullable + default String getContentType() { + return null; + } + + void render(@Nullable Map var1, HttpServletRequest var2, HttpServletResponse var3) throws Exception; +} + diff --git a/java/ql/test/stubs/springframework-5.2.3/org/springframework/web/servlet/view/AbstractUrlBasedView.java b/java/ql/test/stubs/springframework-5.2.3/org/springframework/web/servlet/view/AbstractUrlBasedView.java new file mode 100644 index 00000000000..9efd87af12f --- /dev/null +++ b/java/ql/test/stubs/springframework-5.2.3/org/springframework/web/servlet/view/AbstractUrlBasedView.java @@ -0,0 +1,39 @@ +package org.springframework.web.servlet.view; + +import java.util.Locale; +import org.springframework.lang.Nullable; + +public abstract class AbstractUrlBasedView { + @Nullable + private String url; + + protected AbstractUrlBasedView() { } + + protected AbstractUrlBasedView(String url) { + this.url = url; + } + + public void setUrl(@Nullable String url) { + this.url = url; + } + + @Nullable + public String getUrl() { + return ""; + } + + public void afterPropertiesSet() throws Exception { } + + protected boolean isUrlRequired() { + return true; + } + + public boolean checkResource(Locale locale) throws Exception { + return true; + } + + public String toString() { + return ""; + } +} + diff --git a/java/ql/test/stubs/springframework-5.2.3/org/springframework/web/servlet/view/RedirectView.java b/java/ql/test/stubs/springframework-5.2.3/org/springframework/web/servlet/view/RedirectView.java new file mode 100644 index 00000000000..ee18868231a --- /dev/null +++ b/java/ql/test/stubs/springframework-5.2.3/org/springframework/web/servlet/view/RedirectView.java @@ -0,0 +1,129 @@ +package org.springframework.web.servlet.view; + +import java.io.IOException; +import java.io.UnsupportedEncodingException; +import java.lang.reflect.Array; +import java.net.URLEncoder; +import java.util.Collection; +import java.util.Collections; +import java.util.Iterator; +import java.util.LinkedHashMap; +import java.util.Map; +import java.util.Map.Entry; +import java.util.regex.Matcher; +import java.util.regex.Pattern; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import org.springframework.http.HttpStatus; +import org.springframework.lang.Nullable; + +public class RedirectView extends AbstractUrlBasedView { + private static final Pattern URI_TEMPLATE_VARIABLE_PATTERN = Pattern.compile("\\{([^/]+?)\\}"); + private boolean contextRelative = false; + private boolean http10Compatible = true; + private boolean exposeModelAttributes = true; + @Nullable + private String encodingScheme; + @Nullable + private HttpStatus statusCode; + private boolean expandUriTemplateVariables = true; + private boolean propagateQueryParams = false; + @Nullable + private String[] hosts; + + public RedirectView() { } + + public RedirectView(String url) { } + + public RedirectView(String url, boolean contextRelative) { } + + public RedirectView(String url, boolean contextRelative, boolean http10Compatible) { } + + public RedirectView(String url, boolean contextRelative, boolean http10Compatible, boolean exposeModelAttributes) { } + + public void setContextRelative(boolean contextRelative) { } + + public void setHttp10Compatible(boolean http10Compatible) { } + + public void setExposeModelAttributes(boolean exposeModelAttributes) { } + + public void setEncodingScheme(String encodingScheme) { } + + public void setStatusCode(HttpStatus statusCode) { } + + public void setExpandUriTemplateVariables(boolean expandUriTemplateVariables) { } + + public void setPropagateQueryParams(boolean propagateQueryParams) { } + + public boolean isPropagateQueryProperties() { + return true; + } + + public void setHosts(@Nullable String... hosts) { } + + @Nullable + public String[] getHosts() { + return this.hosts; + } + + public boolean isRedirectView() { + return true; + } + + protected boolean isContextRequired() { + return false; + } + + protected void renderMergedOutputModel(Map model, HttpServletRequest request, HttpServletResponse response) throws IOException { } + + protected final String createTargetUrl(Map model, HttpServletRequest request) throws UnsupportedEncodingException { + return ""; + } + + private String getContextPath(HttpServletRequest request) { + return ""; + } + + protected StringBuilder replaceUriTemplateVariables(String targetUrl, Map model, Map currentUriVariables, String encodingScheme) throws UnsupportedEncodingException { + return null; + } + + private Map getCurrentRequestUriVariables(HttpServletRequest request) { + return null; + } + + protected void appendCurrentQueryParams(StringBuilder targetUrl, HttpServletRequest request) { } + + protected void appendQueryProperties(StringBuilder targetUrl, Map model, String encodingScheme) throws UnsupportedEncodingException { } + + protected Map queryProperties(Map model) { + return null; + } + + protected boolean isEligibleProperty(String key, @Nullable Object value) { + return true; + } + + protected boolean isEligibleValue(@Nullable Object value) { + return true; + } + + protected String urlEncode(String input, String encodingScheme) throws UnsupportedEncodingException { + return ""; + } + + protected String updateTargetUrl(String targetUrl, Map model, HttpServletRequest request, HttpServletResponse response) { + return ""; + } + + protected void sendRedirect(HttpServletRequest request, HttpServletResponse response, String targetUrl, boolean http10Compatible) throws IOException { } + + protected boolean isRemoteHost(String targetUrl) { + return true; + } + + protected HttpStatus getHttp11StatusCode(HttpServletRequest request, HttpServletResponse response, String targetUrl) { + return this.statusCode; + } +} + From 02e415045f04957a33049907bd3256ce84ad7378 Mon Sep 17 00:00:00 2001 From: haby0 Date: Thu, 13 May 2021 15:48:15 +0800 Subject: [PATCH 132/168] Delete RedirectBuilderFlowConfig --- .../Security/CWE/CWE-601/SpringUrlRedirect.qll | 13 +------------ 1 file changed, 1 insertion(+), 12 deletions(-) diff --git a/java/ql/src/experimental/Security/CWE/CWE-601/SpringUrlRedirect.qll b/java/ql/src/experimental/Security/CWE/CWE-601/SpringUrlRedirect.qll index 0ea88e84673..1ab5f3cd0b1 100644 --- a/java/ql/src/experimental/Security/CWE/CWE-601/SpringUrlRedirect.qll +++ b/java/ql/src/experimental/Security/CWE/CWE-601/SpringUrlRedirect.qll @@ -51,22 +51,11 @@ class SpringUrlRedirectSink extends DataFlow::Node { exists(ClassInstanceExpr cie | cie.getConstructedType().hasQualifiedName("org.springframework.web.servlet", "ModelAndView") and cie.getArgument(0) = this.asExpr() and - exists(RedirectBuilderFlowConfig rstrbfc | rstrbfc.hasFlowToExpr(cie.getArgument(0))) + exists(RedirectBuilderExpr rbe | rbe.getRightOperand() = this.asExpr()) ) } } -/** A data flow configuration tracing flow from remote sources to redirect builder expression. */ -private class RedirectBuilderFlowConfig extends DataFlow2::Configuration { - RedirectBuilderFlowConfig() { this = "RedirectBuilderFlowConfig" } - - override predicate isSource(DataFlow::Node src) { src instanceof RemoteFlowSource } - - override predicate isSink(DataFlow::Node sink) { - exists(RedirectBuilderExpr rbe | rbe.getRightOperand() = sink.asExpr()) - } -} - /** A data flow configuration tracing flow from RedirectView object to calling setUrl method. */ private class RedirectViewFlowConfig extends DataFlow2::Configuration { RedirectViewFlowConfig() { this = "RedirectViewFlowConfig" } From 51067af784f47fccb040a6d99ac16491b6a58cf6 Mon Sep 17 00:00:00 2001 From: Erik Krogh Kristensen Date: Thu, 13 May 2021 22:34:10 +0200 Subject: [PATCH 133/168] add "uid" (and friends) as maybe being sensitive account info --- .../internal/SensitiveDataHeuristics.qll | 3 +- .../CWE-338/InsecureRandomness.expected | 40 +++++++++++++++++++ .../test/query-tests/Security/CWE-338/tst.js | 9 ++++- 3 files changed, 50 insertions(+), 2 deletions(-) diff --git a/javascript/ql/src/semmle/javascript/security/internal/SensitiveDataHeuristics.qll b/javascript/ql/src/semmle/javascript/security/internal/SensitiveDataHeuristics.qll index ddf95b1b534..9a3a306c159 100644 --- a/javascript/ql/src/semmle/javascript/security/internal/SensitiveDataHeuristics.qll +++ b/javascript/ql/src/semmle/javascript/security/internal/SensitiveDataHeuristics.qll @@ -58,7 +58,8 @@ module HeuristicNames { */ string maybeAccountInfo() { result = "(?is).*acc(ou)?nt.*" or - result = "(?is).*(puid|username|userid).*" + result = "(?is).*(puid|username|userid).*" or + result = "(?is).*(u|^|_|[a-z(?=U)])(uid).*" } /** diff --git a/javascript/ql/test/query-tests/Security/CWE-338/InsecureRandomness.expected b/javascript/ql/test/query-tests/Security/CWE-338/InsecureRandomness.expected index e4ab385cc07..4db1bb3b088 100644 --- a/javascript/ql/test/query-tests/Security/CWE-338/InsecureRandomness.expected +++ b/javascript/ql/test/query-tests/Security/CWE-338/InsecureRandomness.expected @@ -66,6 +66,26 @@ nodes | tst.js:95:33:95:45 | Math.random() | | tst.js:95:33:95:45 | Math.random() | | tst.js:95:33:95:45 | Math.random() | +| tst.js:115:16:115:56 | Math.fl ... 00_000) | +| tst.js:115:16:115:56 | Math.fl ... 00_000) | +| tst.js:115:27:115:39 | Math.random() | +| tst.js:115:27:115:39 | Math.random() | +| tst.js:115:27:115:55 | Math.ra ... 000_000 | +| tst.js:116:22:116:62 | Math.fl ... 00_000) | +| tst.js:116:22:116:62 | Math.fl ... 00_000) | +| tst.js:116:33:116:45 | Math.random() | +| tst.js:116:33:116:45 | Math.random() | +| tst.js:116:33:116:61 | Math.ra ... 000_000 | +| tst.js:117:15:117:55 | Math.fl ... 00_000) | +| tst.js:117:15:117:55 | Math.fl ... 00_000) | +| tst.js:117:26:117:38 | Math.random() | +| tst.js:117:26:117:38 | Math.random() | +| tst.js:117:26:117:54 | Math.ra ... 000_000 | +| tst.js:118:23:118:63 | Math.fl ... 00_000) | +| tst.js:118:23:118:63 | Math.fl ... 00_000) | +| tst.js:118:34:118:46 | Math.random() | +| tst.js:118:34:118:46 | Math.random() | +| tst.js:118:34:118:62 | Math.ra ... 000_000 | edges | tst.js:2:20:2:32 | Math.random() | tst.js:2:20:2:32 | Math.random() | | tst.js:6:31:6:43 | Math.random() | tst.js:6:20:6:43 | "prefix ... andom() | @@ -114,6 +134,22 @@ edges | tst.js:84:19:84:31 | Math.random() | tst.js:84:19:84:31 | Math.random() | | tst.js:90:32:90:44 | Math.random() | tst.js:90:32:90:44 | Math.random() | | tst.js:95:33:95:45 | Math.random() | tst.js:95:33:95:45 | Math.random() | +| tst.js:115:27:115:39 | Math.random() | tst.js:115:27:115:55 | Math.ra ... 000_000 | +| tst.js:115:27:115:39 | Math.random() | tst.js:115:27:115:55 | Math.ra ... 000_000 | +| tst.js:115:27:115:55 | Math.ra ... 000_000 | tst.js:115:16:115:56 | Math.fl ... 00_000) | +| tst.js:115:27:115:55 | Math.ra ... 000_000 | tst.js:115:16:115:56 | Math.fl ... 00_000) | +| tst.js:116:33:116:45 | Math.random() | tst.js:116:33:116:61 | Math.ra ... 000_000 | +| tst.js:116:33:116:45 | Math.random() | tst.js:116:33:116:61 | Math.ra ... 000_000 | +| tst.js:116:33:116:61 | Math.ra ... 000_000 | tst.js:116:22:116:62 | Math.fl ... 00_000) | +| tst.js:116:33:116:61 | Math.ra ... 000_000 | tst.js:116:22:116:62 | Math.fl ... 00_000) | +| tst.js:117:26:117:38 | Math.random() | tst.js:117:26:117:54 | Math.ra ... 000_000 | +| tst.js:117:26:117:38 | Math.random() | tst.js:117:26:117:54 | Math.ra ... 000_000 | +| tst.js:117:26:117:54 | Math.ra ... 000_000 | tst.js:117:15:117:55 | Math.fl ... 00_000) | +| tst.js:117:26:117:54 | Math.ra ... 000_000 | tst.js:117:15:117:55 | Math.fl ... 00_000) | +| tst.js:118:34:118:46 | Math.random() | tst.js:118:34:118:62 | Math.ra ... 000_000 | +| tst.js:118:34:118:46 | Math.random() | tst.js:118:34:118:62 | Math.ra ... 000_000 | +| tst.js:118:34:118:62 | Math.ra ... 000_000 | tst.js:118:23:118:63 | Math.fl ... 00_000) | +| tst.js:118:34:118:62 | Math.ra ... 000_000 | tst.js:118:23:118:63 | Math.fl ... 00_000) | #select | tst.js:2:20:2:32 | Math.random() | tst.js:2:20:2:32 | Math.random() | tst.js:2:20:2:32 | Math.random() | Cryptographically insecure $@ in a security context. | tst.js:2:20:2:32 | Math.random() | random value | | tst.js:6:20:6:43 | "prefix ... andom() | tst.js:6:31:6:43 | Math.random() | tst.js:6:20:6:43 | "prefix ... andom() | Cryptographically insecure $@ in a security context. | tst.js:6:31:6:43 | Math.random() | random value | @@ -131,3 +167,7 @@ edges | tst.js:84:19:84:31 | Math.random() | tst.js:84:19:84:31 | Math.random() | tst.js:84:19:84:31 | Math.random() | Cryptographically insecure $@ in a security context. | tst.js:84:19:84:31 | Math.random() | random value | | tst.js:90:32:90:44 | Math.random() | tst.js:90:32:90:44 | Math.random() | tst.js:90:32:90:44 | Math.random() | Cryptographically insecure $@ in a security context. | tst.js:90:32:90:44 | Math.random() | random value | | tst.js:95:33:95:45 | Math.random() | tst.js:95:33:95:45 | Math.random() | tst.js:95:33:95:45 | Math.random() | Cryptographically insecure $@ in a security context. | tst.js:95:33:95:45 | Math.random() | random value | +| tst.js:115:16:115:56 | Math.fl ... 00_000) | tst.js:115:27:115:39 | Math.random() | tst.js:115:16:115:56 | Math.fl ... 00_000) | Cryptographically insecure $@ in a security context. | tst.js:115:27:115:39 | Math.random() | random value | +| tst.js:116:22:116:62 | Math.fl ... 00_000) | tst.js:116:33:116:45 | Math.random() | tst.js:116:22:116:62 | Math.fl ... 00_000) | Cryptographically insecure $@ in a security context. | tst.js:116:33:116:45 | Math.random() | random value | +| tst.js:117:15:117:55 | Math.fl ... 00_000) | tst.js:117:26:117:38 | Math.random() | tst.js:117:15:117:55 | Math.fl ... 00_000) | Cryptographically insecure $@ in a security context. | tst.js:117:26:117:38 | Math.random() | random value | +| tst.js:118:23:118:63 | Math.fl ... 00_000) | tst.js:118:34:118:46 | Math.random() | tst.js:118:23:118:63 | Math.fl ... 00_000) | Cryptographically insecure $@ in a security context. | tst.js:118:34:118:46 | Math.random() | random value | diff --git a/javascript/ql/test/query-tests/Security/CWE-338/tst.js b/javascript/ql/test/query-tests/Security/CWE-338/tst.js index 123799426b5..6a1abf1403c 100644 --- a/javascript/ql/test/query-tests/Security/CWE-338/tst.js +++ b/javascript/ql/test/query-tests/Security/CWE-338/tst.js @@ -109,4 +109,11 @@ function f18() { } }; var secret = genRandom(); // OK - Math.random() is only a fallback. -})(); \ No newline at end of file +})(); + +function uid() { + var uuid = Math.floor(Math.random() * 4_000_000_000); // NOT OK + var sessionUid = Math.floor(Math.random() * 4_000_000_000); // NOT OK + var uid = Math.floor(Math.random() * 4_000_000_000); // NOT OK + var my_nice_uid = Math.floor(Math.random() * 4_000_000_000); // NOT OK +} \ No newline at end of file From 662e335424a7205100388feaa263db724a850191 Mon Sep 17 00:00:00 2001 From: Erik Krogh Kristensen Date: Thu, 13 May 2021 22:54:39 +0200 Subject: [PATCH 134/168] keep python in sync --- .../python/security/internal/SensitiveDataHeuristics.qll | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/python/ql/src/semmle/python/security/internal/SensitiveDataHeuristics.qll b/python/ql/src/semmle/python/security/internal/SensitiveDataHeuristics.qll index ddf95b1b534..9a3a306c159 100644 --- a/python/ql/src/semmle/python/security/internal/SensitiveDataHeuristics.qll +++ b/python/ql/src/semmle/python/security/internal/SensitiveDataHeuristics.qll @@ -58,7 +58,8 @@ module HeuristicNames { */ string maybeAccountInfo() { result = "(?is).*acc(ou)?nt.*" or - result = "(?is).*(puid|username|userid).*" + result = "(?is).*(puid|username|userid).*" or + result = "(?is).*(u|^|_|[a-z(?=U)])(uid).*" } /** From 9d60ec035f991655e7ea4ead67552ec567df64c2 Mon Sep 17 00:00:00 2001 From: Erik Krogh Kristensen Date: Thu, 13 May 2021 23:04:30 +0200 Subject: [PATCH 135/168] fix casing on the uid regexp --- .../security/internal/SensitiveDataHeuristics.qll | 2 +- .../Security/CWE-338/InsecureRandomness.expected | 10 ++++++++++ javascript/ql/test/query-tests/Security/CWE-338/tst.js | 3 +++ .../security/internal/SensitiveDataHeuristics.qll | 2 +- 4 files changed, 15 insertions(+), 2 deletions(-) diff --git a/javascript/ql/src/semmle/javascript/security/internal/SensitiveDataHeuristics.qll b/javascript/ql/src/semmle/javascript/security/internal/SensitiveDataHeuristics.qll index 9a3a306c159..589c37120b9 100644 --- a/javascript/ql/src/semmle/javascript/security/internal/SensitiveDataHeuristics.qll +++ b/javascript/ql/src/semmle/javascript/security/internal/SensitiveDataHeuristics.qll @@ -59,7 +59,7 @@ module HeuristicNames { string maybeAccountInfo() { result = "(?is).*acc(ou)?nt.*" or result = "(?is).*(puid|username|userid).*" or - result = "(?is).*(u|^|_|[a-z(?=U)])(uid).*" + result = "(?s).*([uU]|^|_|[a-z](?=U))([uU][iI][dD]).*" } /** diff --git a/javascript/ql/test/query-tests/Security/CWE-338/InsecureRandomness.expected b/javascript/ql/test/query-tests/Security/CWE-338/InsecureRandomness.expected index 4db1bb3b088..42da210c266 100644 --- a/javascript/ql/test/query-tests/Security/CWE-338/InsecureRandomness.expected +++ b/javascript/ql/test/query-tests/Security/CWE-338/InsecureRandomness.expected @@ -86,6 +86,12 @@ nodes | tst.js:118:34:118:46 | Math.random() | | tst.js:118:34:118:46 | Math.random() | | tst.js:118:34:118:62 | Math.ra ... 000_000 | +| tst.js:120:16:120:28 | Math.random() | +| tst.js:120:16:120:28 | Math.random() | +| tst.js:120:16:120:28 | Math.random() | +| tst.js:121:18:121:30 | Math.random() | +| tst.js:121:18:121:30 | Math.random() | +| tst.js:121:18:121:30 | Math.random() | edges | tst.js:2:20:2:32 | Math.random() | tst.js:2:20:2:32 | Math.random() | | tst.js:6:31:6:43 | Math.random() | tst.js:6:20:6:43 | "prefix ... andom() | @@ -150,6 +156,8 @@ edges | tst.js:118:34:118:46 | Math.random() | tst.js:118:34:118:62 | Math.ra ... 000_000 | | tst.js:118:34:118:62 | Math.ra ... 000_000 | tst.js:118:23:118:63 | Math.fl ... 00_000) | | tst.js:118:34:118:62 | Math.ra ... 000_000 | tst.js:118:23:118:63 | Math.fl ... 00_000) | +| tst.js:120:16:120:28 | Math.random() | tst.js:120:16:120:28 | Math.random() | +| tst.js:121:18:121:30 | Math.random() | tst.js:121:18:121:30 | Math.random() | #select | tst.js:2:20:2:32 | Math.random() | tst.js:2:20:2:32 | Math.random() | tst.js:2:20:2:32 | Math.random() | Cryptographically insecure $@ in a security context. | tst.js:2:20:2:32 | Math.random() | random value | | tst.js:6:20:6:43 | "prefix ... andom() | tst.js:6:31:6:43 | Math.random() | tst.js:6:20:6:43 | "prefix ... andom() | Cryptographically insecure $@ in a security context. | tst.js:6:31:6:43 | Math.random() | random value | @@ -171,3 +179,5 @@ edges | tst.js:116:22:116:62 | Math.fl ... 00_000) | tst.js:116:33:116:45 | Math.random() | tst.js:116:22:116:62 | Math.fl ... 00_000) | Cryptographically insecure $@ in a security context. | tst.js:116:33:116:45 | Math.random() | random value | | tst.js:117:15:117:55 | Math.fl ... 00_000) | tst.js:117:26:117:38 | Math.random() | tst.js:117:15:117:55 | Math.fl ... 00_000) | Cryptographically insecure $@ in a security context. | tst.js:117:26:117:38 | Math.random() | random value | | tst.js:118:23:118:63 | Math.fl ... 00_000) | tst.js:118:34:118:46 | Math.random() | tst.js:118:23:118:63 | Math.fl ... 00_000) | Cryptographically insecure $@ in a security context. | tst.js:118:34:118:46 | Math.random() | random value | +| tst.js:120:16:120:28 | Math.random() | tst.js:120:16:120:28 | Math.random() | tst.js:120:16:120:28 | Math.random() | Cryptographically insecure $@ in a security context. | tst.js:120:16:120:28 | Math.random() | random value | +| tst.js:121:18:121:30 | Math.random() | tst.js:121:18:121:30 | Math.random() | tst.js:121:18:121:30 | Math.random() | Cryptographically insecure $@ in a security context. | tst.js:121:18:121:30 | Math.random() | random value | diff --git a/javascript/ql/test/query-tests/Security/CWE-338/tst.js b/javascript/ql/test/query-tests/Security/CWE-338/tst.js index 6a1abf1403c..77393b8983c 100644 --- a/javascript/ql/test/query-tests/Security/CWE-338/tst.js +++ b/javascript/ql/test/query-tests/Security/CWE-338/tst.js @@ -116,4 +116,7 @@ function uid() { var sessionUid = Math.floor(Math.random() * 4_000_000_000); // NOT OK var uid = Math.floor(Math.random() * 4_000_000_000); // NOT OK var my_nice_uid = Math.floor(Math.random() * 4_000_000_000); // NOT OK + var liquid = Math.random(); // OK + var UUID = Math.random(); // NOT OK + var MY_UID = Math.random(); // NOK OK } \ No newline at end of file diff --git a/python/ql/src/semmle/python/security/internal/SensitiveDataHeuristics.qll b/python/ql/src/semmle/python/security/internal/SensitiveDataHeuristics.qll index 9a3a306c159..589c37120b9 100644 --- a/python/ql/src/semmle/python/security/internal/SensitiveDataHeuristics.qll +++ b/python/ql/src/semmle/python/security/internal/SensitiveDataHeuristics.qll @@ -59,7 +59,7 @@ module HeuristicNames { string maybeAccountInfo() { result = "(?is).*acc(ou)?nt.*" or result = "(?is).*(puid|username|userid).*" or - result = "(?is).*(u|^|_|[a-z(?=U)])(uid).*" + result = "(?s).*([uU]|^|_|[a-z](?=U))([uU][iI][dD]).*" } /** From 406fb1e383dcf3408b43fc752bf608bdc3b68fda Mon Sep 17 00:00:00 2001 From: Ethan P <56270045+ethanpalm@users.noreply.github.com> Date: Thu, 13 May 2021 17:29:34 -0400 Subject: [PATCH 136/168] Update with Go custom build options --- .../codeql-cli/creating-codeql-databases.rst | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/docs/codeql/codeql-cli/creating-codeql-databases.rst b/docs/codeql/codeql-cli/creating-codeql-databases.rst index 4f7212050df..a41727e1956 100644 --- a/docs/codeql/codeql-cli/creating-codeql-databases.rst +++ b/docs/codeql/codeql-cli/creating-codeql-databases.rst @@ -165,13 +165,14 @@ build steps, you may need to explicitly define each step in the command line. .. pull-quote:: Creating databases for Go - For Go, you should always use the CodeQL autobuilder. Install the Go - toolchain (version 1.11 or later) and, if there are dependencies, the - appropriate dependency manager (such as `dep + For Go, install the Go toolchain (version 1.11 or later) and, if there + are dependencies, the appropriate dependency manager (such as `dep `__). - Do not specify any build commands, as you will override the autobuilder - invocation, which will create an empty database. + The Go autobuilder attempts to automatically detect Go code in a repository, + and only runs build scripts in an attempt to fetch dependencies. To force + CodeQL to use your build script, set the environment variable + `CODEQL_EXTRACTOR_GO_BUILD_TRACING=on` or pass a command. Specifying build commands ~~~~~~~~~~~~~~~~~~~~~~~~~ @@ -200,6 +201,10 @@ commands that you can specify for compiled languages. codeql database create csharp-database --language=csharp --command='dotnet build /p:UseSharedCompilation=false /t:rebuild' +- Go project built using a custom build script:: + + CODEQL_EXTRACTOR_GO_BUILD_TRACING=on codeql database create go-database --language=go --command='./scripts/build.sh' + - Java project built using Gradle:: codeql database create java-database --language=java --command='gradle clean test' From 498c99e26ca556e466f00015a6203f3ce64a5362 Mon Sep 17 00:00:00 2001 From: haby0 Date: Fri, 14 May 2021 16:31:59 +0800 Subject: [PATCH 137/168] Add left value, Add return expression tracing flow --- .../CWE/CWE-601/SpringUrlRedirect.qll | 70 +++++++++++++++++-- .../CWE-601/SpringUrlRedirect.expected | 8 +++ .../security/CWE-601/SpringUrlRedirect.java | 18 ++++- 3 files changed, 90 insertions(+), 6 deletions(-) diff --git a/java/ql/src/experimental/Security/CWE/CWE-601/SpringUrlRedirect.qll b/java/ql/src/experimental/Security/CWE/CWE-601/SpringUrlRedirect.qll index 1ab5f3cd0b1..866eaae1c34 100644 --- a/java/ql/src/experimental/Security/CWE/CWE-601/SpringUrlRedirect.qll +++ b/java/ql/src/experimental/Security/CWE/CWE-601/SpringUrlRedirect.qll @@ -18,20 +18,48 @@ class StartsWithSanitizer extends DataFlow::BarrierGuard { } /** - * A concatenate expression using the string `redirect:` on the left. + * A concatenate expression using the string `redirect:` or `ajaxredirect:` or `forward:` on the left. * * E.g: `"redirect:" + redirectUrl` */ class RedirectBuilderExpr extends AddExpr { RedirectBuilderExpr() { - this.getLeftOperand().(CompileTimeConstantExpr).getStringValue() = "redirect:" + this.getLeftOperand().(CompileTimeConstantExpr).getStringValue() in [ + "redirect:", "ajaxredirect:", "forward:" + ] + } +} + +/** + * A call to `StringBuilder.append` or `StringBuffer.append` method, and the parameter value is + * `"redirect:"` or `"ajaxredirect:"` or `"forward:"`. + * + * E.g: `StringBuilder.append("redirect:")` + */ +class RedirectAppendCall extends MethodAccess { + RedirectAppendCall() { + this.getMethod().hasName("append") and + this.getMethod().getDeclaringType() instanceof StringBuildingType and + this.getArgument(0).(CompileTimeConstantExpr).getStringValue() in [ + "redirect:", "ajaxredirect:", "forward:" + ] } } /** A URL redirection sink from spring controller method. */ class SpringUrlRedirectSink extends DataFlow::Node { SpringUrlRedirectSink() { - exists(RedirectBuilderExpr rbe | rbe.getRightOperand() = this.asExpr()) + exists(RedirectBuilderExpr rbe | + rbe.getRightOperand() = this.asExpr() and + exists(RedirectBuilderFlowConfig rbfc | rbfc.hasFlow(exprNode(rbe), _)) + ) + or + exists(MethodAccess ma, RedirectAppendCall rac | + DataFlow2::localExprFlow(rac.getQualifier(), ma.getQualifier()) and + ma.getMethod().hasName("append") and + ma.getArgument(0) = this.asExpr() and + exists(RedirectBuilderFlowConfig rbfc | rbfc.hasFlow(exprNode(ma.getQualifier()), _)) + ) or exists(MethodAccess ma | ma.getMethod().hasName("setUrl") and @@ -50,8 +78,40 @@ class SpringUrlRedirectSink extends DataFlow::Node { or exists(ClassInstanceExpr cie | cie.getConstructedType().hasQualifiedName("org.springframework.web.servlet", "ModelAndView") and - cie.getArgument(0) = this.asExpr() and - exists(RedirectBuilderExpr rbe | rbe.getRightOperand() = this.asExpr()) + exists(RedirectBuilderExpr rbe | + rbe = cie.getArgument(0) and rbe.getRightOperand() = this.asExpr() + ) + ) + } +} + +/** A data flow configuration tracing flow from redirect builder expression to spring controller method return expression. */ +private class RedirectBuilderFlowConfig extends DataFlow2::Configuration { + RedirectBuilderFlowConfig() { this = "RedirectBuilderFlowConfig" } + + override predicate isSource(DataFlow::Node src) { + exists(RedirectBuilderExpr rbe | rbe = src.asExpr()) + or + exists(MethodAccess ma, RedirectAppendCall rac | + DataFlow2::localExprFlow(rac.getQualifier(), ma.getQualifier()) and + ma.getMethod().hasName("append") and + ma.getQualifier() = src.asExpr() + ) + } + + override predicate isSink(DataFlow::Node sink) { + exists(ReturnStmt rs, SpringRequestMappingMethod sqmm | + rs.getResult() = sink.asExpr() and + sqmm.getBody().getAStmt() = rs + ) + } + + override predicate isAdditionalFlowStep(Node prod, Node succ) { + exists(MethodAccess ma | + ma.getMethod().hasName("toString") and + ma.getMethod().getDeclaringType() instanceof StringBuildingType and + ma.getQualifier() = prod.asExpr() and + ma = succ.asExpr() ) } } diff --git a/java/ql/test/experimental/query-tests/security/CWE-601/SpringUrlRedirect.expected b/java/ql/test/experimental/query-tests/security/CWE-601/SpringUrlRedirect.expected index fee0598bbee..26b8acd7770 100644 --- a/java/ql/test/experimental/query-tests/security/CWE-601/SpringUrlRedirect.expected +++ b/java/ql/test/experimental/query-tests/security/CWE-601/SpringUrlRedirect.expected @@ -3,6 +3,8 @@ edges | SpringUrlRedirect.java:20:24:20:41 | redirectUrl : String | SpringUrlRedirect.java:21:36:21:46 | redirectUrl | | SpringUrlRedirect.java:26:30:26:47 | redirectUrl : String | SpringUrlRedirect.java:27:44:27:54 | redirectUrl | | SpringUrlRedirect.java:32:30:32:47 | redirectUrl : String | SpringUrlRedirect.java:33:47:33:57 | redirectUrl | +| SpringUrlRedirect.java:37:24:37:41 | redirectUrl : String | SpringUrlRedirect.java:40:29:40:39 | redirectUrl | +| SpringUrlRedirect.java:45:24:45:41 | redirectUrl : String | SpringUrlRedirect.java:48:30:48:40 | redirectUrl | nodes | SpringUrlRedirect.java:13:30:13:47 | redirectUrl : String | semmle.label | redirectUrl : String | | SpringUrlRedirect.java:15:19:15:29 | redirectUrl | semmle.label | redirectUrl | @@ -12,8 +14,14 @@ nodes | SpringUrlRedirect.java:27:44:27:54 | redirectUrl | semmle.label | redirectUrl | | SpringUrlRedirect.java:32:30:32:47 | redirectUrl : String | semmle.label | redirectUrl : String | | SpringUrlRedirect.java:33:47:33:57 | redirectUrl | semmle.label | redirectUrl | +| SpringUrlRedirect.java:37:24:37:41 | redirectUrl : String | semmle.label | redirectUrl : String | +| SpringUrlRedirect.java:40:29:40:39 | redirectUrl | semmle.label | redirectUrl | +| SpringUrlRedirect.java:45:24:45:41 | redirectUrl : String | semmle.label | redirectUrl : String | +| SpringUrlRedirect.java:48:30:48:40 | redirectUrl | semmle.label | redirectUrl | #select | SpringUrlRedirect.java:15:19:15:29 | redirectUrl | SpringUrlRedirect.java:13:30:13:47 | redirectUrl : String | SpringUrlRedirect.java:15:19:15:29 | redirectUrl | Potentially untrusted URL redirection due to $@. | SpringUrlRedirect.java:13:30:13:47 | redirectUrl | user-provided value | | SpringUrlRedirect.java:21:36:21:46 | redirectUrl | SpringUrlRedirect.java:20:24:20:41 | redirectUrl : String | SpringUrlRedirect.java:21:36:21:46 | redirectUrl | Potentially untrusted URL redirection due to $@. | SpringUrlRedirect.java:20:24:20:41 | redirectUrl | user-provided value | | SpringUrlRedirect.java:27:44:27:54 | redirectUrl | SpringUrlRedirect.java:26:30:26:47 | redirectUrl : String | SpringUrlRedirect.java:27:44:27:54 | redirectUrl | Potentially untrusted URL redirection due to $@. | SpringUrlRedirect.java:26:30:26:47 | redirectUrl | user-provided value | | SpringUrlRedirect.java:33:47:33:57 | redirectUrl | SpringUrlRedirect.java:32:30:32:47 | redirectUrl : String | SpringUrlRedirect.java:33:47:33:57 | redirectUrl | Potentially untrusted URL redirection due to $@. | SpringUrlRedirect.java:32:30:32:47 | redirectUrl | user-provided value | +| SpringUrlRedirect.java:40:29:40:39 | redirectUrl | SpringUrlRedirect.java:37:24:37:41 | redirectUrl : String | SpringUrlRedirect.java:40:29:40:39 | redirectUrl | Potentially untrusted URL redirection due to $@. | SpringUrlRedirect.java:37:24:37:41 | redirectUrl | user-provided value | +| SpringUrlRedirect.java:48:30:48:40 | redirectUrl | SpringUrlRedirect.java:45:24:45:41 | redirectUrl : String | SpringUrlRedirect.java:48:30:48:40 | redirectUrl | Potentially untrusted URL redirection due to $@. | SpringUrlRedirect.java:45:24:45:41 | redirectUrl | user-provided value | diff --git a/java/ql/test/experimental/query-tests/security/CWE-601/SpringUrlRedirect.java b/java/ql/test/experimental/query-tests/security/CWE-601/SpringUrlRedirect.java index 1438b0a63a1..5124d8cd8c6 100644 --- a/java/ql/test/experimental/query-tests/security/CWE-601/SpringUrlRedirect.java +++ b/java/ql/test/experimental/query-tests/security/CWE-601/SpringUrlRedirect.java @@ -34,6 +34,22 @@ public class SpringUrlRedirect { } @GetMapping("url5") + public String bad5(String redirectUrl) { + StringBuffer stringBuffer = new StringBuffer(); + stringBuffer.append("redirect:"); + stringBuffer.append(redirectUrl); + return stringBuffer.toString(); + } + + @GetMapping("url6") + public String bad6(String redirectUrl) { + StringBuilder stringBuilder = new StringBuilder(); + stringBuilder.append("redirect:"); + stringBuilder.append(redirectUrl); + return stringBuilder.toString(); + } + + @GetMapping("url7") public RedirectView good1(String redirectUrl) { RedirectView rv = new RedirectView(); if (redirectUrl.startsWith(VALID_REDIRECT)){ @@ -44,7 +60,7 @@ public class SpringUrlRedirect { return rv; } - @GetMapping("url6") + @GetMapping("url8") public ModelAndView good2(String token) { String url = "/edit?token=" + token; return new ModelAndView("redirect:" + url); From f378513ea39f33b15879653e489653d719633910 Mon Sep 17 00:00:00 2001 From: Robin Neatherway Date: Fri, 14 May 2021 11:19:20 +0100 Subject: [PATCH 138/168] Add lines-of-code tags This is a proposed method for advertising which queries are measuring the lines of code in a project in a more robust manner than inspecting the rule id. Note that the python "LinesOfUserCode" query should _not_ have this property, as otherwise the results of the two queries will be summed. --- cpp/ql/src/Summary/LinesOfCode.ql | 1 + csharp/ql/src/Metrics/Summaries/LinesOfCode.ql | 1 + java/ql/src/Metrics/Summaries/LinesOfCode.ql | 1 + javascript/ql/src/Summary/LinesOfCode.ql | 1 + python/ql/src/Summary/LinesOfCode.ql | 1 + 5 files changed, 5 insertions(+) diff --git a/cpp/ql/src/Summary/LinesOfCode.ql b/cpp/ql/src/Summary/LinesOfCode.ql index 3b2aa2ac4c9..2d816b349e8 100644 --- a/cpp/ql/src/Summary/LinesOfCode.ql +++ b/cpp/ql/src/Summary/LinesOfCode.ql @@ -4,6 +4,7 @@ * @description The total number of lines of C/C++ code across all files, including system headers, libraries, and auto-generated files. This is a useful metric of the size of a database. For all files that were seen during the build, this query counts the lines of code, excluding whitespace or comments. * @kind metric * @tags summary + * lines-of-code */ import cpp diff --git a/csharp/ql/src/Metrics/Summaries/LinesOfCode.ql b/csharp/ql/src/Metrics/Summaries/LinesOfCode.ql index 9156a5e4a7f..e93e3c7416f 100644 --- a/csharp/ql/src/Metrics/Summaries/LinesOfCode.ql +++ b/csharp/ql/src/Metrics/Summaries/LinesOfCode.ql @@ -4,6 +4,7 @@ * @description The total number of lines of code across all files. This is a useful metric of the size of a database. For all files that were seen during the build, this query counts the lines of code, excluding whitespace or comments. * @kind metric * @tags summary + * lines-of-code */ import csharp diff --git a/java/ql/src/Metrics/Summaries/LinesOfCode.ql b/java/ql/src/Metrics/Summaries/LinesOfCode.ql index d6db7c6ee6b..c622f8b08ba 100644 --- a/java/ql/src/Metrics/Summaries/LinesOfCode.ql +++ b/java/ql/src/Metrics/Summaries/LinesOfCode.ql @@ -6,6 +6,7 @@ * or comments. * @kind metric * @tags summary + * lines-of-code */ import java diff --git a/javascript/ql/src/Summary/LinesOfCode.ql b/javascript/ql/src/Summary/LinesOfCode.ql index 9f89e0e2163..cadf0a9cf8f 100644 --- a/javascript/ql/src/Summary/LinesOfCode.ql +++ b/javascript/ql/src/Summary/LinesOfCode.ql @@ -4,6 +4,7 @@ * @description The total number of lines of JavaScript or TypeScript code across all files checked into the repository, except in `node_modules`. This is a useful metric of the size of a database. For all files that were seen during extraction, this query counts the lines of code, excluding whitespace or comments. * @kind metric * @tags summary + * lines-of-code */ import javascript diff --git a/python/ql/src/Summary/LinesOfCode.ql b/python/ql/src/Summary/LinesOfCode.ql index d9bfc4f872c..ad0b77730de 100644 --- a/python/ql/src/Summary/LinesOfCode.ql +++ b/python/ql/src/Summary/LinesOfCode.ql @@ -5,6 +5,7 @@ * database. This query counts the lines of code, excluding whitespace or comments. * @kind metric * @tags summary + * lines-of-code * @id py/summary/lines-of-code */ From 5031b73f3509e4d1c1ce21586dd1262502177670 Mon Sep 17 00:00:00 2001 From: Mathias Vorreiter Pedersen Date: Fri, 14 May 2021 13:43:20 +0200 Subject: [PATCH 139/168] C++: Add barrier to cpp/uncontrolled-allocation-size that blocks flow when overflow isn't possible. --- .../CWE/CWE-190/TaintedAllocationSize.ql | 16 ++++++++++ .../TaintedAllocationSize.expected | 30 ------------------- .../semmle/TaintedAllocationSize/test.cpp | 6 ++-- 3 files changed, 19 insertions(+), 33 deletions(-) diff --git a/cpp/ql/src/Security/CWE/CWE-190/TaintedAllocationSize.ql b/cpp/ql/src/Security/CWE/CWE-190/TaintedAllocationSize.ql index cc2d52385c7..859d56ec2a0 100644 --- a/cpp/ql/src/Security/CWE/CWE-190/TaintedAllocationSize.ql +++ b/cpp/ql/src/Security/CWE/CWE-190/TaintedAllocationSize.ql @@ -12,6 +12,7 @@ */ import cpp +import semmle.code.cpp.rangeanalysis.SimpleRangeAnalysis import semmle.code.cpp.security.TaintTracking import TaintedWithPath @@ -27,6 +28,21 @@ predicate allocSink(Expr alloc, Expr tainted) { class TaintedAllocationSizeConfiguration extends TaintTrackingConfiguration { override predicate isSink(Element tainted) { allocSink(_, tainted) } + + override predicate isBarrier(Expr e) { + super.isBarrier(e) + or + // There can be two separate reasons for `convertedExprMightOverflow` not holding: + // 1. `e` really cannot overflow. + // 2. `e` isn't analyzable. + // If we didn't rule out case 2 we would place barriers on anything that isn't analyzable. + ( + e instanceof UnaryArithmeticOperation or + e instanceof BinaryArithmeticOperation or + e instanceof AssignArithmeticOperation + ) and + not convertedExprMightOverflow(e) + } } predicate taintedAllocSize( diff --git a/cpp/ql/test/query-tests/Security/CWE/CWE-190/semmle/TaintedAllocationSize/TaintedAllocationSize.expected b/cpp/ql/test/query-tests/Security/CWE/CWE-190/semmle/TaintedAllocationSize/TaintedAllocationSize.expected index 25ff3162973..cdcf3aa6847 100644 --- a/cpp/ql/test/query-tests/Security/CWE/CWE-190/semmle/TaintedAllocationSize/TaintedAllocationSize.expected +++ b/cpp/ql/test/query-tests/Security/CWE/CWE-190/semmle/TaintedAllocationSize/TaintedAllocationSize.expected @@ -53,10 +53,6 @@ edges | test.cpp:132:19:132:24 | call to getenv | test.cpp:134:10:134:27 | ... * ... | | test.cpp:132:19:132:32 | (const char *)... | test.cpp:134:10:134:27 | ... * ... | | test.cpp:132:19:132:32 | (const char *)... | test.cpp:134:10:134:27 | ... * ... | -| test.cpp:138:19:138:24 | call to getenv | test.cpp:142:11:142:28 | ... * ... | -| test.cpp:138:19:138:24 | call to getenv | test.cpp:142:11:142:28 | ... * ... | -| test.cpp:138:19:138:32 | (const char *)... | test.cpp:142:11:142:28 | ... * ... | -| test.cpp:138:19:138:32 | (const char *)... | test.cpp:142:11:142:28 | ... * ... | | test.cpp:201:9:201:42 | Store | test.cpp:231:9:231:24 | call to get_tainted_size | | test.cpp:201:9:201:42 | Store | test.cpp:231:9:231:24 | call to get_tainted_size | | test.cpp:201:14:201:19 | call to getenv | test.cpp:201:9:201:42 | Store | @@ -91,14 +87,6 @@ edges | test.cpp:295:18:295:21 | Chi | test.cpp:298:10:298:27 | ... * ... | | test.cpp:295:18:295:21 | Chi | test.cpp:298:10:298:27 | ... * ... | | test.cpp:295:18:295:21 | get_size output argument [array content] | test.cpp:295:18:295:21 | Chi | -| test.cpp:301:19:301:24 | call to getenv | test.cpp:305:11:305:28 | ... * ... | -| test.cpp:301:19:301:24 | call to getenv | test.cpp:305:11:305:28 | ... * ... | -| test.cpp:301:19:301:32 | (const char *)... | test.cpp:305:11:305:28 | ... * ... | -| test.cpp:301:19:301:32 | (const char *)... | test.cpp:305:11:305:28 | ... * ... | -| test.cpp:309:19:309:24 | call to getenv | test.cpp:314:10:314:27 | ... * ... | -| test.cpp:309:19:309:24 | call to getenv | test.cpp:314:10:314:27 | ... * ... | -| test.cpp:309:19:309:32 | (const char *)... | test.cpp:314:10:314:27 | ... * ... | -| test.cpp:309:19:309:32 | (const char *)... | test.cpp:314:10:314:27 | ... * ... | nodes | test.cpp:39:21:39:24 | argv | semmle.label | argv | | test.cpp:39:21:39:24 | argv | semmle.label | argv | @@ -149,11 +137,6 @@ nodes | test.cpp:134:10:134:27 | ... * ... | semmle.label | ... * ... | | test.cpp:134:10:134:27 | ... * ... | semmle.label | ... * ... | | test.cpp:134:10:134:27 | ... * ... | semmle.label | ... * ... | -| test.cpp:138:19:138:24 | call to getenv | semmle.label | call to getenv | -| test.cpp:138:19:138:32 | (const char *)... | semmle.label | (const char *)... | -| test.cpp:142:11:142:28 | ... * ... | semmle.label | ... * ... | -| test.cpp:142:11:142:28 | ... * ... | semmle.label | ... * ... | -| test.cpp:142:11:142:28 | ... * ... | semmle.label | ... * ... | | test.cpp:201:9:201:42 | Store | semmle.label | Store | | test.cpp:201:14:201:19 | call to getenv | semmle.label | call to getenv | | test.cpp:201:14:201:27 | (const char *)... | semmle.label | (const char *)... | @@ -196,16 +179,6 @@ nodes | test.cpp:298:10:298:27 | ... * ... | semmle.label | ... * ... | | test.cpp:298:10:298:27 | ... * ... | semmle.label | ... * ... | | test.cpp:298:10:298:27 | ... * ... | semmle.label | ... * ... | -| test.cpp:301:19:301:24 | call to getenv | semmle.label | call to getenv | -| test.cpp:301:19:301:32 | (const char *)... | semmle.label | (const char *)... | -| test.cpp:305:11:305:28 | ... * ... | semmle.label | ... * ... | -| test.cpp:305:11:305:28 | ... * ... | semmle.label | ... * ... | -| test.cpp:305:11:305:28 | ... * ... | semmle.label | ... * ... | -| test.cpp:309:19:309:24 | call to getenv | semmle.label | call to getenv | -| test.cpp:309:19:309:32 | (const char *)... | semmle.label | (const char *)... | -| test.cpp:314:10:314:27 | ... * ... | semmle.label | ... * ... | -| test.cpp:314:10:314:27 | ... * ... | semmle.label | ... * ... | -| test.cpp:314:10:314:27 | ... * ... | semmle.label | ... * ... | #select | test.cpp:42:31:42:36 | call to malloc | test.cpp:39:21:39:24 | argv | test.cpp:42:38:42:44 | tainted | This allocation size is derived from $@ and might overflow | test.cpp:39:21:39:24 | argv | user input (argv) | | test.cpp:43:31:43:36 | call to malloc | test.cpp:39:21:39:24 | argv | test.cpp:43:38:43:63 | ... * ... | This allocation size is derived from $@ and might overflow | test.cpp:39:21:39:24 | argv | user input (argv) | @@ -216,7 +189,6 @@ nodes | test.cpp:79:9:79:29 | new[] | test.cpp:97:18:97:23 | buffer | test.cpp:79:18:79:28 | ... - ... | This allocation size is derived from $@ and might overflow | test.cpp:97:18:97:23 | buffer | user input (fread) | | test.cpp:127:17:127:22 | call to malloc | test.cpp:123:18:123:23 | call to getenv | test.cpp:127:24:127:41 | ... * ... | This allocation size is derived from $@ and might overflow | test.cpp:123:18:123:23 | call to getenv | user input (getenv) | | test.cpp:134:3:134:8 | call to malloc | test.cpp:132:19:132:24 | call to getenv | test.cpp:134:10:134:27 | ... * ... | This allocation size is derived from $@ and might overflow | test.cpp:132:19:132:24 | call to getenv | user input (getenv) | -| test.cpp:142:4:142:9 | call to malloc | test.cpp:138:19:138:24 | call to getenv | test.cpp:142:11:142:28 | ... * ... | This allocation size is derived from $@ and might overflow | test.cpp:138:19:138:24 | call to getenv | user input (getenv) | | test.cpp:215:14:215:19 | call to malloc | test.cpp:227:24:227:29 | call to getenv | test.cpp:215:21:215:21 | s | This allocation size is derived from $@ and might overflow | test.cpp:227:24:227:29 | call to getenv | user input (getenv) | | test.cpp:221:14:221:19 | call to malloc | test.cpp:227:24:227:29 | call to getenv | test.cpp:221:21:221:21 | s | This allocation size is derived from $@ and might overflow | test.cpp:227:24:227:29 | call to getenv | user input (getenv) | | test.cpp:229:2:229:7 | call to malloc | test.cpp:227:24:227:29 | call to getenv | test.cpp:229:9:229:18 | local_size | This allocation size is derived from $@ and might overflow | test.cpp:227:24:227:29 | call to getenv | user input (getenv) | @@ -224,5 +196,3 @@ nodes | test.cpp:253:4:253:9 | call to malloc | test.cpp:249:20:249:25 | call to getenv | test.cpp:253:11:253:29 | ... * ... | This allocation size is derived from $@ and might overflow | test.cpp:249:20:249:25 | call to getenv | user input (getenv) | | test.cpp:281:4:281:9 | call to malloc | test.cpp:241:18:241:23 | call to getenv | test.cpp:281:11:281:28 | ... * ... | This allocation size is derived from $@ and might overflow | test.cpp:241:18:241:23 | call to getenv | user input (getenv) | | test.cpp:298:3:298:8 | call to malloc | test.cpp:241:18:241:23 | call to getenv | test.cpp:298:10:298:27 | ... * ... | This allocation size is derived from $@ and might overflow | test.cpp:241:18:241:23 | call to getenv | user input (getenv) | -| test.cpp:305:4:305:9 | call to malloc | test.cpp:301:19:301:24 | call to getenv | test.cpp:305:11:305:28 | ... * ... | This allocation size is derived from $@ and might overflow | test.cpp:301:19:301:24 | call to getenv | user input (getenv) | -| test.cpp:314:3:314:8 | call to malloc | test.cpp:309:19:309:24 | call to getenv | test.cpp:314:10:314:27 | ... * ... | This allocation size is derived from $@ and might overflow | test.cpp:309:19:309:24 | call to getenv | user input (getenv) | diff --git a/cpp/ql/test/query-tests/Security/CWE/CWE-190/semmle/TaintedAllocationSize/test.cpp b/cpp/ql/test/query-tests/Security/CWE/CWE-190/semmle/TaintedAllocationSize/test.cpp index 943bc3b1214..d00dc10a445 100644 --- a/cpp/ql/test/query-tests/Security/CWE/CWE-190/semmle/TaintedAllocationSize/test.cpp +++ b/cpp/ql/test/query-tests/Security/CWE/CWE-190/semmle/TaintedAllocationSize/test.cpp @@ -139,7 +139,7 @@ void more_bounded_tests() { if (size > 0) { - malloc(size * sizeof(int)); // BAD + malloc(size * sizeof(int)); // GOOD (overflow not possible) } } @@ -302,7 +302,7 @@ void equality_cases() { if ((size == 50) || (size == 100)) { - malloc(size * sizeof(int)); // GOOD [FALSE POSITIVE] + malloc(size * sizeof(int)); // GOOD } } { @@ -311,6 +311,6 @@ void equality_cases() { if (size != 50 && size != 100) return; - malloc(size * sizeof(int)); // GOOD [FALSE POSITIVE] + malloc(size * sizeof(int)); // GOOD } } From 1497fba6f249c757d0a37d832c49d712e879af34 Mon Sep 17 00:00:00 2001 From: luchua-bc Date: Fri, 14 May 2021 11:43:49 +0000 Subject: [PATCH 140/168] Remove the isAdditionalTaintStep predicate --- .../Security/CWE/CWE-094/JythonInjection.ql | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/java/ql/src/experimental/Security/CWE/CWE-094/JythonInjection.ql b/java/ql/src/experimental/Security/CWE/CWE-094/JythonInjection.ql index 9991c0901dc..0f23edc69f4 100644 --- a/java/ql/src/experimental/Security/CWE/CWE-094/JythonInjection.ql +++ b/java/ql/src/experimental/Security/CWE/CWE-094/JythonInjection.ql @@ -102,16 +102,6 @@ class CodeInjectionConfiguration extends TaintTracking::Configuration { override predicate isSource(DataFlow::Node source) { source instanceof RemoteFlowSource } override predicate isSink(DataFlow::Node sink) { sink instanceof CodeInjectionSink } - - override predicate isAdditionalTaintStep(DataFlow::Node node1, DataFlow::Node node2) { - // @RequestBody MyQueryObj query; interpreter.exec(query.getInterpreterCode()); - exists(MethodAccess ma | - ma.getMethod().getDeclaringType().getASubtype*() instanceof SpringUntrustedDataType and - not ma.getMethod().getDeclaringType() instanceof TypeObject and - ma.getQualifier() = node1.asExpr() and - ma = node2.asExpr() - ) - } } from DataFlow::PathNode source, DataFlow::PathNode sink, CodeInjectionConfiguration conf From c1d41b31695edc664e2b3d8b1ca10cac3dda721b Mon Sep 17 00:00:00 2001 From: Mathias Vorreiter Pedersen Date: Fri, 14 May 2021 13:47:23 +0200 Subject: [PATCH 141/168] C++: Add false positive result from pointer-difference expressions. --- .../TaintedAllocationSize.expected | 14 ++++++++++++++ .../CWE-190/semmle/TaintedAllocationSize/test.cpp | 9 +++++++++ 2 files changed, 23 insertions(+) diff --git a/cpp/ql/test/query-tests/Security/CWE/CWE-190/semmle/TaintedAllocationSize/TaintedAllocationSize.expected b/cpp/ql/test/query-tests/Security/CWE/CWE-190/semmle/TaintedAllocationSize/TaintedAllocationSize.expected index cdcf3aa6847..a96865cae60 100644 --- a/cpp/ql/test/query-tests/Security/CWE/CWE-190/semmle/TaintedAllocationSize/TaintedAllocationSize.expected +++ b/cpp/ql/test/query-tests/Security/CWE/CWE-190/semmle/TaintedAllocationSize/TaintedAllocationSize.expected @@ -87,6 +87,12 @@ edges | test.cpp:295:18:295:21 | Chi | test.cpp:298:10:298:27 | ... * ... | | test.cpp:295:18:295:21 | Chi | test.cpp:298:10:298:27 | ... * ... | | test.cpp:295:18:295:21 | get_size output argument [array content] | test.cpp:295:18:295:21 | Chi | +| test.cpp:321:15:321:20 | call to getenv | test.cpp:324:9:324:14 | (size_t)... | +| test.cpp:321:15:321:20 | call to getenv | test.cpp:324:9:324:14 | (size_t)... | +| test.cpp:321:15:321:20 | call to getenv | test.cpp:324:9:324:14 | offset | +| test.cpp:321:15:321:20 | call to getenv | test.cpp:324:9:324:14 | offset | +| test.cpp:321:15:321:20 | call to getenv | test.cpp:324:9:324:14 | offset | +| test.cpp:321:15:321:20 | call to getenv | test.cpp:324:9:324:14 | offset | nodes | test.cpp:39:21:39:24 | argv | semmle.label | argv | | test.cpp:39:21:39:24 | argv | semmle.label | argv | @@ -179,6 +185,13 @@ nodes | test.cpp:298:10:298:27 | ... * ... | semmle.label | ... * ... | | test.cpp:298:10:298:27 | ... * ... | semmle.label | ... * ... | | test.cpp:298:10:298:27 | ... * ... | semmle.label | ... * ... | +| test.cpp:321:15:321:20 | call to getenv | semmle.label | call to getenv | +| test.cpp:321:15:321:20 | call to getenv | semmle.label | call to getenv | +| test.cpp:324:9:324:14 | (size_t)... | semmle.label | (size_t)... | +| test.cpp:324:9:324:14 | (size_t)... | semmle.label | (size_t)... | +| test.cpp:324:9:324:14 | offset | semmle.label | offset | +| test.cpp:324:9:324:14 | offset | semmle.label | offset | +| test.cpp:324:9:324:14 | offset | semmle.label | offset | #select | test.cpp:42:31:42:36 | call to malloc | test.cpp:39:21:39:24 | argv | test.cpp:42:38:42:44 | tainted | This allocation size is derived from $@ and might overflow | test.cpp:39:21:39:24 | argv | user input (argv) | | test.cpp:43:31:43:36 | call to malloc | test.cpp:39:21:39:24 | argv | test.cpp:43:38:43:63 | ... * ... | This allocation size is derived from $@ and might overflow | test.cpp:39:21:39:24 | argv | user input (argv) | @@ -196,3 +209,4 @@ nodes | test.cpp:253:4:253:9 | call to malloc | test.cpp:249:20:249:25 | call to getenv | test.cpp:253:11:253:29 | ... * ... | This allocation size is derived from $@ and might overflow | test.cpp:249:20:249:25 | call to getenv | user input (getenv) | | test.cpp:281:4:281:9 | call to malloc | test.cpp:241:18:241:23 | call to getenv | test.cpp:281:11:281:28 | ... * ... | This allocation size is derived from $@ and might overflow | test.cpp:241:18:241:23 | call to getenv | user input (getenv) | | test.cpp:298:3:298:8 | call to malloc | test.cpp:241:18:241:23 | call to getenv | test.cpp:298:10:298:27 | ... * ... | This allocation size is derived from $@ and might overflow | test.cpp:241:18:241:23 | call to getenv | user input (getenv) | +| test.cpp:324:2:324:7 | call to malloc | test.cpp:321:15:321:20 | call to getenv | test.cpp:324:9:324:14 | offset | This allocation size is derived from $@ and might overflow | test.cpp:321:15:321:20 | call to getenv | user input (getenv) | diff --git a/cpp/ql/test/query-tests/Security/CWE/CWE-190/semmle/TaintedAllocationSize/test.cpp b/cpp/ql/test/query-tests/Security/CWE/CWE-190/semmle/TaintedAllocationSize/test.cpp index d00dc10a445..0d630ac99cb 100644 --- a/cpp/ql/test/query-tests/Security/CWE/CWE-190/semmle/TaintedAllocationSize/test.cpp +++ b/cpp/ql/test/query-tests/Security/CWE/CWE-190/semmle/TaintedAllocationSize/test.cpp @@ -314,3 +314,12 @@ void equality_cases() { malloc(size * sizeof(int)); // GOOD } } + +char * strstr(char *, const char *); + +void ptr_diff_case() { + char* user = getenv("USER"); + char* admin_begin_pos = strstr(user, "ADMIN"); + int offset = admin_begin_pos ? user - admin_begin_pos : 0; + malloc(offset); // GOOD [FALSE POSITIVE] +} \ No newline at end of file From 2d0a56128d8f10acade04f99c73493671186d298 Mon Sep 17 00:00:00 2001 From: Mathias Vorreiter Pedersen Date: Fri, 14 May 2021 13:49:48 +0200 Subject: [PATCH 142/168] C++: Prevent flow out of pointer-difference expressions. --- .../CWE/CWE-190/TaintedAllocationSize.ql | 6 +++ .../TaintedAllocationSize.expected | 48 ------------------- .../semmle/TaintedAllocationSize/test.cpp | 4 +- 3 files changed, 8 insertions(+), 50 deletions(-) diff --git a/cpp/ql/src/Security/CWE/CWE-190/TaintedAllocationSize.ql b/cpp/ql/src/Security/CWE/CWE-190/TaintedAllocationSize.ql index 859d56ec2a0..75cac365a1a 100644 --- a/cpp/ql/src/Security/CWE/CWE-190/TaintedAllocationSize.ql +++ b/cpp/ql/src/Security/CWE/CWE-190/TaintedAllocationSize.ql @@ -42,6 +42,12 @@ class TaintedAllocationSizeConfiguration extends TaintTrackingConfiguration { e instanceof AssignArithmeticOperation ) and not convertedExprMightOverflow(e) + or + // Subtracting two pointers is either well-defined (and the result will likely be small), or + // terribly undefined and dangerous. Here, we assume that the programmer has ensured that the + // result is well-defined (i.e., the two pointers point to the same object), and thus the result + // will likely be small. + e = any(PointerDiffExpr diff).getAnOperand() } } diff --git a/cpp/ql/test/query-tests/Security/CWE/CWE-190/semmle/TaintedAllocationSize/TaintedAllocationSize.expected b/cpp/ql/test/query-tests/Security/CWE/CWE-190/semmle/TaintedAllocationSize/TaintedAllocationSize.expected index a96865cae60..234efe2c35b 100644 --- a/cpp/ql/test/query-tests/Security/CWE/CWE-190/semmle/TaintedAllocationSize/TaintedAllocationSize.expected +++ b/cpp/ql/test/query-tests/Security/CWE/CWE-190/semmle/TaintedAllocationSize/TaintedAllocationSize.expected @@ -27,24 +27,6 @@ edges | test.cpp:39:21:39:24 | argv | test.cpp:52:35:52:60 | ... * ... | | test.cpp:39:21:39:24 | argv | test.cpp:52:35:52:60 | ... * ... | | test.cpp:39:21:39:24 | argv | test.cpp:52:35:52:60 | ... * ... | -| test.cpp:75:25:75:29 | start | test.cpp:79:18:79:28 | ... - ... | -| test.cpp:75:25:75:29 | start | test.cpp:79:18:79:28 | ... - ... | -| test.cpp:75:38:75:40 | end | test.cpp:79:18:79:28 | ... - ... | -| test.cpp:75:38:75:40 | end | test.cpp:79:18:79:28 | ... - ... | -| test.cpp:97:18:97:23 | buffer | test.cpp:100:4:100:15 | buffer | -| test.cpp:97:18:97:23 | buffer | test.cpp:100:17:100:22 | buffer indirection | -| test.cpp:97:18:97:23 | buffer | test.cpp:101:4:101:15 | ... + ... | -| test.cpp:97:18:97:23 | buffer | test.cpp:101:4:101:15 | buffer | -| test.cpp:97:18:97:23 | fread output argument | test.cpp:100:4:100:15 | buffer | -| test.cpp:97:18:97:23 | fread output argument | test.cpp:100:17:100:22 | buffer indirection | -| test.cpp:97:18:97:23 | fread output argument | test.cpp:101:4:101:15 | ... + ... | -| test.cpp:97:18:97:23 | fread output argument | test.cpp:101:4:101:15 | buffer | -| test.cpp:100:4:100:15 | buffer | test.cpp:100:17:100:22 | processData1 output argument | -| test.cpp:100:17:100:22 | buffer indirection | test.cpp:100:17:100:22 | processData1 output argument | -| test.cpp:100:17:100:22 | processData1 output argument | test.cpp:101:4:101:15 | ... + ... | -| test.cpp:100:17:100:22 | processData1 output argument | test.cpp:101:4:101:15 | buffer | -| test.cpp:101:4:101:15 | ... + ... | test.cpp:75:38:75:40 | end | -| test.cpp:101:4:101:15 | buffer | test.cpp:75:25:75:29 | start | | test.cpp:123:18:123:23 | call to getenv | test.cpp:127:24:127:41 | ... * ... | | test.cpp:123:18:123:23 | call to getenv | test.cpp:127:24:127:41 | ... * ... | | test.cpp:123:18:123:31 | (const char *)... | test.cpp:127:24:127:41 | ... * ... | @@ -87,12 +69,6 @@ edges | test.cpp:295:18:295:21 | Chi | test.cpp:298:10:298:27 | ... * ... | | test.cpp:295:18:295:21 | Chi | test.cpp:298:10:298:27 | ... * ... | | test.cpp:295:18:295:21 | get_size output argument [array content] | test.cpp:295:18:295:21 | Chi | -| test.cpp:321:15:321:20 | call to getenv | test.cpp:324:9:324:14 | (size_t)... | -| test.cpp:321:15:321:20 | call to getenv | test.cpp:324:9:324:14 | (size_t)... | -| test.cpp:321:15:321:20 | call to getenv | test.cpp:324:9:324:14 | offset | -| test.cpp:321:15:321:20 | call to getenv | test.cpp:324:9:324:14 | offset | -| test.cpp:321:15:321:20 | call to getenv | test.cpp:324:9:324:14 | offset | -| test.cpp:321:15:321:20 | call to getenv | test.cpp:324:9:324:14 | offset | nodes | test.cpp:39:21:39:24 | argv | semmle.label | argv | | test.cpp:39:21:39:24 | argv | semmle.label | argv | @@ -118,21 +94,6 @@ nodes | test.cpp:52:35:52:60 | ... * ... | semmle.label | ... * ... | | test.cpp:52:35:52:60 | ... * ... | semmle.label | ... * ... | | test.cpp:52:35:52:60 | ... * ... | semmle.label | ... * ... | -| test.cpp:64:25:64:30 | *buffer | semmle.label | *buffer | -| test.cpp:64:25:64:30 | *buffer | semmle.label | *buffer | -| test.cpp:64:25:64:30 | buffer | semmle.label | buffer | -| test.cpp:75:25:75:29 | start | semmle.label | start | -| test.cpp:75:38:75:40 | end | semmle.label | end | -| test.cpp:79:18:79:28 | ... - ... | semmle.label | ... - ... | -| test.cpp:79:18:79:28 | ... - ... | semmle.label | ... - ... | -| test.cpp:79:18:79:28 | ... - ... | semmle.label | ... - ... | -| test.cpp:97:18:97:23 | buffer | semmle.label | buffer | -| test.cpp:97:18:97:23 | fread output argument | semmle.label | fread output argument | -| test.cpp:100:4:100:15 | buffer | semmle.label | buffer | -| test.cpp:100:17:100:22 | buffer indirection | semmle.label | buffer indirection | -| test.cpp:100:17:100:22 | processData1 output argument | semmle.label | processData1 output argument | -| test.cpp:101:4:101:15 | ... + ... | semmle.label | ... + ... | -| test.cpp:101:4:101:15 | buffer | semmle.label | buffer | | test.cpp:123:18:123:23 | call to getenv | semmle.label | call to getenv | | test.cpp:123:18:123:31 | (const char *)... | semmle.label | (const char *)... | | test.cpp:127:24:127:41 | ... * ... | semmle.label | ... * ... | @@ -185,13 +146,6 @@ nodes | test.cpp:298:10:298:27 | ... * ... | semmle.label | ... * ... | | test.cpp:298:10:298:27 | ... * ... | semmle.label | ... * ... | | test.cpp:298:10:298:27 | ... * ... | semmle.label | ... * ... | -| test.cpp:321:15:321:20 | call to getenv | semmle.label | call to getenv | -| test.cpp:321:15:321:20 | call to getenv | semmle.label | call to getenv | -| test.cpp:324:9:324:14 | (size_t)... | semmle.label | (size_t)... | -| test.cpp:324:9:324:14 | (size_t)... | semmle.label | (size_t)... | -| test.cpp:324:9:324:14 | offset | semmle.label | offset | -| test.cpp:324:9:324:14 | offset | semmle.label | offset | -| test.cpp:324:9:324:14 | offset | semmle.label | offset | #select | test.cpp:42:31:42:36 | call to malloc | test.cpp:39:21:39:24 | argv | test.cpp:42:38:42:44 | tainted | This allocation size is derived from $@ and might overflow | test.cpp:39:21:39:24 | argv | user input (argv) | | test.cpp:43:31:43:36 | call to malloc | test.cpp:39:21:39:24 | argv | test.cpp:43:38:43:63 | ... * ... | This allocation size is derived from $@ and might overflow | test.cpp:39:21:39:24 | argv | user input (argv) | @@ -199,7 +153,6 @@ nodes | test.cpp:48:25:48:30 | call to malloc | test.cpp:39:21:39:24 | argv | test.cpp:48:32:48:35 | size | This allocation size is derived from $@ and might overflow | test.cpp:39:21:39:24 | argv | user input (argv) | | test.cpp:49:17:49:30 | new[] | test.cpp:39:21:39:24 | argv | test.cpp:49:26:49:29 | size | This allocation size is derived from $@ and might overflow | test.cpp:39:21:39:24 | argv | user input (argv) | | test.cpp:52:21:52:27 | call to realloc | test.cpp:39:21:39:24 | argv | test.cpp:52:35:52:60 | ... * ... | This allocation size is derived from $@ and might overflow | test.cpp:39:21:39:24 | argv | user input (argv) | -| test.cpp:79:9:79:29 | new[] | test.cpp:97:18:97:23 | buffer | test.cpp:79:18:79:28 | ... - ... | This allocation size is derived from $@ and might overflow | test.cpp:97:18:97:23 | buffer | user input (fread) | | test.cpp:127:17:127:22 | call to malloc | test.cpp:123:18:123:23 | call to getenv | test.cpp:127:24:127:41 | ... * ... | This allocation size is derived from $@ and might overflow | test.cpp:123:18:123:23 | call to getenv | user input (getenv) | | test.cpp:134:3:134:8 | call to malloc | test.cpp:132:19:132:24 | call to getenv | test.cpp:134:10:134:27 | ... * ... | This allocation size is derived from $@ and might overflow | test.cpp:132:19:132:24 | call to getenv | user input (getenv) | | test.cpp:215:14:215:19 | call to malloc | test.cpp:227:24:227:29 | call to getenv | test.cpp:215:21:215:21 | s | This allocation size is derived from $@ and might overflow | test.cpp:227:24:227:29 | call to getenv | user input (getenv) | @@ -209,4 +162,3 @@ nodes | test.cpp:253:4:253:9 | call to malloc | test.cpp:249:20:249:25 | call to getenv | test.cpp:253:11:253:29 | ... * ... | This allocation size is derived from $@ and might overflow | test.cpp:249:20:249:25 | call to getenv | user input (getenv) | | test.cpp:281:4:281:9 | call to malloc | test.cpp:241:18:241:23 | call to getenv | test.cpp:281:11:281:28 | ... * ... | This allocation size is derived from $@ and might overflow | test.cpp:241:18:241:23 | call to getenv | user input (getenv) | | test.cpp:298:3:298:8 | call to malloc | test.cpp:241:18:241:23 | call to getenv | test.cpp:298:10:298:27 | ... * ... | This allocation size is derived from $@ and might overflow | test.cpp:241:18:241:23 | call to getenv | user input (getenv) | -| test.cpp:324:2:324:7 | call to malloc | test.cpp:321:15:321:20 | call to getenv | test.cpp:324:9:324:14 | offset | This allocation size is derived from $@ and might overflow | test.cpp:321:15:321:20 | call to getenv | user input (getenv) | diff --git a/cpp/ql/test/query-tests/Security/CWE/CWE-190/semmle/TaintedAllocationSize/test.cpp b/cpp/ql/test/query-tests/Security/CWE/CWE-190/semmle/TaintedAllocationSize/test.cpp index 0d630ac99cb..57eb5b91a07 100644 --- a/cpp/ql/test/query-tests/Security/CWE/CWE-190/semmle/TaintedAllocationSize/test.cpp +++ b/cpp/ql/test/query-tests/Security/CWE/CWE-190/semmle/TaintedAllocationSize/test.cpp @@ -76,7 +76,7 @@ void processData2(char *start, char *end) { char *copy; - copy = new char[end - start]; // GOOD [FALSE POSITIVE] + copy = new char[end - start]; // GOOD // ... @@ -321,5 +321,5 @@ void ptr_diff_case() { char* user = getenv("USER"); char* admin_begin_pos = strstr(user, "ADMIN"); int offset = admin_begin_pos ? user - admin_begin_pos : 0; - malloc(offset); // GOOD [FALSE POSITIVE] + malloc(offset); // GOOD } \ No newline at end of file From 58dde68b10d74d6d7ed4b7d206c1fe319b4e15ac Mon Sep 17 00:00:00 2001 From: Mathias Vorreiter Pedersen Date: Fri, 14 May 2021 14:16:00 +0200 Subject: [PATCH 143/168] C++: Add change-note. --- cpp/change-notes/2021-05-14-uncontrolled-allocation-size.md | 2 ++ 1 file changed, 2 insertions(+) create mode 100644 cpp/change-notes/2021-05-14-uncontrolled-allocation-size.md diff --git a/cpp/change-notes/2021-05-14-uncontrolled-allocation-size.md b/cpp/change-notes/2021-05-14-uncontrolled-allocation-size.md new file mode 100644 index 00000000000..6f0c9d6fa98 --- /dev/null +++ b/cpp/change-notes/2021-05-14-uncontrolled-allocation-size.md @@ -0,0 +1,2 @@ +lgtm +* The "Tainted allocation size" query (cpp/uncontrolled-allocation-size) has been improved to produce fewer false positives. From 4cf695b5ab0a8a50e5143f298e8e1ff3e7ec3c30 Mon Sep 17 00:00:00 2001 From: Ethan Palm <56270045+ethanpalm@users.noreply.github.com> Date: Fri, 14 May 2021 10:00:17 -0400 Subject: [PATCH 144/168] specify ``--command`` option Co-authored-by: intrigus-lgtm <60750685+intrigus-lgtm@users.noreply.github.com> --- docs/codeql/codeql-cli/creating-codeql-databases.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/codeql/codeql-cli/creating-codeql-databases.rst b/docs/codeql/codeql-cli/creating-codeql-databases.rst index a41727e1956..8810e3deb9d 100644 --- a/docs/codeql/codeql-cli/creating-codeql-databases.rst +++ b/docs/codeql/codeql-cli/creating-codeql-databases.rst @@ -172,7 +172,7 @@ build steps, you may need to explicitly define each step in the command line. The Go autobuilder attempts to automatically detect Go code in a repository, and only runs build scripts in an attempt to fetch dependencies. To force CodeQL to use your build script, set the environment variable - `CODEQL_EXTRACTOR_GO_BUILD_TRACING=on` or pass a command. + `CODEQL_EXTRACTOR_GO_BUILD_TRACING=on` or use the ``--command`` option. Specifying build commands ~~~~~~~~~~~~~~~~~~~~~~~~~ From 6dd30ee5e2a03fdffc8b7cb8bf23689bce2813b7 Mon Sep 17 00:00:00 2001 From: Ethan Palm <56270045+ethanpalm@users.noreply.github.com> Date: Fri, 14 May 2021 14:00:33 -0400 Subject: [PATCH 145/168] clarify options for tracing Co-authored-by: Chris Smowton --- docs/codeql/codeql-cli/creating-codeql-databases.rst | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/docs/codeql/codeql-cli/creating-codeql-databases.rst b/docs/codeql/codeql-cli/creating-codeql-databases.rst index 8810e3deb9d..9cdd02f78fc 100644 --- a/docs/codeql/codeql-cli/creating-codeql-databases.rst +++ b/docs/codeql/codeql-cli/creating-codeql-databases.rst @@ -171,8 +171,9 @@ build steps, you may need to explicitly define each step in the command line. The Go autobuilder attempts to automatically detect Go code in a repository, and only runs build scripts in an attempt to fetch dependencies. To force - CodeQL to use your build script, set the environment variable - `CODEQL_EXTRACTOR_GO_BUILD_TRACING=on` or use the ``--command`` option. + CodeQL to limit extraction to the files compiled by your build script, set the environment variable + `CODEQL_EXTRACTOR_GO_BUILD_TRACING=on` or use the ``--command`` option to specify a + build command. Specifying build commands ~~~~~~~~~~~~~~~~~~~~~~~~~ From 0e99d5e379041cdffcaab2da4e57218648d1fd96 Mon Sep 17 00:00:00 2001 From: Ethan P <56270045+ethanpalm@users.noreply.github.com> Date: Fri, 14 May 2021 14:05:55 -0400 Subject: [PATCH 146/168] Add examples of both tracing mechanisms --- docs/codeql/codeql-cli/creating-codeql-databases.rst | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/docs/codeql/codeql-cli/creating-codeql-databases.rst b/docs/codeql/codeql-cli/creating-codeql-databases.rst index 9cdd02f78fc..8658da9a1d4 100644 --- a/docs/codeql/codeql-cli/creating-codeql-databases.rst +++ b/docs/codeql/codeql-cli/creating-codeql-databases.rst @@ -202,9 +202,12 @@ commands that you can specify for compiled languages. codeql database create csharp-database --language=csharp --command='dotnet build /p:UseSharedCompilation=false /t:rebuild' +- Go project built using the ``COEQL_EXTRACTOR_GO_BUILD_TRACING=on`` environment variable:: + CODEQL_EXTRACTOR_GO_BUILD_TRACING=on codeql database create go-database --language=go + - Go project built using a custom build script:: - CODEQL_EXTRACTOR_GO_BUILD_TRACING=on codeql database create go-database --language=go --command='./scripts/build.sh' + codeql database create go-database --language=go --command='./scripts/build.sh' - Java project built using Gradle:: From 58c746e42b174c1ee4465569e832c7a7cb735d03 Mon Sep 17 00:00:00 2001 From: Ethan P <56270045+ethanpalm@users.noreply.github.com> Date: Fri, 14 May 2021 14:09:07 -0400 Subject: [PATCH 147/168] fix formatting --- docs/codeql/codeql-cli/creating-codeql-databases.rst | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/codeql/codeql-cli/creating-codeql-databases.rst b/docs/codeql/codeql-cli/creating-codeql-databases.rst index 8658da9a1d4..1552a077e24 100644 --- a/docs/codeql/codeql-cli/creating-codeql-databases.rst +++ b/docs/codeql/codeql-cli/creating-codeql-databases.rst @@ -203,6 +203,7 @@ commands that you can specify for compiled languages. codeql database create csharp-database --language=csharp --command='dotnet build /p:UseSharedCompilation=false /t:rebuild' - Go project built using the ``COEQL_EXTRACTOR_GO_BUILD_TRACING=on`` environment variable:: + CODEQL_EXTRACTOR_GO_BUILD_TRACING=on codeql database create go-database --language=go - Go project built using a custom build script:: From 31091c66c19fdb401967decc596621fa68db8d0a Mon Sep 17 00:00:00 2001 From: Mathias Vorreiter Pedersen Date: Mon, 17 May 2021 08:06:06 +0200 Subject: [PATCH 148/168] C++: Add a test containing a guarded long. --- .../TaintedAllocationSize.expected | 332 +++++++++--------- .../semmle/TaintedAllocationSize/test.cpp | 10 + 2 files changed, 181 insertions(+), 161 deletions(-) diff --git a/cpp/ql/test/query-tests/Security/CWE/CWE-190/semmle/TaintedAllocationSize/TaintedAllocationSize.expected b/cpp/ql/test/query-tests/Security/CWE/CWE-190/semmle/TaintedAllocationSize/TaintedAllocationSize.expected index 234efe2c35b..752e9165c07 100644 --- a/cpp/ql/test/query-tests/Security/CWE/CWE-190/semmle/TaintedAllocationSize/TaintedAllocationSize.expected +++ b/cpp/ql/test/query-tests/Security/CWE/CWE-190/semmle/TaintedAllocationSize/TaintedAllocationSize.expected @@ -1,164 +1,174 @@ edges -| test.cpp:39:21:39:24 | argv | test.cpp:42:38:42:44 | (size_t)... | -| test.cpp:39:21:39:24 | argv | test.cpp:42:38:42:44 | (size_t)... | -| test.cpp:39:21:39:24 | argv | test.cpp:42:38:42:44 | tainted | -| test.cpp:39:21:39:24 | argv | test.cpp:42:38:42:44 | tainted | -| test.cpp:39:21:39:24 | argv | test.cpp:42:38:42:44 | tainted | -| test.cpp:39:21:39:24 | argv | test.cpp:42:38:42:44 | tainted | -| test.cpp:39:21:39:24 | argv | test.cpp:43:38:43:63 | ... * ... | -| test.cpp:39:21:39:24 | argv | test.cpp:43:38:43:63 | ... * ... | -| test.cpp:39:21:39:24 | argv | test.cpp:43:38:43:63 | ... * ... | -| test.cpp:39:21:39:24 | argv | test.cpp:43:38:43:63 | ... * ... | -| test.cpp:39:21:39:24 | argv | test.cpp:45:38:45:63 | ... + ... | -| test.cpp:39:21:39:24 | argv | test.cpp:45:38:45:63 | ... + ... | -| test.cpp:39:21:39:24 | argv | test.cpp:45:38:45:63 | ... + ... | -| test.cpp:39:21:39:24 | argv | test.cpp:45:38:45:63 | ... + ... | -| test.cpp:39:21:39:24 | argv | test.cpp:48:32:48:35 | (size_t)... | -| test.cpp:39:21:39:24 | argv | test.cpp:48:32:48:35 | (size_t)... | -| test.cpp:39:21:39:24 | argv | test.cpp:48:32:48:35 | size | -| test.cpp:39:21:39:24 | argv | test.cpp:48:32:48:35 | size | -| test.cpp:39:21:39:24 | argv | test.cpp:48:32:48:35 | size | -| test.cpp:39:21:39:24 | argv | test.cpp:48:32:48:35 | size | -| test.cpp:39:21:39:24 | argv | test.cpp:49:26:49:29 | size | -| test.cpp:39:21:39:24 | argv | test.cpp:49:26:49:29 | size | -| test.cpp:39:21:39:24 | argv | test.cpp:49:26:49:29 | size | -| test.cpp:39:21:39:24 | argv | test.cpp:49:26:49:29 | size | -| test.cpp:39:21:39:24 | argv | test.cpp:52:35:52:60 | ... * ... | -| test.cpp:39:21:39:24 | argv | test.cpp:52:35:52:60 | ... * ... | -| test.cpp:39:21:39:24 | argv | test.cpp:52:35:52:60 | ... * ... | -| test.cpp:39:21:39:24 | argv | test.cpp:52:35:52:60 | ... * ... | -| test.cpp:123:18:123:23 | call to getenv | test.cpp:127:24:127:41 | ... * ... | -| test.cpp:123:18:123:23 | call to getenv | test.cpp:127:24:127:41 | ... * ... | -| test.cpp:123:18:123:31 | (const char *)... | test.cpp:127:24:127:41 | ... * ... | -| test.cpp:123:18:123:31 | (const char *)... | test.cpp:127:24:127:41 | ... * ... | -| test.cpp:132:19:132:24 | call to getenv | test.cpp:134:10:134:27 | ... * ... | -| test.cpp:132:19:132:24 | call to getenv | test.cpp:134:10:134:27 | ... * ... | -| test.cpp:132:19:132:32 | (const char *)... | test.cpp:134:10:134:27 | ... * ... | -| test.cpp:132:19:132:32 | (const char *)... | test.cpp:134:10:134:27 | ... * ... | -| test.cpp:201:9:201:42 | Store | test.cpp:231:9:231:24 | call to get_tainted_size | -| test.cpp:201:9:201:42 | Store | test.cpp:231:9:231:24 | call to get_tainted_size | -| test.cpp:201:14:201:19 | call to getenv | test.cpp:201:9:201:42 | Store | -| test.cpp:201:14:201:27 | (const char *)... | test.cpp:201:9:201:42 | Store | -| test.cpp:214:23:214:23 | s | test.cpp:215:21:215:21 | s | -| test.cpp:214:23:214:23 | s | test.cpp:215:21:215:21 | s | -| test.cpp:220:21:220:21 | s | test.cpp:221:21:221:21 | s | -| test.cpp:220:21:220:21 | s | test.cpp:221:21:221:21 | s | -| test.cpp:227:24:227:29 | call to getenv | test.cpp:229:9:229:18 | (size_t)... | -| test.cpp:227:24:227:29 | call to getenv | test.cpp:229:9:229:18 | local_size | -| test.cpp:227:24:227:29 | call to getenv | test.cpp:229:9:229:18 | local_size | -| test.cpp:227:24:227:29 | call to getenv | test.cpp:235:2:235:9 | local_size | -| test.cpp:227:24:227:29 | call to getenv | test.cpp:237:2:237:8 | local_size | -| test.cpp:227:24:227:37 | (const char *)... | test.cpp:229:9:229:18 | (size_t)... | -| test.cpp:227:24:227:37 | (const char *)... | test.cpp:229:9:229:18 | local_size | -| test.cpp:227:24:227:37 | (const char *)... | test.cpp:229:9:229:18 | local_size | -| test.cpp:227:24:227:37 | (const char *)... | test.cpp:235:2:235:9 | local_size | -| test.cpp:227:24:227:37 | (const char *)... | test.cpp:237:2:237:8 | local_size | -| test.cpp:235:2:235:9 | local_size | test.cpp:214:23:214:23 | s | -| test.cpp:237:2:237:8 | local_size | test.cpp:220:21:220:21 | s | -| test.cpp:241:2:241:32 | Chi [array content] | test.cpp:279:17:279:20 | get_size output argument [array content] | -| test.cpp:241:2:241:32 | Chi [array content] | test.cpp:295:18:295:21 | get_size output argument [array content] | -| test.cpp:241:18:241:23 | call to getenv | test.cpp:241:2:241:32 | Chi [array content] | -| test.cpp:241:18:241:31 | (const char *)... | test.cpp:241:2:241:32 | Chi [array content] | -| test.cpp:249:20:249:25 | call to getenv | test.cpp:253:11:253:29 | ... * ... | -| test.cpp:249:20:249:25 | call to getenv | test.cpp:253:11:253:29 | ... * ... | -| test.cpp:249:20:249:33 | (const char *)... | test.cpp:253:11:253:29 | ... * ... | -| test.cpp:249:20:249:33 | (const char *)... | test.cpp:253:11:253:29 | ... * ... | -| test.cpp:279:17:279:20 | Chi | test.cpp:281:11:281:28 | ... * ... | -| test.cpp:279:17:279:20 | Chi | test.cpp:281:11:281:28 | ... * ... | -| test.cpp:279:17:279:20 | get_size output argument [array content] | test.cpp:279:17:279:20 | Chi | -| test.cpp:295:18:295:21 | Chi | test.cpp:298:10:298:27 | ... * ... | -| test.cpp:295:18:295:21 | Chi | test.cpp:298:10:298:27 | ... * ... | -| test.cpp:295:18:295:21 | get_size output argument [array content] | test.cpp:295:18:295:21 | Chi | +| test.cpp:40:21:40:24 | argv | test.cpp:43:38:43:44 | (size_t)... | +| test.cpp:40:21:40:24 | argv | test.cpp:43:38:43:44 | (size_t)... | +| test.cpp:40:21:40:24 | argv | test.cpp:43:38:43:44 | tainted | +| test.cpp:40:21:40:24 | argv | test.cpp:43:38:43:44 | tainted | +| test.cpp:40:21:40:24 | argv | test.cpp:43:38:43:44 | tainted | +| test.cpp:40:21:40:24 | argv | test.cpp:43:38:43:44 | tainted | +| test.cpp:40:21:40:24 | argv | test.cpp:44:38:44:63 | ... * ... | +| test.cpp:40:21:40:24 | argv | test.cpp:44:38:44:63 | ... * ... | +| test.cpp:40:21:40:24 | argv | test.cpp:44:38:44:63 | ... * ... | +| test.cpp:40:21:40:24 | argv | test.cpp:44:38:44:63 | ... * ... | +| test.cpp:40:21:40:24 | argv | test.cpp:46:38:46:63 | ... + ... | +| test.cpp:40:21:40:24 | argv | test.cpp:46:38:46:63 | ... + ... | +| test.cpp:40:21:40:24 | argv | test.cpp:46:38:46:63 | ... + ... | +| test.cpp:40:21:40:24 | argv | test.cpp:46:38:46:63 | ... + ... | +| test.cpp:40:21:40:24 | argv | test.cpp:49:32:49:35 | (size_t)... | +| test.cpp:40:21:40:24 | argv | test.cpp:49:32:49:35 | (size_t)... | +| test.cpp:40:21:40:24 | argv | test.cpp:49:32:49:35 | size | +| test.cpp:40:21:40:24 | argv | test.cpp:49:32:49:35 | size | +| test.cpp:40:21:40:24 | argv | test.cpp:49:32:49:35 | size | +| test.cpp:40:21:40:24 | argv | test.cpp:49:32:49:35 | size | +| test.cpp:40:21:40:24 | argv | test.cpp:50:26:50:29 | size | +| test.cpp:40:21:40:24 | argv | test.cpp:50:26:50:29 | size | +| test.cpp:40:21:40:24 | argv | test.cpp:50:26:50:29 | size | +| test.cpp:40:21:40:24 | argv | test.cpp:50:26:50:29 | size | +| test.cpp:40:21:40:24 | argv | test.cpp:53:35:53:60 | ... * ... | +| test.cpp:40:21:40:24 | argv | test.cpp:53:35:53:60 | ... * ... | +| test.cpp:40:21:40:24 | argv | test.cpp:53:35:53:60 | ... * ... | +| test.cpp:40:21:40:24 | argv | test.cpp:53:35:53:60 | ... * ... | +| test.cpp:124:18:124:23 | call to getenv | test.cpp:128:24:128:41 | ... * ... | +| test.cpp:124:18:124:23 | call to getenv | test.cpp:128:24:128:41 | ... * ... | +| test.cpp:124:18:124:31 | (const char *)... | test.cpp:128:24:128:41 | ... * ... | +| test.cpp:124:18:124:31 | (const char *)... | test.cpp:128:24:128:41 | ... * ... | +| test.cpp:133:19:133:24 | call to getenv | test.cpp:135:10:135:27 | ... * ... | +| test.cpp:133:19:133:24 | call to getenv | test.cpp:135:10:135:27 | ... * ... | +| test.cpp:133:19:133:32 | (const char *)... | test.cpp:135:10:135:27 | ... * ... | +| test.cpp:133:19:133:32 | (const char *)... | test.cpp:135:10:135:27 | ... * ... | +| test.cpp:148:20:148:25 | call to getenv | test.cpp:152:11:152:28 | ... * ... | +| test.cpp:148:20:148:25 | call to getenv | test.cpp:152:11:152:28 | ... * ... | +| test.cpp:148:20:148:33 | (const char *)... | test.cpp:152:11:152:28 | ... * ... | +| test.cpp:148:20:148:33 | (const char *)... | test.cpp:152:11:152:28 | ... * ... | +| test.cpp:211:9:211:42 | Store | test.cpp:241:9:241:24 | call to get_tainted_size | +| test.cpp:211:9:211:42 | Store | test.cpp:241:9:241:24 | call to get_tainted_size | +| test.cpp:211:14:211:19 | call to getenv | test.cpp:211:9:211:42 | Store | +| test.cpp:211:14:211:27 | (const char *)... | test.cpp:211:9:211:42 | Store | +| test.cpp:224:23:224:23 | s | test.cpp:225:21:225:21 | s | +| test.cpp:224:23:224:23 | s | test.cpp:225:21:225:21 | s | +| test.cpp:230:21:230:21 | s | test.cpp:231:21:231:21 | s | +| test.cpp:230:21:230:21 | s | test.cpp:231:21:231:21 | s | +| test.cpp:237:24:237:29 | call to getenv | test.cpp:239:9:239:18 | (size_t)... | +| test.cpp:237:24:237:29 | call to getenv | test.cpp:239:9:239:18 | local_size | +| test.cpp:237:24:237:29 | call to getenv | test.cpp:239:9:239:18 | local_size | +| test.cpp:237:24:237:29 | call to getenv | test.cpp:245:2:245:9 | local_size | +| test.cpp:237:24:237:29 | call to getenv | test.cpp:247:2:247:8 | local_size | +| test.cpp:237:24:237:37 | (const char *)... | test.cpp:239:9:239:18 | (size_t)... | +| test.cpp:237:24:237:37 | (const char *)... | test.cpp:239:9:239:18 | local_size | +| test.cpp:237:24:237:37 | (const char *)... | test.cpp:239:9:239:18 | local_size | +| test.cpp:237:24:237:37 | (const char *)... | test.cpp:245:2:245:9 | local_size | +| test.cpp:237:24:237:37 | (const char *)... | test.cpp:247:2:247:8 | local_size | +| test.cpp:245:2:245:9 | local_size | test.cpp:224:23:224:23 | s | +| test.cpp:247:2:247:8 | local_size | test.cpp:230:21:230:21 | s | +| test.cpp:251:2:251:32 | Chi [array content] | test.cpp:289:17:289:20 | get_size output argument [array content] | +| test.cpp:251:2:251:32 | Chi [array content] | test.cpp:305:18:305:21 | get_size output argument [array content] | +| test.cpp:251:18:251:23 | call to getenv | test.cpp:251:2:251:32 | Chi [array content] | +| test.cpp:251:18:251:31 | (const char *)... | test.cpp:251:2:251:32 | Chi [array content] | +| test.cpp:259:20:259:25 | call to getenv | test.cpp:263:11:263:29 | ... * ... | +| test.cpp:259:20:259:25 | call to getenv | test.cpp:263:11:263:29 | ... * ... | +| test.cpp:259:20:259:33 | (const char *)... | test.cpp:263:11:263:29 | ... * ... | +| test.cpp:259:20:259:33 | (const char *)... | test.cpp:263:11:263:29 | ... * ... | +| test.cpp:289:17:289:20 | Chi | test.cpp:291:11:291:28 | ... * ... | +| test.cpp:289:17:289:20 | Chi | test.cpp:291:11:291:28 | ... * ... | +| test.cpp:289:17:289:20 | get_size output argument [array content] | test.cpp:289:17:289:20 | Chi | +| test.cpp:305:18:305:21 | Chi | test.cpp:308:10:308:27 | ... * ... | +| test.cpp:305:18:305:21 | Chi | test.cpp:308:10:308:27 | ... * ... | +| test.cpp:305:18:305:21 | get_size output argument [array content] | test.cpp:305:18:305:21 | Chi | nodes -| test.cpp:39:21:39:24 | argv | semmle.label | argv | -| test.cpp:39:21:39:24 | argv | semmle.label | argv | -| test.cpp:42:38:42:44 | (size_t)... | semmle.label | (size_t)... | -| test.cpp:42:38:42:44 | (size_t)... | semmle.label | (size_t)... | -| test.cpp:42:38:42:44 | tainted | semmle.label | tainted | -| test.cpp:42:38:42:44 | tainted | semmle.label | tainted | -| test.cpp:42:38:42:44 | tainted | semmle.label | tainted | -| test.cpp:43:38:43:63 | ... * ... | semmle.label | ... * ... | -| test.cpp:43:38:43:63 | ... * ... | semmle.label | ... * ... | -| test.cpp:43:38:43:63 | ... * ... | semmle.label | ... * ... | -| test.cpp:45:38:45:63 | ... + ... | semmle.label | ... + ... | -| test.cpp:45:38:45:63 | ... + ... | semmle.label | ... + ... | -| test.cpp:45:38:45:63 | ... + ... | semmle.label | ... + ... | -| test.cpp:48:32:48:35 | (size_t)... | semmle.label | (size_t)... | -| test.cpp:48:32:48:35 | (size_t)... | semmle.label | (size_t)... | -| test.cpp:48:32:48:35 | size | semmle.label | size | -| test.cpp:48:32:48:35 | size | semmle.label | size | -| test.cpp:48:32:48:35 | size | semmle.label | size | -| test.cpp:49:26:49:29 | size | semmle.label | size | -| test.cpp:49:26:49:29 | size | semmle.label | size | -| test.cpp:49:26:49:29 | size | semmle.label | size | -| test.cpp:52:35:52:60 | ... * ... | semmle.label | ... * ... | -| test.cpp:52:35:52:60 | ... * ... | semmle.label | ... * ... | -| test.cpp:52:35:52:60 | ... * ... | semmle.label | ... * ... | -| test.cpp:123:18:123:23 | call to getenv | semmle.label | call to getenv | -| test.cpp:123:18:123:31 | (const char *)... | semmle.label | (const char *)... | -| test.cpp:127:24:127:41 | ... * ... | semmle.label | ... * ... | -| test.cpp:127:24:127:41 | ... * ... | semmle.label | ... * ... | -| test.cpp:127:24:127:41 | ... * ... | semmle.label | ... * ... | -| test.cpp:132:19:132:24 | call to getenv | semmle.label | call to getenv | -| test.cpp:132:19:132:32 | (const char *)... | semmle.label | (const char *)... | -| test.cpp:134:10:134:27 | ... * ... | semmle.label | ... * ... | -| test.cpp:134:10:134:27 | ... * ... | semmle.label | ... * ... | -| test.cpp:134:10:134:27 | ... * ... | semmle.label | ... * ... | -| test.cpp:201:9:201:42 | Store | semmle.label | Store | -| test.cpp:201:14:201:19 | call to getenv | semmle.label | call to getenv | -| test.cpp:201:14:201:27 | (const char *)... | semmle.label | (const char *)... | -| test.cpp:214:23:214:23 | s | semmle.label | s | -| test.cpp:215:21:215:21 | s | semmle.label | s | -| test.cpp:215:21:215:21 | s | semmle.label | s | -| test.cpp:215:21:215:21 | s | semmle.label | s | -| test.cpp:220:21:220:21 | s | semmle.label | s | -| test.cpp:221:21:221:21 | s | semmle.label | s | -| test.cpp:221:21:221:21 | s | semmle.label | s | -| test.cpp:221:21:221:21 | s | semmle.label | s | -| test.cpp:227:24:227:29 | call to getenv | semmle.label | call to getenv | -| test.cpp:227:24:227:37 | (const char *)... | semmle.label | (const char *)... | -| test.cpp:229:9:229:18 | (size_t)... | semmle.label | (size_t)... | -| test.cpp:229:9:229:18 | (size_t)... | semmle.label | (size_t)... | -| test.cpp:229:9:229:18 | local_size | semmle.label | local_size | -| test.cpp:229:9:229:18 | local_size | semmle.label | local_size | -| test.cpp:229:9:229:18 | local_size | semmle.label | local_size | -| test.cpp:231:9:231:24 | call to get_tainted_size | semmle.label | call to get_tainted_size | -| test.cpp:231:9:231:24 | call to get_tainted_size | semmle.label | call to get_tainted_size | -| test.cpp:231:9:231:24 | call to get_tainted_size | semmle.label | call to get_tainted_size | -| test.cpp:235:2:235:9 | local_size | semmle.label | local_size | -| test.cpp:237:2:237:8 | local_size | semmle.label | local_size | -| test.cpp:241:2:241:32 | Chi [array content] | semmle.label | Chi [array content] | -| test.cpp:241:2:241:32 | ChiPartial | semmle.label | ChiPartial | -| test.cpp:241:18:241:23 | call to getenv | semmle.label | call to getenv | -| test.cpp:241:18:241:31 | (const char *)... | semmle.label | (const char *)... | -| test.cpp:249:20:249:25 | call to getenv | semmle.label | call to getenv | -| test.cpp:249:20:249:33 | (const char *)... | semmle.label | (const char *)... | -| test.cpp:253:11:253:29 | ... * ... | semmle.label | ... * ... | -| test.cpp:253:11:253:29 | ... * ... | semmle.label | ... * ... | -| test.cpp:253:11:253:29 | ... * ... | semmle.label | ... * ... | -| test.cpp:279:17:279:20 | Chi | semmle.label | Chi | -| test.cpp:279:17:279:20 | get_size output argument [array content] | semmle.label | get_size output argument [array content] | -| test.cpp:281:11:281:28 | ... * ... | semmle.label | ... * ... | -| test.cpp:281:11:281:28 | ... * ... | semmle.label | ... * ... | -| test.cpp:281:11:281:28 | ... * ... | semmle.label | ... * ... | -| test.cpp:295:18:295:21 | Chi | semmle.label | Chi | -| test.cpp:295:18:295:21 | get_size output argument [array content] | semmle.label | get_size output argument [array content] | -| test.cpp:298:10:298:27 | ... * ... | semmle.label | ... * ... | -| test.cpp:298:10:298:27 | ... * ... | semmle.label | ... * ... | -| test.cpp:298:10:298:27 | ... * ... | semmle.label | ... * ... | +| test.cpp:40:21:40:24 | argv | semmle.label | argv | +| test.cpp:40:21:40:24 | argv | semmle.label | argv | +| test.cpp:43:38:43:44 | (size_t)... | semmle.label | (size_t)... | +| test.cpp:43:38:43:44 | (size_t)... | semmle.label | (size_t)... | +| test.cpp:43:38:43:44 | tainted | semmle.label | tainted | +| test.cpp:43:38:43:44 | tainted | semmle.label | tainted | +| test.cpp:43:38:43:44 | tainted | semmle.label | tainted | +| test.cpp:44:38:44:63 | ... * ... | semmle.label | ... * ... | +| test.cpp:44:38:44:63 | ... * ... | semmle.label | ... * ... | +| test.cpp:44:38:44:63 | ... * ... | semmle.label | ... * ... | +| test.cpp:46:38:46:63 | ... + ... | semmle.label | ... + ... | +| test.cpp:46:38:46:63 | ... + ... | semmle.label | ... + ... | +| test.cpp:46:38:46:63 | ... + ... | semmle.label | ... + ... | +| test.cpp:49:32:49:35 | (size_t)... | semmle.label | (size_t)... | +| test.cpp:49:32:49:35 | (size_t)... | semmle.label | (size_t)... | +| test.cpp:49:32:49:35 | size | semmle.label | size | +| test.cpp:49:32:49:35 | size | semmle.label | size | +| test.cpp:49:32:49:35 | size | semmle.label | size | +| test.cpp:50:26:50:29 | size | semmle.label | size | +| test.cpp:50:26:50:29 | size | semmle.label | size | +| test.cpp:50:26:50:29 | size | semmle.label | size | +| test.cpp:53:35:53:60 | ... * ... | semmle.label | ... * ... | +| test.cpp:53:35:53:60 | ... * ... | semmle.label | ... * ... | +| test.cpp:53:35:53:60 | ... * ... | semmle.label | ... * ... | +| test.cpp:124:18:124:23 | call to getenv | semmle.label | call to getenv | +| test.cpp:124:18:124:31 | (const char *)... | semmle.label | (const char *)... | +| test.cpp:128:24:128:41 | ... * ... | semmle.label | ... * ... | +| test.cpp:128:24:128:41 | ... * ... | semmle.label | ... * ... | +| test.cpp:128:24:128:41 | ... * ... | semmle.label | ... * ... | +| test.cpp:133:19:133:24 | call to getenv | semmle.label | call to getenv | +| test.cpp:133:19:133:32 | (const char *)... | semmle.label | (const char *)... | +| test.cpp:135:10:135:27 | ... * ... | semmle.label | ... * ... | +| test.cpp:135:10:135:27 | ... * ... | semmle.label | ... * ... | +| test.cpp:135:10:135:27 | ... * ... | semmle.label | ... * ... | +| test.cpp:148:20:148:25 | call to getenv | semmle.label | call to getenv | +| test.cpp:148:20:148:33 | (const char *)... | semmle.label | (const char *)... | +| test.cpp:152:11:152:28 | ... * ... | semmle.label | ... * ... | +| test.cpp:152:11:152:28 | ... * ... | semmle.label | ... * ... | +| test.cpp:152:11:152:28 | ... * ... | semmle.label | ... * ... | +| test.cpp:211:9:211:42 | Store | semmle.label | Store | +| test.cpp:211:14:211:19 | call to getenv | semmle.label | call to getenv | +| test.cpp:211:14:211:27 | (const char *)... | semmle.label | (const char *)... | +| test.cpp:224:23:224:23 | s | semmle.label | s | +| test.cpp:225:21:225:21 | s | semmle.label | s | +| test.cpp:225:21:225:21 | s | semmle.label | s | +| test.cpp:225:21:225:21 | s | semmle.label | s | +| test.cpp:230:21:230:21 | s | semmle.label | s | +| test.cpp:231:21:231:21 | s | semmle.label | s | +| test.cpp:231:21:231:21 | s | semmle.label | s | +| test.cpp:231:21:231:21 | s | semmle.label | s | +| test.cpp:237:24:237:29 | call to getenv | semmle.label | call to getenv | +| test.cpp:237:24:237:37 | (const char *)... | semmle.label | (const char *)... | +| test.cpp:239:9:239:18 | (size_t)... | semmle.label | (size_t)... | +| test.cpp:239:9:239:18 | (size_t)... | semmle.label | (size_t)... | +| test.cpp:239:9:239:18 | local_size | semmle.label | local_size | +| test.cpp:239:9:239:18 | local_size | semmle.label | local_size | +| test.cpp:239:9:239:18 | local_size | semmle.label | local_size | +| test.cpp:241:9:241:24 | call to get_tainted_size | semmle.label | call to get_tainted_size | +| test.cpp:241:9:241:24 | call to get_tainted_size | semmle.label | call to get_tainted_size | +| test.cpp:241:9:241:24 | call to get_tainted_size | semmle.label | call to get_tainted_size | +| test.cpp:245:2:245:9 | local_size | semmle.label | local_size | +| test.cpp:247:2:247:8 | local_size | semmle.label | local_size | +| test.cpp:251:2:251:32 | Chi [array content] | semmle.label | Chi [array content] | +| test.cpp:251:2:251:32 | ChiPartial | semmle.label | ChiPartial | +| test.cpp:251:18:251:23 | call to getenv | semmle.label | call to getenv | +| test.cpp:251:18:251:31 | (const char *)... | semmle.label | (const char *)... | +| test.cpp:259:20:259:25 | call to getenv | semmle.label | call to getenv | +| test.cpp:259:20:259:33 | (const char *)... | semmle.label | (const char *)... | +| test.cpp:263:11:263:29 | ... * ... | semmle.label | ... * ... | +| test.cpp:263:11:263:29 | ... * ... | semmle.label | ... * ... | +| test.cpp:263:11:263:29 | ... * ... | semmle.label | ... * ... | +| test.cpp:289:17:289:20 | Chi | semmle.label | Chi | +| test.cpp:289:17:289:20 | get_size output argument [array content] | semmle.label | get_size output argument [array content] | +| test.cpp:291:11:291:28 | ... * ... | semmle.label | ... * ... | +| test.cpp:291:11:291:28 | ... * ... | semmle.label | ... * ... | +| test.cpp:291:11:291:28 | ... * ... | semmle.label | ... * ... | +| test.cpp:305:18:305:21 | Chi | semmle.label | Chi | +| test.cpp:305:18:305:21 | get_size output argument [array content] | semmle.label | get_size output argument [array content] | +| test.cpp:308:10:308:27 | ... * ... | semmle.label | ... * ... | +| test.cpp:308:10:308:27 | ... * ... | semmle.label | ... * ... | +| test.cpp:308:10:308:27 | ... * ... | semmle.label | ... * ... | #select -| test.cpp:42:31:42:36 | call to malloc | test.cpp:39:21:39:24 | argv | test.cpp:42:38:42:44 | tainted | This allocation size is derived from $@ and might overflow | test.cpp:39:21:39:24 | argv | user input (argv) | -| test.cpp:43:31:43:36 | call to malloc | test.cpp:39:21:39:24 | argv | test.cpp:43:38:43:63 | ... * ... | This allocation size is derived from $@ and might overflow | test.cpp:39:21:39:24 | argv | user input (argv) | -| test.cpp:45:31:45:36 | call to malloc | test.cpp:39:21:39:24 | argv | test.cpp:45:38:45:63 | ... + ... | This allocation size is derived from $@ and might overflow | test.cpp:39:21:39:24 | argv | user input (argv) | -| test.cpp:48:25:48:30 | call to malloc | test.cpp:39:21:39:24 | argv | test.cpp:48:32:48:35 | size | This allocation size is derived from $@ and might overflow | test.cpp:39:21:39:24 | argv | user input (argv) | -| test.cpp:49:17:49:30 | new[] | test.cpp:39:21:39:24 | argv | test.cpp:49:26:49:29 | size | This allocation size is derived from $@ and might overflow | test.cpp:39:21:39:24 | argv | user input (argv) | -| test.cpp:52:21:52:27 | call to realloc | test.cpp:39:21:39:24 | argv | test.cpp:52:35:52:60 | ... * ... | This allocation size is derived from $@ and might overflow | test.cpp:39:21:39:24 | argv | user input (argv) | -| test.cpp:127:17:127:22 | call to malloc | test.cpp:123:18:123:23 | call to getenv | test.cpp:127:24:127:41 | ... * ... | This allocation size is derived from $@ and might overflow | test.cpp:123:18:123:23 | call to getenv | user input (getenv) | -| test.cpp:134:3:134:8 | call to malloc | test.cpp:132:19:132:24 | call to getenv | test.cpp:134:10:134:27 | ... * ... | This allocation size is derived from $@ and might overflow | test.cpp:132:19:132:24 | call to getenv | user input (getenv) | -| test.cpp:215:14:215:19 | call to malloc | test.cpp:227:24:227:29 | call to getenv | test.cpp:215:21:215:21 | s | This allocation size is derived from $@ and might overflow | test.cpp:227:24:227:29 | call to getenv | user input (getenv) | -| test.cpp:221:14:221:19 | call to malloc | test.cpp:227:24:227:29 | call to getenv | test.cpp:221:21:221:21 | s | This allocation size is derived from $@ and might overflow | test.cpp:227:24:227:29 | call to getenv | user input (getenv) | -| test.cpp:229:2:229:7 | call to malloc | test.cpp:227:24:227:29 | call to getenv | test.cpp:229:9:229:18 | local_size | This allocation size is derived from $@ and might overflow | test.cpp:227:24:227:29 | call to getenv | user input (getenv) | -| test.cpp:231:2:231:7 | call to malloc | test.cpp:201:14:201:19 | call to getenv | test.cpp:231:9:231:24 | call to get_tainted_size | This allocation size is derived from $@ and might overflow | test.cpp:201:14:201:19 | call to getenv | user input (getenv) | -| test.cpp:253:4:253:9 | call to malloc | test.cpp:249:20:249:25 | call to getenv | test.cpp:253:11:253:29 | ... * ... | This allocation size is derived from $@ and might overflow | test.cpp:249:20:249:25 | call to getenv | user input (getenv) | -| test.cpp:281:4:281:9 | call to malloc | test.cpp:241:18:241:23 | call to getenv | test.cpp:281:11:281:28 | ... * ... | This allocation size is derived from $@ and might overflow | test.cpp:241:18:241:23 | call to getenv | user input (getenv) | -| test.cpp:298:3:298:8 | call to malloc | test.cpp:241:18:241:23 | call to getenv | test.cpp:298:10:298:27 | ... * ... | This allocation size is derived from $@ and might overflow | test.cpp:241:18:241:23 | call to getenv | user input (getenv) | +| test.cpp:43:31:43:36 | call to malloc | test.cpp:40:21:40:24 | argv | test.cpp:43:38:43:44 | tainted | This allocation size is derived from $@ and might overflow | test.cpp:40:21:40:24 | argv | user input (argv) | +| test.cpp:44:31:44:36 | call to malloc | test.cpp:40:21:40:24 | argv | test.cpp:44:38:44:63 | ... * ... | This allocation size is derived from $@ and might overflow | test.cpp:40:21:40:24 | argv | user input (argv) | +| test.cpp:46:31:46:36 | call to malloc | test.cpp:40:21:40:24 | argv | test.cpp:46:38:46:63 | ... + ... | This allocation size is derived from $@ and might overflow | test.cpp:40:21:40:24 | argv | user input (argv) | +| test.cpp:49:25:49:30 | call to malloc | test.cpp:40:21:40:24 | argv | test.cpp:49:32:49:35 | size | This allocation size is derived from $@ and might overflow | test.cpp:40:21:40:24 | argv | user input (argv) | +| test.cpp:50:17:50:30 | new[] | test.cpp:40:21:40:24 | argv | test.cpp:50:26:50:29 | size | This allocation size is derived from $@ and might overflow | test.cpp:40:21:40:24 | argv | user input (argv) | +| test.cpp:53:21:53:27 | call to realloc | test.cpp:40:21:40:24 | argv | test.cpp:53:35:53:60 | ... * ... | This allocation size is derived from $@ and might overflow | test.cpp:40:21:40:24 | argv | user input (argv) | +| test.cpp:128:17:128:22 | call to malloc | test.cpp:124:18:124:23 | call to getenv | test.cpp:128:24:128:41 | ... * ... | This allocation size is derived from $@ and might overflow | test.cpp:124:18:124:23 | call to getenv | user input (getenv) | +| test.cpp:135:3:135:8 | call to malloc | test.cpp:133:19:133:24 | call to getenv | test.cpp:135:10:135:27 | ... * ... | This allocation size is derived from $@ and might overflow | test.cpp:133:19:133:24 | call to getenv | user input (getenv) | +| test.cpp:152:4:152:9 | call to malloc | test.cpp:148:20:148:25 | call to getenv | test.cpp:152:11:152:28 | ... * ... | This allocation size is derived from $@ and might overflow | test.cpp:148:20:148:25 | call to getenv | user input (getenv) | +| test.cpp:225:14:225:19 | call to malloc | test.cpp:237:24:237:29 | call to getenv | test.cpp:225:21:225:21 | s | This allocation size is derived from $@ and might overflow | test.cpp:237:24:237:29 | call to getenv | user input (getenv) | +| test.cpp:231:14:231:19 | call to malloc | test.cpp:237:24:237:29 | call to getenv | test.cpp:231:21:231:21 | s | This allocation size is derived from $@ and might overflow | test.cpp:237:24:237:29 | call to getenv | user input (getenv) | +| test.cpp:239:2:239:7 | call to malloc | test.cpp:237:24:237:29 | call to getenv | test.cpp:239:9:239:18 | local_size | This allocation size is derived from $@ and might overflow | test.cpp:237:24:237:29 | call to getenv | user input (getenv) | +| test.cpp:241:2:241:7 | call to malloc | test.cpp:211:14:211:19 | call to getenv | test.cpp:241:9:241:24 | call to get_tainted_size | This allocation size is derived from $@ and might overflow | test.cpp:211:14:211:19 | call to getenv | user input (getenv) | +| test.cpp:263:4:263:9 | call to malloc | test.cpp:259:20:259:25 | call to getenv | test.cpp:263:11:263:29 | ... * ... | This allocation size is derived from $@ and might overflow | test.cpp:259:20:259:25 | call to getenv | user input (getenv) | +| test.cpp:291:4:291:9 | call to malloc | test.cpp:251:18:251:23 | call to getenv | test.cpp:291:11:291:28 | ... * ... | This allocation size is derived from $@ and might overflow | test.cpp:251:18:251:23 | call to getenv | user input (getenv) | +| test.cpp:308:3:308:8 | call to malloc | test.cpp:251:18:251:23 | call to getenv | test.cpp:308:10:308:27 | ... * ... | This allocation size is derived from $@ and might overflow | test.cpp:251:18:251:23 | call to getenv | user input (getenv) | diff --git a/cpp/ql/test/query-tests/Security/CWE/CWE-190/semmle/TaintedAllocationSize/test.cpp b/cpp/ql/test/query-tests/Security/CWE/CWE-190/semmle/TaintedAllocationSize/test.cpp index 57eb5b91a07..b11a136ed24 100644 --- a/cpp/ql/test/query-tests/Security/CWE/CWE-190/semmle/TaintedAllocationSize/test.cpp +++ b/cpp/ql/test/query-tests/Security/CWE/CWE-190/semmle/TaintedAllocationSize/test.cpp @@ -7,6 +7,7 @@ void *malloc(size_t size); void *realloc(void *ptr, size_t size); void free(void *ptr); int atoi(const char *nptr); +long atol(const char *nptr); struct MyStruct { char data[256]; @@ -143,6 +144,15 @@ void more_bounded_tests() { } } + { + long size = atol(getenv("USER")); + + if (size > 0) + { + malloc(size * sizeof(int)); // BAD + } + } + { int size = atoi(getenv("USER")); From b142ecb1dbc73e269b912719ca41f9c3684e56e5 Mon Sep 17 00:00:00 2001 From: Tom Hvitved Date: Mon, 17 May 2021 10:33:06 +0200 Subject: [PATCH 149/168] C#: Address review comment --- csharp/autobuilder/Semmle.Autobuild.CSharp/DotNetRule.cs | 6 ------ 1 file changed, 6 deletions(-) diff --git a/csharp/autobuilder/Semmle.Autobuild.CSharp/DotNetRule.cs b/csharp/autobuilder/Semmle.Autobuild.CSharp/DotNetRule.cs index 955775d0f66..3d7a1168e30 100644 --- a/csharp/autobuilder/Semmle.Autobuild.CSharp/DotNetRule.cs +++ b/csharp/autobuilder/Semmle.Autobuild.CSharp/DotNetRule.cs @@ -236,12 +236,6 @@ namespace Semmle.Autobuild.CSharp /// /// Gets the `dotnet build` script. - /// - /// The CLR tracer only works on .NET Core >= 3 on Linux and macOS (see - /// https://github.com/dotnet/coreclr/issues/19622), so in case we are - /// running on an older .NET Core, we disable shared compilation (and - /// hence the need for CLR tracing), by adding a - /// `/p:UseSharedCompilation=false` argument. /// private static BuildScript GetBuildScript(Autobuilder builder, string? dotNetPath, IDictionary? environment, string projOrSln) { From 77c93dcf26fa083cc7fffc8e6b53a8bc59c169e1 Mon Sep 17 00:00:00 2001 From: Anders Schack-Mulligen Date: Mon, 17 May 2021 10:35:04 +0200 Subject: [PATCH 150/168] Make private --- .../code/java/frameworks/jackson/JacksonSerializability.qll | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/java/ql/src/semmle/code/java/frameworks/jackson/JacksonSerializability.qll b/java/ql/src/semmle/code/java/frameworks/jackson/JacksonSerializability.qll index 50266c377f8..6cda84512a0 100644 --- a/java/ql/src/semmle/code/java/frameworks/jackson/JacksonSerializability.qll +++ b/java/ql/src/semmle/code/java/frameworks/jackson/JacksonSerializability.qll @@ -156,7 +156,7 @@ class JacksonDeserializableField extends DeserializableField { } /** A call to a field that may be deserialized using the Jackson JSON framework. */ -class JacksonDeserializableFieldAccess extends FieldAccess { +private class JacksonDeserializableFieldAccess extends FieldAccess { JacksonDeserializableFieldAccess() { getField() instanceof JacksonDeserializableField } } @@ -164,7 +164,7 @@ class JacksonDeserializableFieldAccess extends FieldAccess { * When an object is deserialized by the Jackson JSON framework using a tainted input source, * the fields that the framework deserialized are themselves tainted input data. */ -class JacksonDeserializedTaintStep extends AdditionalTaintStep { +private class JacksonDeserializedTaintStep extends AdditionalTaintStep { override predicate step(DataFlow::Node node1, DataFlow::Node node2) { DataFlow::getFieldQualifier(node2.asExpr().(JacksonDeserializableFieldAccess)) = node1 } From e652d8771c343c819d62d1901593185af2506d25 Mon Sep 17 00:00:00 2001 From: luchua-bc Date: Mon, 17 May 2021 20:34:16 +0000 Subject: [PATCH 151/168] Update method name and qldoc --- .../Security/CWE/CWE-094/JythonInjection.ql | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/java/ql/src/experimental/Security/CWE/CWE-094/JythonInjection.ql b/java/ql/src/experimental/Security/CWE/CWE-094/JythonInjection.ql index 0f23edc69f4..06b077446e2 100644 --- a/java/ql/src/experimental/Security/CWE/CWE-094/JythonInjection.ql +++ b/java/ql/src/experimental/Security/CWE/CWE-094/JythonInjection.ql @@ -52,7 +52,7 @@ class LoadClassMethod extends Method { * Holds if `ma` is a call to a class-loading method, and `sink` is the byte array * representing the class to be loaded. */ -predicate loadClass(MethodAccess ma, Expr sink) { +predicate loadsClass(MethodAccess ma, Expr sink) { exists(Method m, int i | m = ma.getMethod() | m instanceof LoadClassMethod and m.getParameter(i).getType() instanceof Array and // makeClass(java.lang.String name, byte[] data, ...) @@ -85,17 +85,21 @@ predicate compile(MethodAccess ma, Expr sink) { class CodeInjectionSink extends DataFlow::ExprNode { CodeInjectionSink() { runCode(_, this.getExpr()) or - loadClass(_, this.getExpr()) or + loadsClass(_, this.getExpr()) or compile(_, this.getExpr()) } MethodAccess getMethodAccess() { runCode(result, this.getExpr()) or - loadClass(result, this.getExpr()) or + loadsClass(result, this.getExpr()) or compile(result, this.getExpr()) } } +/** + * A taint configuration for tracking flow from `RemoteFlowSource` to a Jython method call + * `CodeInjectionSink` that executes injected code. + */ class CodeInjectionConfiguration extends TaintTracking::Configuration { CodeInjectionConfiguration() { this = "CodeInjectionConfiguration" } From a0cd551bae9ae005c59657ef3463817e226cfe29 Mon Sep 17 00:00:00 2001 From: haby0 Date: Tue, 18 May 2021 11:05:10 +0800 Subject: [PATCH 152/168] Add filtering of String.format --- .../Security/CWE/CWE-601/SpringUrlRedirect.ql | 10 +++ .../CWE/CWE-601/SpringUrlRedirect.qll | 61 +------------------ .../CWE-601/SpringUrlRedirect.expected | 8 +++ .../security/CWE-601/SpringUrlRedirect.java | 17 +++++- 4 files changed, 37 insertions(+), 59 deletions(-) diff --git a/java/ql/src/experimental/Security/CWE/CWE-601/SpringUrlRedirect.ql b/java/ql/src/experimental/Security/CWE/CWE-601/SpringUrlRedirect.ql index 138bce57ac9..5c8a8ea78d8 100644 --- a/java/ql/src/experimental/Security/CWE/CWE-601/SpringUrlRedirect.ql +++ b/java/ql/src/experimental/Security/CWE/CWE-601/SpringUrlRedirect.ql @@ -33,6 +33,16 @@ class SpringUrlRedirectFlowConfig extends TaintTracking::Configuration { ae.getRightOperand() = node.asExpr() and not ae instanceof RedirectBuilderExpr ) + or + exists(MethodAccess ma, int index | + ma.getMethod().hasName("format") and + ma.getMethod().getDeclaringType() instanceof TypeString and + ma.getArgument(index) = node.asExpr() and + ( + index != 0 and + not ma.getArgument(0).(CompileTimeConstantExpr).getStringValue().regexpMatch("^%s.*") + ) + ) } } diff --git a/java/ql/src/experimental/Security/CWE/CWE-601/SpringUrlRedirect.qll b/java/ql/src/experimental/Security/CWE/CWE-601/SpringUrlRedirect.qll index 866eaae1c34..84bb5d9adf8 100644 --- a/java/ql/src/experimental/Security/CWE/CWE-601/SpringUrlRedirect.qll +++ b/java/ql/src/experimental/Security/CWE/CWE-601/SpringUrlRedirect.qll @@ -51,14 +51,14 @@ class SpringUrlRedirectSink extends DataFlow::Node { SpringUrlRedirectSink() { exists(RedirectBuilderExpr rbe | rbe.getRightOperand() = this.asExpr() and - exists(RedirectBuilderFlowConfig rbfc | rbfc.hasFlow(exprNode(rbe), _)) + any(SpringRequestMappingMethod sqmm).polyCalls*(this.getEnclosingCallable()) ) or exists(MethodAccess ma, RedirectAppendCall rac | DataFlow2::localExprFlow(rac.getQualifier(), ma.getQualifier()) and ma.getMethod().hasName("append") and ma.getArgument(0) = this.asExpr() and - exists(RedirectBuilderFlowConfig rbfc | rbfc.hasFlow(exprNode(ma.getQualifier()), _)) + any(SpringRequestMappingMethod sqmm).polyCalls*(this.getEnclosingCallable()) ) or exists(MethodAccess ma | @@ -66,8 +66,7 @@ class SpringUrlRedirectSink extends DataFlow::Node { ma.getMethod() .getDeclaringType() .hasQualifiedName("org.springframework.web.servlet.view", "AbstractUrlBasedView") and - ma.getArgument(0) = this.asExpr() and - exists(RedirectViewFlowConfig rvfc | rvfc.hasFlowToExpr(ma.getQualifier())) + ma.getArgument(0) = this.asExpr() ) or exists(ClassInstanceExpr cie | @@ -84,57 +83,3 @@ class SpringUrlRedirectSink extends DataFlow::Node { ) } } - -/** A data flow configuration tracing flow from redirect builder expression to spring controller method return expression. */ -private class RedirectBuilderFlowConfig extends DataFlow2::Configuration { - RedirectBuilderFlowConfig() { this = "RedirectBuilderFlowConfig" } - - override predicate isSource(DataFlow::Node src) { - exists(RedirectBuilderExpr rbe | rbe = src.asExpr()) - or - exists(MethodAccess ma, RedirectAppendCall rac | - DataFlow2::localExprFlow(rac.getQualifier(), ma.getQualifier()) and - ma.getMethod().hasName("append") and - ma.getQualifier() = src.asExpr() - ) - } - - override predicate isSink(DataFlow::Node sink) { - exists(ReturnStmt rs, SpringRequestMappingMethod sqmm | - rs.getResult() = sink.asExpr() and - sqmm.getBody().getAStmt() = rs - ) - } - - override predicate isAdditionalFlowStep(Node prod, Node succ) { - exists(MethodAccess ma | - ma.getMethod().hasName("toString") and - ma.getMethod().getDeclaringType() instanceof StringBuildingType and - ma.getQualifier() = prod.asExpr() and - ma = succ.asExpr() - ) - } -} - -/** A data flow configuration tracing flow from RedirectView object to calling setUrl method. */ -private class RedirectViewFlowConfig extends DataFlow2::Configuration { - RedirectViewFlowConfig() { this = "RedirectViewFlowConfig" } - - override predicate isSource(DataFlow::Node src) { - exists(ClassInstanceExpr cie | - cie.getConstructedType() - .hasQualifiedName("org.springframework.web.servlet.view", "RedirectView") and - cie = src.asExpr() - ) - } - - override predicate isSink(DataFlow::Node sink) { - exists(MethodAccess ma | - ma.getMethod().hasName("setUrl") and - ma.getMethod() - .getDeclaringType() - .hasQualifiedName("org.springframework.web.servlet.view", "AbstractUrlBasedView") and - ma.getQualifier() = sink.asExpr() - ) - } -} diff --git a/java/ql/test/experimental/query-tests/security/CWE-601/SpringUrlRedirect.expected b/java/ql/test/experimental/query-tests/security/CWE-601/SpringUrlRedirect.expected index 26b8acd7770..bcf5e892e1b 100644 --- a/java/ql/test/experimental/query-tests/security/CWE-601/SpringUrlRedirect.expected +++ b/java/ql/test/experimental/query-tests/security/CWE-601/SpringUrlRedirect.expected @@ -5,6 +5,8 @@ edges | SpringUrlRedirect.java:32:30:32:47 | redirectUrl : String | SpringUrlRedirect.java:33:47:33:57 | redirectUrl | | SpringUrlRedirect.java:37:24:37:41 | redirectUrl : String | SpringUrlRedirect.java:40:29:40:39 | redirectUrl | | SpringUrlRedirect.java:45:24:45:41 | redirectUrl : String | SpringUrlRedirect.java:48:30:48:40 | redirectUrl | +| SpringUrlRedirect.java:53:24:53:41 | redirectUrl : String | SpringUrlRedirect.java:54:30:54:66 | format(...) | +| SpringUrlRedirect.java:58:24:58:41 | redirectUrl : String | SpringUrlRedirect.java:59:30:59:76 | format(...) | nodes | SpringUrlRedirect.java:13:30:13:47 | redirectUrl : String | semmle.label | redirectUrl : String | | SpringUrlRedirect.java:15:19:15:29 | redirectUrl | semmle.label | redirectUrl | @@ -18,6 +20,10 @@ nodes | SpringUrlRedirect.java:40:29:40:39 | redirectUrl | semmle.label | redirectUrl | | SpringUrlRedirect.java:45:24:45:41 | redirectUrl : String | semmle.label | redirectUrl : String | | SpringUrlRedirect.java:48:30:48:40 | redirectUrl | semmle.label | redirectUrl | +| SpringUrlRedirect.java:53:24:53:41 | redirectUrl : String | semmle.label | redirectUrl : String | +| SpringUrlRedirect.java:54:30:54:66 | format(...) | semmle.label | format(...) | +| SpringUrlRedirect.java:58:24:58:41 | redirectUrl : String | semmle.label | redirectUrl : String | +| SpringUrlRedirect.java:59:30:59:76 | format(...) | semmle.label | format(...) | #select | SpringUrlRedirect.java:15:19:15:29 | redirectUrl | SpringUrlRedirect.java:13:30:13:47 | redirectUrl : String | SpringUrlRedirect.java:15:19:15:29 | redirectUrl | Potentially untrusted URL redirection due to $@. | SpringUrlRedirect.java:13:30:13:47 | redirectUrl | user-provided value | | SpringUrlRedirect.java:21:36:21:46 | redirectUrl | SpringUrlRedirect.java:20:24:20:41 | redirectUrl : String | SpringUrlRedirect.java:21:36:21:46 | redirectUrl | Potentially untrusted URL redirection due to $@. | SpringUrlRedirect.java:20:24:20:41 | redirectUrl | user-provided value | @@ -25,3 +31,5 @@ nodes | SpringUrlRedirect.java:33:47:33:57 | redirectUrl | SpringUrlRedirect.java:32:30:32:47 | redirectUrl : String | SpringUrlRedirect.java:33:47:33:57 | redirectUrl | Potentially untrusted URL redirection due to $@. | SpringUrlRedirect.java:32:30:32:47 | redirectUrl | user-provided value | | SpringUrlRedirect.java:40:29:40:39 | redirectUrl | SpringUrlRedirect.java:37:24:37:41 | redirectUrl : String | SpringUrlRedirect.java:40:29:40:39 | redirectUrl | Potentially untrusted URL redirection due to $@. | SpringUrlRedirect.java:37:24:37:41 | redirectUrl | user-provided value | | SpringUrlRedirect.java:48:30:48:40 | redirectUrl | SpringUrlRedirect.java:45:24:45:41 | redirectUrl : String | SpringUrlRedirect.java:48:30:48:40 | redirectUrl | Potentially untrusted URL redirection due to $@. | SpringUrlRedirect.java:45:24:45:41 | redirectUrl | user-provided value | +| SpringUrlRedirect.java:54:30:54:66 | format(...) | SpringUrlRedirect.java:53:24:53:41 | redirectUrl : String | SpringUrlRedirect.java:54:30:54:66 | format(...) | Potentially untrusted URL redirection due to $@. | SpringUrlRedirect.java:53:24:53:41 | redirectUrl | user-provided value | +| SpringUrlRedirect.java:59:30:59:76 | format(...) | SpringUrlRedirect.java:58:24:58:41 | redirectUrl : String | SpringUrlRedirect.java:59:30:59:76 | format(...) | Potentially untrusted URL redirection due to $@. | SpringUrlRedirect.java:58:24:58:41 | redirectUrl | user-provided value | diff --git a/java/ql/test/experimental/query-tests/security/CWE-601/SpringUrlRedirect.java b/java/ql/test/experimental/query-tests/security/CWE-601/SpringUrlRedirect.java index 5124d8cd8c6..f3958cba102 100644 --- a/java/ql/test/experimental/query-tests/security/CWE-601/SpringUrlRedirect.java +++ b/java/ql/test/experimental/query-tests/security/CWE-601/SpringUrlRedirect.java @@ -50,6 +50,16 @@ public class SpringUrlRedirect { } @GetMapping("url7") + public String bad7(String redirectUrl) { + return "redirect:" + String.format("%s/?aaa", redirectUrl); + } + + @GetMapping("url8") + public String bad8(String redirectUrl, String token) { + return "redirect:" + String.format(redirectUrl + "?token=%s", token); + } + + @GetMapping("url9") public RedirectView good1(String redirectUrl) { RedirectView rv = new RedirectView(); if (redirectUrl.startsWith(VALID_REDIRECT)){ @@ -60,9 +70,14 @@ public class SpringUrlRedirect { return rv; } - @GetMapping("url8") + @GetMapping("url10") public ModelAndView good2(String token) { String url = "/edit?token=" + token; return new ModelAndView("redirect:" + url); } + + @GetMapping("url11") + public String good3(String status) { + return "redirect:" + String.format("/stories/search/criteria?status=%s", status); + } } From 1435ac715ac6d7f3f5255c7069e305620cc6afd2 Mon Sep 17 00:00:00 2001 From: Erik Krogh Kristensen Date: Tue, 18 May 2021 12:46:34 +0200 Subject: [PATCH 153/168] add support for the clone library --- javascript/change-notes/2021-05-18-clone.md | 4 ++++ javascript/ql/src/javascript.qll | 1 + .../src/semmle/javascript/frameworks/Clone.qll | 15 +++++++++++++++ .../CWE-079/ReflectedXss/ReflectedXss.expected | 17 +++++++++++++++++ .../ReflectedXssWithCustomSanitizer.expected | 2 ++ .../Security/CWE-079/ReflectedXss/tst2.js | 14 ++++++++++++++ 6 files changed, 53 insertions(+) create mode 100644 javascript/change-notes/2021-05-18-clone.md create mode 100644 javascript/ql/src/semmle/javascript/frameworks/Clone.qll diff --git a/javascript/change-notes/2021-05-18-clone.md b/javascript/change-notes/2021-05-18-clone.md new file mode 100644 index 00000000000..42d52d72a3d --- /dev/null +++ b/javascript/change-notes/2021-05-18-clone.md @@ -0,0 +1,4 @@ +lgtm,codescanning +* The dataflow libraries now model dataflow in the `clone` library. + Affected packages are + [clone](https://npmjs.com/package/clone) diff --git a/javascript/ql/src/javascript.qll b/javascript/ql/src/javascript.qll index 95db948bbd4..a46313f47f0 100644 --- a/javascript/ql/src/javascript.qll +++ b/javascript/ql/src/javascript.qll @@ -78,6 +78,7 @@ import semmle.javascript.frameworks.ComposedFunctions import semmle.javascript.frameworks.Classnames import semmle.javascript.frameworks.ClassValidator import semmle.javascript.frameworks.ClientRequests +import semmle.javascript.frameworks.Clone import semmle.javascript.frameworks.ClosureLibrary import semmle.javascript.frameworks.CookieLibraries import semmle.javascript.frameworks.Credentials diff --git a/javascript/ql/src/semmle/javascript/frameworks/Clone.qll b/javascript/ql/src/semmle/javascript/frameworks/Clone.qll new file mode 100644 index 00000000000..5aabee438c5 --- /dev/null +++ b/javascript/ql/src/semmle/javascript/frameworks/Clone.qll @@ -0,0 +1,15 @@ +/** + * Provides a dataflow-step for the `clone` package. + */ + +import javascript +private import semmle.javascript.dataflow.internal.PreCallGraphStep + +private class CloneStep extends PreCallGraphStep { + override predicate step(DataFlow::Node pred, DataFlow::Node succ) { + exists(DataFlow::CallNode call | call = DataFlow::moduleImport("clone").getACall() | + pred = call.getArgument(0) and + succ = call + ) + } +} diff --git a/javascript/ql/test/query-tests/Security/CWE-079/ReflectedXss/ReflectedXss.expected b/javascript/ql/test/query-tests/Security/CWE-079/ReflectedXss/ReflectedXss.expected index f8ceeb45b62..e8d093325ad 100644 --- a/javascript/ql/test/query-tests/Security/CWE-079/ReflectedXss/ReflectedXss.expected +++ b/javascript/ql/test/query-tests/Security/CWE-079/ReflectedXss/ReflectedXss.expected @@ -174,6 +174,14 @@ nodes | tst2.js:18:12:18:12 | p | | tst2.js:21:14:21:14 | p | | tst2.js:21:14:21:14 | p | +| tst2.js:30:7:30:24 | p | +| tst2.js:30:9:30:9 | p | +| tst2.js:30:9:30:9 | p | +| tst2.js:33:11:33:11 | p | +| tst2.js:36:12:36:12 | p | +| tst2.js:36:12:36:12 | p | +| tst2.js:37:12:37:18 | other.p | +| tst2.js:37:12:37:18 | other.p | edges | ReflectedXss.js:8:33:8:45 | req.params.id | ReflectedXss.js:8:14:8:45 | "Unknow ... rams.id | | ReflectedXss.js:8:33:8:45 | req.params.id | ReflectedXss.js:8:14:8:45 | "Unknow ... rams.id | @@ -318,6 +326,13 @@ edges | tst2.js:14:7:14:24 | p | tst2.js:21:14:21:14 | p | | tst2.js:14:9:14:9 | p | tst2.js:14:7:14:24 | p | | tst2.js:14:9:14:9 | p | tst2.js:14:7:14:24 | p | +| tst2.js:30:7:30:24 | p | tst2.js:33:11:33:11 | p | +| tst2.js:30:7:30:24 | p | tst2.js:36:12:36:12 | p | +| tst2.js:30:7:30:24 | p | tst2.js:36:12:36:12 | p | +| tst2.js:30:9:30:9 | p | tst2.js:30:7:30:24 | p | +| tst2.js:30:9:30:9 | p | tst2.js:30:7:30:24 | p | +| tst2.js:33:11:33:11 | p | tst2.js:37:12:37:18 | other.p | +| tst2.js:33:11:33:11 | p | tst2.js:37:12:37:18 | other.p | #select | ReflectedXss.js:8:14:8:45 | "Unknow ... rams.id | ReflectedXss.js:8:33:8:45 | req.params.id | ReflectedXss.js:8:14:8:45 | "Unknow ... rams.id | Cross-site scripting vulnerability due to $@. | ReflectedXss.js:8:33:8:45 | req.params.id | user-provided value | | ReflectedXss.js:17:12:17:39 | "Unknow ... rams.id | ReflectedXss.js:17:31:17:39 | params.id | ReflectedXss.js:17:12:17:39 | "Unknow ... rams.id | Cross-site scripting vulnerability due to $@. | ReflectedXss.js:17:31:17:39 | params.id | user-provided value | @@ -359,3 +374,5 @@ edges | tst2.js:8:12:8:12 | r | tst2.js:6:12:6:15 | q: r | tst2.js:8:12:8:12 | r | Cross-site scripting vulnerability due to $@. | tst2.js:6:12:6:15 | q: r | user-provided value | | tst2.js:18:12:18:12 | p | tst2.js:14:9:14:9 | p | tst2.js:18:12:18:12 | p | Cross-site scripting vulnerability due to $@. | tst2.js:14:9:14:9 | p | user-provided value | | tst2.js:21:14:21:14 | p | tst2.js:14:9:14:9 | p | tst2.js:21:14:21:14 | p | Cross-site scripting vulnerability due to $@. | tst2.js:14:9:14:9 | p | user-provided value | +| tst2.js:36:12:36:12 | p | tst2.js:30:9:30:9 | p | tst2.js:36:12:36:12 | p | Cross-site scripting vulnerability due to $@. | tst2.js:30:9:30:9 | p | user-provided value | +| tst2.js:37:12:37:18 | other.p | tst2.js:30:9:30:9 | p | tst2.js:37:12:37:18 | other.p | Cross-site scripting vulnerability due to $@. | tst2.js:30:9:30:9 | p | user-provided value | diff --git a/javascript/ql/test/query-tests/Security/CWE-079/ReflectedXss/ReflectedXssWithCustomSanitizer.expected b/javascript/ql/test/query-tests/Security/CWE-079/ReflectedXss/ReflectedXssWithCustomSanitizer.expected index 719a82171a8..8ddc55dde36 100644 --- a/javascript/ql/test/query-tests/Security/CWE-079/ReflectedXss/ReflectedXssWithCustomSanitizer.expected +++ b/javascript/ql/test/query-tests/Security/CWE-079/ReflectedXss/ReflectedXssWithCustomSanitizer.expected @@ -37,3 +37,5 @@ | tst2.js:8:12:8:12 | r | Cross-site scripting vulnerability due to $@. | tst2.js:6:12:6:15 | q: r | user-provided value | | tst2.js:18:12:18:12 | p | Cross-site scripting vulnerability due to $@. | tst2.js:14:9:14:9 | p | user-provided value | | tst2.js:21:14:21:14 | p | Cross-site scripting vulnerability due to $@. | tst2.js:14:9:14:9 | p | user-provided value | +| tst2.js:36:12:36:12 | p | Cross-site scripting vulnerability due to $@. | tst2.js:30:9:30:9 | p | user-provided value | +| tst2.js:37:12:37:18 | other.p | Cross-site scripting vulnerability due to $@. | tst2.js:30:9:30:9 | p | user-provided value | diff --git a/javascript/ql/test/query-tests/Security/CWE-079/ReflectedXss/tst2.js b/javascript/ql/test/query-tests/Security/CWE-079/ReflectedXss/tst2.js index 521b6b20a7c..034b0791217 100644 --- a/javascript/ql/test/query-tests/Security/CWE-079/ReflectedXss/tst2.js +++ b/javascript/ql/test/query-tests/Security/CWE-079/ReflectedXss/tst2.js @@ -22,3 +22,17 @@ app.get('/bar', function(req, res) { else res.send(p); // OK }); + + +const clone = require('clone'); + +app.get('/baz', function(req, res) { + let { p } = req.params; + + var obj = {}; + obj.p = p; + var other = clone(obj); + + res.send(p); // NOT OK + res.send(other.p); // NOT OK +}); \ No newline at end of file From caf5f4d605bbdcb67fa65de5b65f9288a39d8ef4 Mon Sep 17 00:00:00 2001 From: haby0 Date: Tue, 18 May 2021 19:10:03 +0800 Subject: [PATCH 154/168] modified comment --- .../src/experimental/Security/CWE/CWE-601/SpringUrlRedirect.ql | 2 ++ .../src/experimental/Security/CWE/CWE-601/SpringUrlRedirect.qll | 2 +- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/java/ql/src/experimental/Security/CWE/CWE-601/SpringUrlRedirect.ql b/java/ql/src/experimental/Security/CWE/CWE-601/SpringUrlRedirect.ql index 5c8a8ea78d8..210067fde2a 100644 --- a/java/ql/src/experimental/Security/CWE/CWE-601/SpringUrlRedirect.ql +++ b/java/ql/src/experimental/Security/CWE/CWE-601/SpringUrlRedirect.ql @@ -29,6 +29,8 @@ class SpringUrlRedirectFlowConfig extends TaintTracking::Configuration { override predicate isSanitizer(DataFlow::Node node) { // Exclude the case where the left side of the concatenated string is not `redirect:`. // E.g: `String url = "/path?token=" + request.getParameter("token");` + // Note this is quite a broad sanitizer (it will also sanitize the right-hand side of `url = "http://" + request.getParameter("token")`); + // Consider making this stricter in future. exists(AddExpr ae | ae.getRightOperand() = node.asExpr() and not ae instanceof RedirectBuilderExpr diff --git a/java/ql/src/experimental/Security/CWE/CWE-601/SpringUrlRedirect.qll b/java/ql/src/experimental/Security/CWE/CWE-601/SpringUrlRedirect.qll index 84bb5d9adf8..6ae87a37bcc 100644 --- a/java/ql/src/experimental/Security/CWE/CWE-601/SpringUrlRedirect.qll +++ b/java/ql/src/experimental/Security/CWE/CWE-601/SpringUrlRedirect.qll @@ -5,7 +5,7 @@ import semmle.code.java.dataflow.DataFlow2 import semmle.code.java.dataflow.TaintTracking import semmle.code.java.frameworks.spring.SpringController -class StartsWithSanitizer extends DataFlow::BarrierGuard { +private class StartsWithSanitizer extends DataFlow::BarrierGuard { StartsWithSanitizer() { this.(MethodAccess).getMethod().hasName("startsWith") and this.(MethodAccess).getMethod().getDeclaringType() instanceof TypeString and From 06514a2bb6c3577269c312910e1d3c15764a6347 Mon Sep 17 00:00:00 2001 From: Erik Krogh Kristensen Date: Tue, 18 May 2021 13:16:41 +0200 Subject: [PATCH 155/168] move clone model to Extend.qll --- javascript/ql/src/javascript.qll | 1 - javascript/ql/src/semmle/javascript/Extend.qll | 14 ++++++++++++++ .../ql/src/semmle/javascript/frameworks/Clone.qll | 15 --------------- 3 files changed, 14 insertions(+), 16 deletions(-) delete mode 100644 javascript/ql/src/semmle/javascript/frameworks/Clone.qll diff --git a/javascript/ql/src/javascript.qll b/javascript/ql/src/javascript.qll index a46313f47f0..95db948bbd4 100644 --- a/javascript/ql/src/javascript.qll +++ b/javascript/ql/src/javascript.qll @@ -78,7 +78,6 @@ import semmle.javascript.frameworks.ComposedFunctions import semmle.javascript.frameworks.Classnames import semmle.javascript.frameworks.ClassValidator import semmle.javascript.frameworks.ClientRequests -import semmle.javascript.frameworks.Clone import semmle.javascript.frameworks.ClosureLibrary import semmle.javascript.frameworks.CookieLibraries import semmle.javascript.frameworks.Credentials diff --git a/javascript/ql/src/semmle/javascript/Extend.qll b/javascript/ql/src/semmle/javascript/Extend.qll index 9b7255983b9..d50054fc4ee 100644 --- a/javascript/ql/src/semmle/javascript/Extend.qll +++ b/javascript/ql/src/semmle/javascript/Extend.qll @@ -174,3 +174,17 @@ private class ExtendCallTaintStep extends TaintTracking::SharedTaintStep { ) } } + +private import semmle.javascript.dataflow.internal.PreCallGraphStep + +/** + * A step for the `clone` package. + */ +private class CloneStep extends PreCallGraphStep { + override predicate step(DataFlow::Node pred, DataFlow::Node succ) { + exists(DataFlow::CallNode call | call = DataFlow::moduleImport("clone").getACall() | + pred = call.getArgument(0) and + succ = call + ) + } +} diff --git a/javascript/ql/src/semmle/javascript/frameworks/Clone.qll b/javascript/ql/src/semmle/javascript/frameworks/Clone.qll deleted file mode 100644 index 5aabee438c5..00000000000 --- a/javascript/ql/src/semmle/javascript/frameworks/Clone.qll +++ /dev/null @@ -1,15 +0,0 @@ -/** - * Provides a dataflow-step for the `clone` package. - */ - -import javascript -private import semmle.javascript.dataflow.internal.PreCallGraphStep - -private class CloneStep extends PreCallGraphStep { - override predicate step(DataFlow::Node pred, DataFlow::Node succ) { - exists(DataFlow::CallNode call | call = DataFlow::moduleImport("clone").getACall() | - pred = call.getArgument(0) and - succ = call - ) - } -} From e46de44473791493bd73068dabced3be868da819 Mon Sep 17 00:00:00 2001 From: haby0 Date: Tue, 18 May 2021 19:56:32 +0800 Subject: [PATCH 156/168] Solve errors caused by private ownership --- .../Security/CWE/CWE-601/SpringUrlRedirect.ql | 12 ++++++++++++ .../Security/CWE/CWE-601/SpringUrlRedirect.qll | 12 ------------ 2 files changed, 12 insertions(+), 12 deletions(-) diff --git a/java/ql/src/experimental/Security/CWE/CWE-601/SpringUrlRedirect.ql b/java/ql/src/experimental/Security/CWE/CWE-601/SpringUrlRedirect.ql index 210067fde2a..b02bd3e4c30 100644 --- a/java/ql/src/experimental/Security/CWE/CWE-601/SpringUrlRedirect.ql +++ b/java/ql/src/experimental/Security/CWE/CWE-601/SpringUrlRedirect.ql @@ -15,6 +15,18 @@ import SpringUrlRedirect import semmle.code.java.dataflow.FlowSources import DataFlow::PathGraph +private class StartsWithSanitizer extends DataFlow::BarrierGuard { + StartsWithSanitizer() { + this.(MethodAccess).getMethod().hasName("startsWith") and + this.(MethodAccess).getMethod().getDeclaringType() instanceof TypeString and + this.(MethodAccess).getMethod().getNumberOfParameters() = 1 + } + + override predicate checks(Expr e, boolean branch) { + e = this.(MethodAccess).getQualifier() and branch = true + } +} + class SpringUrlRedirectFlowConfig extends TaintTracking::Configuration { SpringUrlRedirectFlowConfig() { this = "SpringUrlRedirectFlowConfig" } diff --git a/java/ql/src/experimental/Security/CWE/CWE-601/SpringUrlRedirect.qll b/java/ql/src/experimental/Security/CWE/CWE-601/SpringUrlRedirect.qll index 6ae87a37bcc..4a86640d4d4 100644 --- a/java/ql/src/experimental/Security/CWE/CWE-601/SpringUrlRedirect.qll +++ b/java/ql/src/experimental/Security/CWE/CWE-601/SpringUrlRedirect.qll @@ -5,18 +5,6 @@ import semmle.code.java.dataflow.DataFlow2 import semmle.code.java.dataflow.TaintTracking import semmle.code.java.frameworks.spring.SpringController -private class StartsWithSanitizer extends DataFlow::BarrierGuard { - StartsWithSanitizer() { - this.(MethodAccess).getMethod().hasName("startsWith") and - this.(MethodAccess).getMethod().getDeclaringType() instanceof TypeString and - this.(MethodAccess).getMethod().getNumberOfParameters() = 1 - } - - override predicate checks(Expr e, boolean branch) { - e = this.(MethodAccess).getQualifier() and branch = true - } -} - /** * A concatenate expression using the string `redirect:` or `ajaxredirect:` or `forward:` on the left. * From 2a0721b2aeae25d99e1c5805311b8b6e46a735af Mon Sep 17 00:00:00 2001 From: luchua-bc Date: Tue, 18 May 2021 12:18:14 +0000 Subject: [PATCH 157/168] Optimize the sink and update method name --- .../Security/CWE/CWE-094/JythonInjection.ql | 16 +++++++--------- 1 file changed, 7 insertions(+), 9 deletions(-) diff --git a/java/ql/src/experimental/Security/CWE/CWE-094/JythonInjection.ql b/java/ql/src/experimental/Security/CWE/CWE-094/JythonInjection.ql index 06b077446e2..c44eee23602 100644 --- a/java/ql/src/experimental/Security/CWE/CWE-094/JythonInjection.ql +++ b/java/ql/src/experimental/Security/CWE/CWE-094/JythonInjection.ql @@ -33,7 +33,7 @@ class BytecodeLoader extends RefType { } /** Holds if a Jython expression if evaluated, compiled or executed. */ -predicate runCode(MethodAccess ma, Expr sink) { +predicate runsCode(MethodAccess ma, Expr sink) { exists(Method m | m = ma.getMethod() | m instanceof InterpretExprMethod and sink = ma.getArgument(0) @@ -83,17 +83,15 @@ predicate compile(MethodAccess ma, Expr sink) { /** An expression loaded by Jython. */ class CodeInjectionSink extends DataFlow::ExprNode { + MethodAccess methodAccess; + CodeInjectionSink() { - runCode(_, this.getExpr()) or - loadsClass(_, this.getExpr()) or - compile(_, this.getExpr()) + runsCode(methodAccess, this.getExpr()) or + loadsClass(methodAccess, this.getExpr()) or + compile(methodAccess, this.getExpr()) } - MethodAccess getMethodAccess() { - runCode(result, this.getExpr()) or - loadsClass(result, this.getExpr()) or - compile(result, this.getExpr()) - } + MethodAccess getMethodAccess() { result = methodAccess } } /** From 610e041e28a3c1d417e1e1c56fbccc7a2cb60896 Mon Sep 17 00:00:00 2001 From: Ethan Palm <56270045+ethanpalm@users.noreply.github.com> Date: Tue, 18 May 2021 11:42:08 -0400 Subject: [PATCH 158/168] Add reviewer feedback Co-authored-by: mc <42146119+mchammer01@users.noreply.github.com> --- docs/codeql/codeql-cli/creating-codeql-databases.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/codeql/codeql-cli/creating-codeql-databases.rst b/docs/codeql/codeql-cli/creating-codeql-databases.rst index 1552a077e24..637df58555b 100644 --- a/docs/codeql/codeql-cli/creating-codeql-databases.rst +++ b/docs/codeql/codeql-cli/creating-codeql-databases.rst @@ -169,7 +169,7 @@ build steps, you may need to explicitly define each step in the command line. are dependencies, the appropriate dependency manager (such as `dep `__). - The Go autobuilder attempts to automatically detect Go code in a repository, + The Go autobuilder attempts to automatically detect code written in Go in a repository, and only runs build scripts in an attempt to fetch dependencies. To force CodeQL to limit extraction to the files compiled by your build script, set the environment variable `CODEQL_EXTRACTOR_GO_BUILD_TRACING=on` or use the ``--command`` option to specify a From b0b5338359ce7da9525f705d77784545f6090c8e Mon Sep 17 00:00:00 2001 From: luchua-bc Date: Thu, 29 Apr 2021 02:59:41 +0000 Subject: [PATCH 159/168] Rhino code injection --- .../Security/CWE/CWE-094/RhinoInjection.java | 39 ++ .../Security/CWE/CWE-094/RhinoInjection.qhelp | 51 ++ .../Security/CWE/CWE-094/RhinoInjection.ql | 17 + .../Security/CWE/CWE-094/RhinoInjection.qll | 56 ++ .../security/CWE-094/RhinoInjection.expected | 7 + .../security/CWE-094/RhinoInjection.qlref | 1 + .../security/CWE-094/RhinoServlet.java | 78 +++ .../query-tests/security/CWE-094/options | 2 +- .../org/mozilla/javascript/ClassShutter.java | 56 ++ .../org/mozilla/javascript/Context.java | 623 ++++++++++++++++++ .../mozilla/javascript/ContextFactory.java | 314 +++++++++ .../mozilla/javascript/RhinoException.java | 15 + .../org/mozilla/javascript/Scriptable.java | 304 +++++++++ .../mozilla/javascript/ScriptableObject.java | 27 + 14 files changed, 1589 insertions(+), 1 deletion(-) create mode 100644 java/ql/src/experimental/Security/CWE/CWE-094/RhinoInjection.java create mode 100644 java/ql/src/experimental/Security/CWE/CWE-094/RhinoInjection.qhelp create mode 100644 java/ql/src/experimental/Security/CWE/CWE-094/RhinoInjection.ql create mode 100644 java/ql/src/experimental/Security/CWE/CWE-094/RhinoInjection.qll create mode 100644 java/ql/test/experimental/query-tests/security/CWE-094/RhinoInjection.expected create mode 100644 java/ql/test/experimental/query-tests/security/CWE-094/RhinoInjection.qlref create mode 100644 java/ql/test/experimental/query-tests/security/CWE-094/RhinoServlet.java create mode 100644 java/ql/test/experimental/stubs/rhino-1.7.13/org/mozilla/javascript/ClassShutter.java create mode 100644 java/ql/test/experimental/stubs/rhino-1.7.13/org/mozilla/javascript/Context.java create mode 100644 java/ql/test/experimental/stubs/rhino-1.7.13/org/mozilla/javascript/ContextFactory.java create mode 100644 java/ql/test/experimental/stubs/rhino-1.7.13/org/mozilla/javascript/RhinoException.java create mode 100644 java/ql/test/experimental/stubs/rhino-1.7.13/org/mozilla/javascript/Scriptable.java create mode 100644 java/ql/test/experimental/stubs/rhino-1.7.13/org/mozilla/javascript/ScriptableObject.java diff --git a/java/ql/src/experimental/Security/CWE/CWE-094/RhinoInjection.java b/java/ql/src/experimental/Security/CWE/CWE-094/RhinoInjection.java new file mode 100644 index 00000000000..4a87d5bc354 --- /dev/null +++ b/java/ql/src/experimental/Security/CWE/CWE-094/RhinoInjection.java @@ -0,0 +1,39 @@ +public class RhinoInjection extends HttpServlet { + + protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { + response.setContentType("text/plain"); + String code = request.getParameter("code"); + Context ctx = Context.enter(); + try { + { + // BAD: allow arbitrary Java and JavaScript code to be executed + Scriptable scope = ctx.initStandardObjects(); + } + + { + // GOOD: enable the safe mode + Scriptable scope = ctx.initSafeStandardObjects(); + } + + { + // GOOD: enforce a constraint on allowed classes + Scriptable scope = ctx.initStandardObjects(); + ctx.setClassShutter(new ClassShutter() { + public boolean visibleToScripts(String className) { + if(className.startsWith("com.example.")) { + return true; + } + return false; + } + }); + } + + Object result = ctx.evaluateString(scope, code, "", 1, null); + response.getWriter().print(Context.toString(result)); + } catch(RhinoException ex) { + response.getWriter().println(ex.getMessage()); + } finally { + Context.exit(); + } + } +} diff --git a/java/ql/src/experimental/Security/CWE/CWE-094/RhinoInjection.qhelp b/java/ql/src/experimental/Security/CWE/CWE-094/RhinoInjection.qhelp new file mode 100644 index 00000000000..73c252ed54c --- /dev/null +++ b/java/ql/src/experimental/Security/CWE/CWE-094/RhinoInjection.qhelp @@ -0,0 +1,51 @@ + + + + +

    +Rhino is a JavaScript engine written fully in Java and managed by the Mozilla Foundation. +It serves as an embedded scripting engine inside Java applications which allows +Java-to-JavaScript interoperability and provides a seamless integration between the two +languages. If an expression is built using attacker-controlled data, and then evaluated in +a powerful context, it may allow the attacker to run arbitrary code. +

    +

    +Typically an expression is evaluated in the powerful context initialized with +initStandardObjects that allows an expression of arbitrary Java code to +execute in the JVM. +

    +
    + + +

    +In general, including user input in a Rhino expression should be avoided. +If user input must be included in the expression, it should be then evaluated in a safe +context that doesn't allow arbitrary code invocation. +

    +
    + + +

    +The following example shows two ways of using Rhino expression. In the 'BAD' case, +an unsafe context is initialized with initStandardObjects. In the 'GOOD' case, +a safe context is initialized with initSafeStandardObjects or +setClassShutter. +

    + +
    + + +
  • + Mozilla Rhino: + Rhino: JavaScript in Java +
  • +
  • + Rhino Sandbox: + A sandbox to execute JavaScript code with Rhino in Java. +
  • +
  • + GuardRails: + Code Injection +
  • +
    +
    \ No newline at end of file diff --git a/java/ql/src/experimental/Security/CWE/CWE-094/RhinoInjection.ql b/java/ql/src/experimental/Security/CWE/CWE-094/RhinoInjection.ql new file mode 100644 index 00000000000..cac41244aa2 --- /dev/null +++ b/java/ql/src/experimental/Security/CWE/CWE-094/RhinoInjection.ql @@ -0,0 +1,17 @@ +/** + * @name Injection in Mozilla Rhino JavaScript Engine + * @description Evaluation of a user-controlled JavaScript or Java expression in Rhino + * JavaScript Engine may lead to remote code execution. + * @kind path-problem + * @id java/rhino-injection + * @tags security + * external/cwe/cwe-094 + */ + +import java +import RhinoInjection +import DataFlow::PathGraph + +from DataFlow::PathNode source, DataFlow::PathNode sink, RhinoInjectionConfig conf +where conf.hasFlowPath(source, sink) +select sink.getNode(), source, sink, "Rhino injection from $@.", source.getNode(), " user input" diff --git a/java/ql/src/experimental/Security/CWE/CWE-094/RhinoInjection.qll b/java/ql/src/experimental/Security/CWE/CWE-094/RhinoInjection.qll new file mode 100644 index 00000000000..a560e340e85 --- /dev/null +++ b/java/ql/src/experimental/Security/CWE/CWE-094/RhinoInjection.qll @@ -0,0 +1,56 @@ +import java +import semmle.code.java.dataflow.FlowSources +import semmle.code.java.dataflow.TaintTracking + +/** The class `org.mozilla.javascript.Context`. */ +class Context extends RefType { + Context() { this.hasQualifiedName("org.mozilla.javascript", "Context") } +} + +/** + * A method that evaluates a Rhino expression. + */ +class EvaluateExpressionMethod extends Method { + EvaluateExpressionMethod() { + this.getDeclaringType().getAnAncestor*() instanceof Context and + ( + hasName("evaluateString") or + hasName("evaluateReader") + ) + } +} + +/** + * A taint-tracking configuration for unsafe user input that is used to evaluate + * a Rhino expression. + */ +class RhinoInjectionConfig extends TaintTracking::Configuration { + RhinoInjectionConfig() { this = "RhinoInjectionConfig" } + + override predicate isSource(DataFlow::Node source) { + source instanceof RemoteFlowSource + or + source instanceof LocalUserInput + } + + override predicate isSink(DataFlow::Node sink) { sink instanceof EvaluateExpressionSink } +} + +/** + * A sink for Rhino code injection vulnerabilities. + */ +class EvaluateExpressionSink extends DataFlow::ExprNode { + EvaluateExpressionSink() { + exists(MethodAccess ea, EvaluateExpressionMethod m | m = ea.getMethod() | + this.asExpr() = ea.getArgument(1) and // The second argument is the JavaScript or Java input + not exists(MethodAccess ca | + ( + ca.getMethod().hasName("initSafeStandardObjects") // safe mode + or + ca.getMethod().hasName("setClassShutter") // `ClassShutter` constraint is enforced + ) and + ea.getQualifier() = ca.getQualifier().(VarAccess).getVariable().getAnAccess() + ) + ) + } +} diff --git a/java/ql/test/experimental/query-tests/security/CWE-094/RhinoInjection.expected b/java/ql/test/experimental/query-tests/security/CWE-094/RhinoInjection.expected new file mode 100644 index 00000000000..4d2736c8230 --- /dev/null +++ b/java/ql/test/experimental/query-tests/security/CWE-094/RhinoInjection.expected @@ -0,0 +1,7 @@ +edges +| RhinoServlet.java:25:23:25:50 | getParameter(...) : String | RhinoServlet.java:29:55:29:58 | code | +nodes +| RhinoServlet.java:25:23:25:50 | getParameter(...) : String | semmle.label | getParameter(...) : String | +| RhinoServlet.java:29:55:29:58 | code | semmle.label | code | +#select +| RhinoServlet.java:29:55:29:58 | code | RhinoServlet.java:25:23:25:50 | getParameter(...) : String | RhinoServlet.java:29:55:29:58 | code | Rhino injection from $@. | RhinoServlet.java:25:23:25:50 | getParameter(...) | user input | diff --git a/java/ql/test/experimental/query-tests/security/CWE-094/RhinoInjection.qlref b/java/ql/test/experimental/query-tests/security/CWE-094/RhinoInjection.qlref new file mode 100644 index 00000000000..e306dda44df --- /dev/null +++ b/java/ql/test/experimental/query-tests/security/CWE-094/RhinoInjection.qlref @@ -0,0 +1 @@ +experimental/Security/CWE/CWE-094/RhinoInjection.ql \ No newline at end of file diff --git a/java/ql/test/experimental/query-tests/security/CWE-094/RhinoServlet.java b/java/ql/test/experimental/query-tests/security/CWE-094/RhinoServlet.java new file mode 100644 index 00000000000..f6f529785cc --- /dev/null +++ b/java/ql/test/experimental/query-tests/security/CWE-094/RhinoServlet.java @@ -0,0 +1,78 @@ +import java.io.IOException; +import javax.servlet.ServletException; +import javax.servlet.http.HttpServlet; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +import org.mozilla.javascript.ClassShutter; +import org.mozilla.javascript.Context; +import org.mozilla.javascript.Scriptable; +import org.mozilla.javascript.RhinoException; + +/** + * Servlet implementation class RhinoServlet + */ +public class RhinoServlet extends HttpServlet { + private static final long serialVersionUID = 1L; + + public RhinoServlet() { + super(); + } + + // BAD: allow arbitrary Java and JavaScript code to be executed + protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { + response.setContentType("text/plain"); + String code = request.getParameter("code"); + Context ctx = Context.enter(); + try { + Scriptable scope = ctx.initStandardObjects(); + Object result = ctx.evaluateString(scope, code, "", 1, null); + response.getWriter().print(Context.toString(result)); + } catch(RhinoException ex) { + response.getWriter().println(ex.getMessage()); + } finally { + Context.exit(); + } + } + + // GOOD: enable the safe mode + protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { + response.setContentType("text/plain"); + String code = request.getParameter("code"); + Context ctx = Context.enter(); + try { + Scriptable scope = ctx.initSafeStandardObjects(); + Object result = ctx.evaluateString(scope, code, "", 1, null); + response.getWriter().print(Context.toString(result)); + } catch(RhinoException ex) { + response.getWriter().println(ex.getMessage()); + } finally { + Context.exit(); + } + } + + // GOOD: enforce a constraint on allowed classes + protected void doPut(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { + response.setContentType("text/plain"); + String code = request.getParameter("code"); + Context ctx = Context.enter(); + try { + Scriptable scope = ctx.initStandardObjects(); + ctx.setClassShutter(new ClassShutter() { + public boolean visibleToScripts(String className) { + if(className.startsWith("com.example.")) { + return true; + } + return false; + } + }); + + Object result = ctx.evaluateString(scope, code, "", 1, null); + response.getWriter().print(Context.toString(result)); + } catch(RhinoException ex) { + response.getWriter().println(ex.getMessage()); + } finally { + Context.exit(); + } + } +} diff --git a/java/ql/test/experimental/query-tests/security/CWE-094/options b/java/ql/test/experimental/query-tests/security/CWE-094/options index 95bc9acaa08..48cc00e0a17 100644 --- a/java/ql/test/experimental/query-tests/security/CWE-094/options +++ b/java/ql/test/experimental/query-tests/security/CWE-094/options @@ -1,2 +1,2 @@ -//semmle-extractor-options: --javac-args -cp ${testdir}/../../../../stubs/springframework-5.2.3:${testdir}/../../../../stubs/mvel2-2.4.7:${testdir}/../../../../stubs/jsr223-api:${testdir}/../../../../stubs/apache-commons-jexl-2.1.1:${testdir}/../../../../stubs/apache-commons-jexl-3.1:${testdir}/../../../../stubs/scriptengine:${testdir}/../../../../stubs/java-ee-el:${testdir}/../../../../stubs/juel-2.2:${testdir}/../../../stubs/groovy-all-3.0.7:${testdir}/../../../../stubs/servlet-api-2.4:${testdir}/../../../../stubs/jython-2.7.2 +//semmle-extractor-options: --javac-args -cp ${testdir}/../../../../stubs/springframework-5.2.3:${testdir}/../../../../stubs/mvel2-2.4.7:${testdir}/../../../../stubs/jsr223-api:${testdir}/../../../../stubs/apache-commons-jexl-2.1.1:${testdir}/../../../../stubs/apache-commons-jexl-3.1:${testdir}/../../../../stubs/scriptengine:${testdir}/../../../../stubs/java-ee-el:${testdir}/../../../../stubs/juel-2.2:${testdir}/../../../stubs/groovy-all-3.0.7:${testdir}/../../../../stubs/servlet-api-2.4:${testdir}/../../../../stubs/jython-2.7.2:${testdir}/../../../../experimental/stubs/rhino-1.7.13 diff --git a/java/ql/test/experimental/stubs/rhino-1.7.13/org/mozilla/javascript/ClassShutter.java b/java/ql/test/experimental/stubs/rhino-1.7.13/org/mozilla/javascript/ClassShutter.java new file mode 100644 index 00000000000..f425e08e966 --- /dev/null +++ b/java/ql/test/experimental/stubs/rhino-1.7.13/org/mozilla/javascript/ClassShutter.java @@ -0,0 +1,56 @@ +/* -*- Mode: java; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +// API class + +package org.mozilla.javascript; + +/** +Embeddings that wish to filter Java classes that are visible to scripts +through the LiveConnect, should implement this interface. + +@see Context#setClassShutter(ClassShutter) +@since 1.5 Release 4 +@author Norris Boyd +*/ + + public interface ClassShutter { + + /** + * Return true iff the Java class with the given name should be exposed + * to scripts. + *

    + * An embedding may filter which Java classes are exposed through + * LiveConnect to JavaScript scripts. + *

    + * Due to the fact that there is no package reflection in Java, + * this method will also be called with package names. There + * is no way for Rhino to tell if "Packages.a.b" is a package name + * or a class that doesn't exist. What Rhino does is attempt + * to load each segment of "Packages.a.b.c": It first attempts to + * load class "a", then attempts to load class "a.b", then + * finally attempts to load class "a.b.c". On a Rhino installation + * without any ClassShutter set, and without any of the + * above classes, the expression "Packages.a.b.c" will result in + * a [JavaPackage a.b.c] and not an error. + *

    + * With ClassShutter supplied, Rhino will first call + * visibleToScripts before attempting to look up the class name. If + * visibleToScripts returns false, the class name lookup is not + * performed and subsequent Rhino execution assumes the class is + * not present. So for "java.lang.System.out.println" the lookup + * of "java.lang.System" is skipped and thus Rhino assumes that + * "java.lang.System" doesn't exist. So then for "java.lang.System.out", + * Rhino attempts to load the class "java.lang.System.out" because + * it assumes that "java.lang.System" is a package name. + *

    + * @param fullClassName the full name of the class (including the package + * name, with '.' as a delimiter). For example the + * standard string class is "java.lang.String" + * @return whether or not to reveal this class to scripts + */ + public boolean visibleToScripts(String fullClassName); +} diff --git a/java/ql/test/experimental/stubs/rhino-1.7.13/org/mozilla/javascript/Context.java b/java/ql/test/experimental/stubs/rhino-1.7.13/org/mozilla/javascript/Context.java new file mode 100644 index 00000000000..57d30a3ab25 --- /dev/null +++ b/java/ql/test/experimental/stubs/rhino-1.7.13/org/mozilla/javascript/Context.java @@ -0,0 +1,623 @@ +/* -*- Mode: java; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +// API class + +package org.mozilla.javascript; + +import java.io.Closeable; +import java.io.IOException; +import java.io.Reader; +import java.util.Locale; + +/** + * This class represents the runtime context of an executing script. + * + * Before executing a script, an instance of Context must be created + * and associated with the thread that will be executing the script. + * The Context will be used to store information about the executing + * of the script such as the call stack. Contexts are associated with + * the current thread using the {@link #call(ContextAction)} + * or {@link #enter()} methods.

    + * + * Different forms of script execution are supported. Scripts may be + * evaluated from the source directly, or first compiled and then later + * executed. Interactive execution is also supported.

    + * + * Some aspects of script execution, such as type conversions and + * object creation, may be accessed directly through methods of + * Context. + * + * @see Scriptable + * @author Norris Boyd + * @author Brendan Eich + */ + +public class Context + implements Closeable +{ + /** + * Creates a new Context. The context will be associated with the {@link + * ContextFactory#getGlobal() global context factory}. + * + * Note that the Context must be associated with a thread before + * it can be used to execute a script. + * @deprecated this constructor is deprecated because it creates a + * dependency on a static singleton context factory. Use + * {@link ContextFactory#enter()} or + * {@link ContextFactory#call(ContextAction)} instead. If you subclass + * this class, consider using {@link #Context(ContextFactory)} constructor + * instead in the subclasses' constructors. + */ + @Deprecated + public Context() + { + } + + /** + * Creates a new context. Provided as a preferred super constructor for + * subclasses in place of the deprecated default public constructor. + * @param factory the context factory associated with this context (most + * likely, the one that created the context). Can not be null. The context + * features are inherited from the factory, and the context will also + * otherwise use its factory's services. + * @throws IllegalArgumentException if factory parameter is null. + */ + protected Context(ContextFactory factory) + { + } + + /** + * Get the current Context. + * + * The current Context is per-thread; this method looks up + * the Context associated with the current thread.

    + * + * @return the Context associated with the current thread, or + * null if no context is associated with the current + * thread. + * @see ContextFactory#enterContext() + * @see ContextFactory#call(ContextAction) + */ + public static Context getCurrentContext() + { + return null; + } + + /** + * Same as calling {@link ContextFactory#enterContext()} on the global + * ContextFactory instance. + * @return a Context associated with the current thread + * @see #getCurrentContext() + * @see #exit() + * @see #call(ContextAction) + */ + public static Context enter() + { + return null; + } + + /** + * Get a Context associated with the current thread, using + * the given Context if need be. + *

    + * The same as enter() except that cx + * is associated with the current thread and returned if + * the current thread has no associated context and cx + * is not associated with any other thread. + * @param cx a Context to associate with the thread if possible + * @return a Context associated with the current thread + * @deprecated use {@link ContextFactory#enterContext(Context)} instead as + * this method relies on usage of a static singleton "global" ContextFactory. + * @see ContextFactory#enterContext(Context) + * @see ContextFactory#call(ContextAction) + */ + @Deprecated + public static Context enter(Context cx) + { + return null; + } + + static final Context enter(Context cx, ContextFactory factory) + { + return null; + } + + /** + * Exit a block of code requiring a Context. + * + * Calling exit() will remove the association between + * the current thread and a Context if the prior call to + * {@link ContextFactory#enterContext()} on this thread newly associated a + * Context with this thread. Once the current thread no longer has an + * associated Context, it cannot be used to execute JavaScript until it is + * again associated with a Context. + * @see ContextFactory#enterContext() + */ + public static void exit() + { + } + + @Override + public void close() { + } + + /** + * Return {@link ContextFactory} instance used to create this Context. + */ + public final ContextFactory getFactory() + { + return null; + } + + /** + * Checks if this is a sealed Context. A sealed Context instance does not + * allow to modify any of its properties and will throw an exception + * on any such attempt. + * @see #seal(Object sealKey) + */ + public final boolean isSealed() + { + return false; + } + + /** + * Seal this Context object so any attempt to modify any of its properties + * including calling {@link #enter()} and {@link #exit()} methods will + * throw an exception. + *

    + * If sealKey is not null, calling + * {@link #unseal(Object sealKey)} with the same key unseals + * the object. If sealKey is null, unsealing is no longer possible. + * + * @see #isSealed() + * @see #unseal(Object) + */ + public final void seal(Object sealKey) + { + } + + /** + * Unseal previously sealed Context object. + * The sealKey argument should not be null and should match + * sealKey suplied with the last call to + * {@link #seal(Object)} or an exception will be thrown. + * + * @see #isSealed() + * @see #seal(Object sealKey) + */ + public final void unseal(Object sealKey) + { + } + + /** + * Get the current language version. + *

    + * The language version number affects JavaScript semantics as detailed + * in the overview documentation. + * + * @return an integer that is one of VERSION_1_0, VERSION_1_1, etc. + */ + public final int getLanguageVersion() + { + return -1; + } + + /** + * Set the language version. + * + *

    + * Setting the language version will affect functions and scripts compiled + * subsequently. See the overview documentation for version-specific + * behavior. + * + * @param version the version as specified by VERSION_1_0, VERSION_1_1, etc. + */ + public void setLanguageVersion(int version) + { + } + + public static boolean isValidLanguageVersion(int version) + { + return false; + } + + public static void checkLanguageVersion(int version) + { + } + + /** + * Get the implementation version. + * + *

    + * The implementation version is of the form + *

    +     *    "name langVer release relNum date"
    +     * 
    + * where name is the name of the product, langVer is + * the language version, relNum is the release number, and + * date is the release date for that specific + * release in the form "yyyy mm dd". + * + * @return a string that encodes the product, language version, release + * number, and date. + */ + public final String getImplementationVersion() { + return null; + } + + /** + * Initialize the standard objects. + * + * Creates instances of the standard objects and their constructors + * (Object, String, Number, Date, etc.), setting up 'scope' to act + * as a global object as in ECMA 15.1.

    + * + * This method must be called to initialize a scope before scripts + * can be evaluated in that scope.

    + * + * This method does not affect the Context it is called upon. + * + * @return the initialized scope + */ + public final ScriptableObject initStandardObjects() + { + return null; + } + + /** + * Initialize the standard objects, leaving out those that offer access directly + * to Java classes. This sets up "scope" to have access to all the standard + * JavaScript classes, but does not create global objects for any top-level + * Java packages. In addition, the "Packages," "JavaAdapter," and + * "JavaImporter" classes, and the "getClass" function, are not + * initialized. + * + * The result of this function is a scope that may be safely used in a "sandbox" + * environment where it is not desirable to give access to Java code from JavaScript. + * + * Creates instances of the standard objects and their constructors + * (Object, String, Number, Date, etc.), setting up 'scope' to act + * as a global object as in ECMA 15.1.

    + * + * This method must be called to initialize a scope before scripts + * can be evaluated in that scope.

    + * + * This method does not affect the Context it is called upon. + * + * @return the initialized scope + */ + public final ScriptableObject initSafeStandardObjects() + { + return null; + } + + /** + * Initialize the standard objects. + * + * Creates instances of the standard objects and their constructors + * (Object, String, Number, Date, etc.), setting up 'scope' to act + * as a global object as in ECMA 15.1.

    + * + * This method must be called to initialize a scope before scripts + * can be evaluated in that scope.

    + * + * This method does not affect the Context it is called upon. + * + * @param scope the scope to initialize, or null, in which case a new + * object will be created to serve as the scope + * @return the initialized scope. The method returns the value of the scope + * argument if it is not null or newly allocated scope object which + * is an instance {@link ScriptableObject}. + */ + public final Scriptable initStandardObjects(ScriptableObject scope) + { + return null; + } + + /** + * Initialize the standard objects, leaving out those that offer access directly + * to Java classes. This sets up "scope" to have access to all the standard + * JavaScript classes, but does not create global objects for any top-level + * Java packages. In addition, the "Packages," "JavaAdapter," and + * "JavaImporter" classes, and the "getClass" function, are not + * initialized. + * + * The result of this function is a scope that may be safely used in a "sandbox" + * environment where it is not desirable to give access to Java code from JavaScript. + * + * Creates instances of the standard objects and their constructors + * (Object, String, Number, Date, etc.), setting up 'scope' to act + * as a global object as in ECMA 15.1.

    + * + * This method must be called to initialize a scope before scripts + * can be evaluated in that scope.

    + * + * This method does not affect the Context it is called upon. + * + * @param scope the scope to initialize, or null, in which case a new + * object will be created to serve as the scope + * @return the initialized scope. The method returns the value of the scope + * argument if it is not null or newly allocated scope object which + * is an instance {@link ScriptableObject}. + */ + public final Scriptable initSafeStandardObjects(ScriptableObject scope) + { + return null; + } + + /** + * Initialize the standard objects. + * + * Creates instances of the standard objects and their constructors + * (Object, String, Number, Date, etc.), setting up 'scope' to act + * as a global object as in ECMA 15.1.

    + * + * This method must be called to initialize a scope before scripts + * can be evaluated in that scope.

    + * + * This method does not affect the Context it is called upon.

    + * + * This form of the method also allows for creating "sealed" standard + * objects. An object that is sealed cannot have properties added, changed, + * or removed. This is useful to create a "superglobal" that can be shared + * among several top-level objects. Note that sealing is not allowed in + * the current ECMA/ISO language specification, but is likely for + * the next version. + * + * @param scope the scope to initialize, or null, in which case a new + * object will be created to serve as the scope + * @param sealed whether or not to create sealed standard objects that + * cannot be modified. + * @return the initialized scope. The method returns the value of the scope + * argument if it is not null or newly allocated scope object. + * @since 1.4R3 + */ + public ScriptableObject initStandardObjects(ScriptableObject scope, + boolean sealed) + { + return null; + } + + /** + * Initialize the standard objects, leaving out those that offer access directly + * to Java classes. This sets up "scope" to have access to all the standard + * JavaScript classes, but does not create global objects for any top-level + * Java packages. In addition, the "Packages," "JavaAdapter," and + * "JavaImporter" classes, and the "getClass" function, are not + * initialized. + * + * The result of this function is a scope that may be safely used in a "sandbox" + * environment where it is not desirable to give access to Java code from JavaScript. + * + * Creates instances of the standard objects and their constructors + * (Object, String, Number, Date, etc.), setting up 'scope' to act + * as a global object as in ECMA 15.1.

    + * + * This method must be called to initialize a scope before scripts + * can be evaluated in that scope.

    + * + * This method does not affect the Context it is called upon.

    + * + * This form of the method also allows for creating "sealed" standard + * objects. An object that is sealed cannot have properties added, changed, + * or removed. This is useful to create a "superglobal" that can be shared + * among several top-level objects. Note that sealing is not allowed in + * the current ECMA/ISO language specification, but is likely for + * the next version. + * + * @param scope the scope to initialize, or null, in which case a new + * object will be created to serve as the scope + * @param sealed whether or not to create sealed standard objects that + * cannot be modified. + * @return the initialized scope. The method returns the value of the scope + * argument if it is not null or newly allocated scope object. + * @since 1.7.6 + */ + public ScriptableObject initSafeStandardObjects(ScriptableObject scope, + boolean sealed) + { + return null; + } + + /** + * Get the singleton object that represents the JavaScript Undefined value. + */ + public static Object getUndefinedValue() + { + return null; + } + + /** + * Evaluate a JavaScript source string. + * + * The provided source name and line number are used for error messages + * and for producing debug information. + * + * @param scope the scope to execute in + * @param source the JavaScript source + * @param sourceName a string describing the source, such as a filename + * @param lineno the starting line number + * @param securityDomain an arbitrary object that specifies security + * information about the origin or owner of the script. For + * implementations that don't care about security, this value + * may be null. + * @return the result of evaluating the string + * @see org.mozilla.javascript.SecurityController + */ + public final Object evaluateString(Scriptable scope, String source, + String sourceName, int lineno, + Object securityDomain) + { + return null; + } + + /** + * Evaluate a reader as JavaScript source. + * + * All characters of the reader are consumed. + * + * @param scope the scope to execute in + * @param in the Reader to get JavaScript source from + * @param sourceName a string describing the source, such as a filename + * @param lineno the starting line number + * @param securityDomain an arbitrary object that specifies security + * information about the origin or owner of the script. For + * implementations that don't care about security, this value + * may be null. + * @return the result of evaluating the source + * + * @exception IOException if an IOException was generated by the Reader + */ + public final Object evaluateReader(Scriptable scope, Reader in, + String sourceName, int lineno, + Object securityDomain) + throws IOException + { + return null; + } + + /** + * Convert the value to a JavaScript boolean value. + *

    + * See ECMA 9.2. + * + * @param value a JavaScript value + * @return the corresponding boolean value converted using + * the ECMA rules + */ + public static boolean toBoolean(Object value) + { + return false; + } + + /** + * Convert the value to a JavaScript Number value. + *

    + * Returns a Java double for the JavaScript Number. + *

    + * See ECMA 9.3. + * + * @param value a JavaScript value + * @return the corresponding double value converted using + * the ECMA rules + */ + public static double toNumber(Object value) + { + return -1; + } + + /** + * Convert the value to a JavaScript String value. + *

    + * See ECMA 9.8. + *

    + * @param value a JavaScript value + * @return the corresponding String value converted using + * the ECMA rules + */ + public static String toString(Object value) + { + return null; + } + + /** + * Convert the value to an JavaScript object value. + *

    + * Note that a scope must be provided to look up the constructors + * for Number, Boolean, and String. + *

    + * See ECMA 9.9. + *

    + * Additionally, arbitrary Java objects and classes will be + * wrapped in a Scriptable object with its Java fields and methods + * reflected as JavaScript properties of the object. + * + * @param value any Java object + * @param scope global scope containing constructors for Number, + * Boolean, and String + * @return new JavaScript object + */ + public static Scriptable toObject(Object value, Scriptable scope) + { + return null; + } + + /** + * Convenient method to convert java value to its closest representation + * in JavaScript. + *

    + * If value is an instance of String, Number, Boolean, Function or + * Scriptable, it is returned as it and will be treated as the corresponding + * JavaScript type of string, number, boolean, function and object. + *

    + * Note that for Number instances during any arithmetic operation in + * JavaScript the engine will always use the result of + * Number.doubleValue() resulting in a precision loss if + * the number can not fit into double. + *

    + * If value is an instance of Character, it will be converted to string of + * length 1 and its JavaScript type will be string. + *

    + * The rest of values will be wrapped as LiveConnect objects + * by calling {@link WrapFactory#wrap(Context cx, Scriptable scope, + * Object obj, Class staticType)} as in: + *

    +     *    Context cx = Context.getCurrentContext();
    +     *    return cx.getWrapFactory().wrap(cx, scope, value, null);
    +     * 
    + * + * @param value any Java object + * @param scope top scope object + * @return value suitable to pass to any API that takes JavaScript values. + */ + public static Object javaToJS(Object value, Scriptable scope) + { + return null; + } + + /** + * Convert a JavaScript value into the desired type. + * Uses the semantics defined with LiveConnect3 and throws an + * Illegal argument exception if the conversion cannot be performed. + * @param value the JavaScript value to convert + * @param desiredType the Java type to convert to. Primitive Java + * types are represented using the TYPE fields in the corresponding + * wrapper class in java.lang. + * @return the converted value + * @throws EvaluatorException if the conversion cannot be performed + */ + public static Object jsToJava(Object value, Class desiredType) + { + return null; + } + + /** + * Set the LiveConnect access filter for this context. + *

    {@link ClassShutter} may only be set if it is currently null. + * Otherwise a SecurityException is thrown. + * @param shutter a ClassShutter object + * @throws SecurityException if there is already a ClassShutter + * object for this Context + */ + public synchronized final void setClassShutter(ClassShutter shutter) + { + } + + final synchronized ClassShutter getClassShutter() + { + return null; + } + + public interface ClassShutterSetter { + public void setClassShutter(ClassShutter shutter); + public ClassShutter getClassShutter(); + } + + public final synchronized ClassShutterSetter getClassShutterSetter() { + return null; + } +} diff --git a/java/ql/test/experimental/stubs/rhino-1.7.13/org/mozilla/javascript/ContextFactory.java b/java/ql/test/experimental/stubs/rhino-1.7.13/org/mozilla/javascript/ContextFactory.java new file mode 100644 index 00000000000..a7f83f2095d --- /dev/null +++ b/java/ql/test/experimental/stubs/rhino-1.7.13/org/mozilla/javascript/ContextFactory.java @@ -0,0 +1,314 @@ +/* -*- Mode: java; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +// API class + +package org.mozilla.javascript; + +/** + * Factory class that Rhino runtime uses to create new {@link Context} + * instances. A ContextFactory can also notify listeners + * about context creation and release. + *

    + * When the Rhino runtime needs to create new {@link Context} instance during + * execution of {@link Context#enter()} or {@link Context}, it will call + * {@link #makeContext()} of the current global ContextFactory. + * See {@link #getGlobal()} and {@link #initGlobal(ContextFactory)}. + *

    + * It is also possible to use explicit ContextFactory instances for Context + * creation. This is useful to have a set of independent Rhino runtime + * instances under single JVM. See {@link #call(ContextAction)}. + *

    + * The following example demonstrates Context customization to terminate + * scripts running more then 10 seconds and to provide better compatibility + * with JavaScript code using MSIE-specific features. + *

    + * import org.mozilla.javascript.*;
    + *
    + * class MyFactory extends ContextFactory
    + * {
    + *
    + *     // Custom {@link Context} to store execution time.
    + *     private static class MyContext extends Context
    + *     {
    + *         long startTime;
    + *     }
    + *
    + *     static {
    + *         // Initialize GlobalFactory with custom factory
    + *         ContextFactory.initGlobal(new MyFactory());
    + *     }
    + *
    + *     // Override {@link #makeContext()}
    + *     protected Context makeContext()
    + *     {
    + *         MyContext cx = new MyContext();
    + *         // Make Rhino runtime to call observeInstructionCount
    + *         // each 10000 bytecode instructions
    + *         cx.setInstructionObserverThreshold(10000);
    + *         return cx;
    + *     }
    + *
    + *     // Override {@link #hasFeature(Context, int)}
    + *     public boolean hasFeature(Context cx, int featureIndex)
    + *     {
    + *         // Turn on maximum compatibility with MSIE scripts
    + *         switch (featureIndex) {
    + *             case {@link Context#FEATURE_NON_ECMA_GET_YEAR}:
    + *                 return true;
    + *
    + *             case {@link Context#FEATURE_MEMBER_EXPR_AS_FUNCTION_NAME}:
    + *                 return true;
    + *
    + *             case {@link Context#FEATURE_RESERVED_KEYWORD_AS_IDENTIFIER}:
    + *                 return true;
    + *
    + *             case {@link Context#FEATURE_PARENT_PROTO_PROPERTIES}:
    + *                 return false;
    + *         }
    + *         return super.hasFeature(cx, featureIndex);
    + *     }
    + *
    + *     // Override {@link #observeInstructionCount(Context, int)}
    + *     protected void observeInstructionCount(Context cx, int instructionCount)
    + *     {
    + *         MyContext mcx = (MyContext)cx;
    + *         long currentTime = System.currentTimeMillis();
    + *         if (currentTime - mcx.startTime > 10*1000) {
    + *             // More then 10 seconds from Context creation time:
    + *             // it is time to stop the script.
    + *             // Throw Error instance to ensure that script will never
    + *             // get control back through catch or finally.
    + *             throw new Error();
    + *         }
    + *     }
    + *
    + *     // Override {@link #doTopCall(Callable,
    +                               Context, Scriptable,
    +                               Scriptable, Object[])}
    + *     protected Object doTopCall(Callable callable,
    + *                                Context cx, Scriptable scope,
    + *                                Scriptable thisObj, Object[] args)
    + *     {
    + *         MyContext mcx = (MyContext)cx;
    + *         mcx.startTime = System.currentTimeMillis();
    + *
    + *         return super.doTopCall(callable, cx, scope, thisObj, args);
    + *     }
    + *
    + * }
    + * 
    + */ + +public class ContextFactory +{ + + /** + * Listener of {@link Context} creation and release events. + */ + public interface Listener + { + /** + * Notify about newly created {@link Context} object. + */ + public void contextCreated(Context cx); + + /** + * Notify that the specified {@link Context} instance is no longer + * associated with the current thread. + */ + public void contextReleased(Context cx); + } + + /** + * Get global ContextFactory. + * + * @see #hasExplicitGlobal() + * @see #initGlobal(ContextFactory) + */ + public static ContextFactory getGlobal() + { + return null; + } + + /** + * Check if global factory was set. + * Return true to indicate that {@link #initGlobal(ContextFactory)} was + * already called and false to indicate that the global factory was not + * explicitly set. + * + * @see #getGlobal() + * @see #initGlobal(ContextFactory) + */ + public static boolean hasExplicitGlobal() + { + return false; + } + + /** + * Set global ContextFactory. + * The method can only be called once. + * + * @see #getGlobal() + * @see #hasExplicitGlobal() + */ + public synchronized static void initGlobal(ContextFactory factory) + { + } + + public interface GlobalSetter { + public void setContextFactoryGlobal(ContextFactory factory); + public ContextFactory getContextFactoryGlobal(); + } + + public synchronized static GlobalSetter getGlobalSetter() { + return null; + } + + /** + * Create new {@link Context} instance to be associated with the current + * thread. + * This is a callback method used by Rhino to create {@link Context} + * instance when it is necessary to associate one with the current + * execution thread. makeContext() is allowed to call + * {@link Context#seal(Object)} on the result to prevent + * {@link Context} changes by hostile scripts or applets. + */ + protected Context makeContext() + { + return null; + } + + /** + * Implementation of {@link Context#hasFeature(int featureIndex)}. + * This can be used to customize {@link Context} without introducing + * additional subclasses. + */ + protected boolean hasFeature(Context cx, int featureIndex) + { + return false; + } + + /** + * Get ClassLoader to use when searching for Java classes. + * Unless it was explicitly initialized with + * {@link #initApplicationClassLoader(ClassLoader)} the method returns + * null to indicate that Thread.getContextClassLoader() should be used. + */ + public final ClassLoader getApplicationClassLoader() + { + return null; + } + + /** + * Set explicit class loader to use when searching for Java classes. + * + * @see #getApplicationClassLoader() + */ + public final void initApplicationClassLoader(ClassLoader loader) + { + } + + /** + * Checks if this is a sealed ContextFactory. + * @see #seal() + */ + public final boolean isSealed() + { + return false; + } + + /** + * Seal this ContextFactory so any attempt to modify it like to add or + * remove its listeners will throw an exception. + * @see #isSealed() + */ + public final void seal() + { + } + + /** + * Get a context associated with the current thread, creating one if need + * be. The Context stores the execution state of the JavaScript engine, so + * it is required that the context be entered before execution may begin. + * Once a thread has entered a Context, then getCurrentContext() may be + * called to find the context that is associated with the current thread. + *

    + * Calling enterContext() will return either the Context + * currently associated with the thread, or will create a new context and + * associate it with the current thread. Each call to + * enterContext() must have a matching call to + * {@link Context#exit()}. + *

    +     *      Context cx = contextFactory.enterContext();
    +     *      try {
    +     *          ...
    +     *          cx.evaluateString(...);
    +     *      } finally {
    +     *          Context.exit();
    +     *      }
    +     * 
    + * Instead of using enterContext(), exit() pair consider + * using {@link #call(ContextAction)} which guarantees proper association + * of Context instances with the current thread. + * With this method the above example becomes: + *
    +     *      ContextFactory.call(new ContextAction() {
    +     *          public Object run(Context cx) {
    +     *              ...
    +     *              cx.evaluateString(...);
    +     *              return null;
    +     *          }
    +     *      });
    +     * 
    + * @return a Context associated with the current thread + * @see Context#getCurrentContext() + * @see Context#exit() + * @see #call(ContextAction) + */ + public Context enterContext() + { + return null; + } + + /** + * @deprecated use {@link #enterContext()} instead + * @return a Context associated with the current thread + */ + @Deprecated + public final Context enter() + { + return null; + } + + /** + * @deprecated Use {@link Context#exit()} instead. + */ + @Deprecated + public final void exit() + { + } + + /** + * Get a Context associated with the current thread, using the given + * Context if need be. + *

    + * The same as enterContext() except that cx + * is associated with the current thread and returned if the current thread + * has no associated context and cx is not associated with any + * other thread. + * @param cx a Context to associate with the thread if possible + * @return a Context associated with the current thread + * @see #enterContext() + * @see #call(ContextAction) + * @throws IllegalStateException if cx is already associated + * with a different thread + */ + public final Context enterContext(Context cx) + { + return null; + } +} diff --git a/java/ql/test/experimental/stubs/rhino-1.7.13/org/mozilla/javascript/RhinoException.java b/java/ql/test/experimental/stubs/rhino-1.7.13/org/mozilla/javascript/RhinoException.java new file mode 100644 index 00000000000..b11befb4a63 --- /dev/null +++ b/java/ql/test/experimental/stubs/rhino-1.7.13/org/mozilla/javascript/RhinoException.java @@ -0,0 +1,15 @@ +/* -*- Mode: java; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + + +package org.mozilla.javascript; + +/** + * The class of exceptions thrown by the JavaScript engine. + */ +public abstract class RhinoException extends RuntimeException +{ +} diff --git a/java/ql/test/experimental/stubs/rhino-1.7.13/org/mozilla/javascript/Scriptable.java b/java/ql/test/experimental/stubs/rhino-1.7.13/org/mozilla/javascript/Scriptable.java new file mode 100644 index 00000000000..34616f7ad74 --- /dev/null +++ b/java/ql/test/experimental/stubs/rhino-1.7.13/org/mozilla/javascript/Scriptable.java @@ -0,0 +1,304 @@ +/* -*- Mode: java; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +// API class + +package org.mozilla.javascript; + +/** + * This is interface that all objects in JavaScript must implement. + * The interface provides for the management of properties and for + * performing conversions. + *

    + * Host system implementors may find it easier to extend the ScriptableObject + * class rather than implementing Scriptable when writing host objects. + *

    + * There are many static methods defined in ScriptableObject that perform + * the multiple calls to the Scriptable interface needed in order to + * manipulate properties in prototype chains. + *

    + * + * @see org.mozilla.javascript.ScriptableObject + * @author Norris Boyd + * @author Nick Thompson + * @author Brendan Eich + */ + +public interface Scriptable { + + /** + * Get the name of the set of objects implemented by this Java class. + * This corresponds to the [[Class]] operation in ECMA and is used + * by Object.prototype.toString() in ECMA.

    + * See ECMA 8.6.2 and 15.2.4.2. + */ + public String getClassName(); + + /** + * Get a named property from the object. + * + * Looks property up in this object and returns the associated value + * if found. Returns NOT_FOUND if not found. + * Note that this method is not expected to traverse the prototype + * chain. This is different from the ECMA [[Get]] operation. + * + * Depending on the property selector, the runtime will call + * this method or the form of get that takes an + * integer: + * + * + * + * + * + * + * + * + * + * + *
    JavaScript codeJava code
    a.b a.get("b", a)
    a["foo"] a.get("foo", a)
    a[3] a.get(3, a)
    a["3"] a.get(3, a)
    a[3.0] a.get(3, a)
    a["3.0"] a.get("3.0", a)
    a[1.1] a.get("1.1", a)
    a[-4] a.get(-4, a)
    + *

    + * The values that may be returned are limited to the following: + *

      + *
    • java.lang.Boolean objects
    • + *
    • java.lang.String objects
    • + *
    • java.lang.Number objects
    • + *
    • org.mozilla.javascript.Scriptable objects
    • + *
    • null
    • + *
    • The value returned by Context.getUndefinedValue()
    • + *
    • NOT_FOUND
    • + *
    + * @param name the name of the property + * @param start the object in which the lookup began + * @return the value of the property (may be null), or NOT_FOUND + * @see org.mozilla.javascript.Context#getUndefinedValue + */ + public Object get(String name, Scriptable start); + + /** + * Get a property from the object selected by an integral index. + * + * Identical to get(String, Scriptable) except that + * an integral index is used to select the property. + * + * @param index the numeric index for the property + * @param start the object in which the lookup began + * @return the value of the property (may be null), or NOT_FOUND + * @see org.mozilla.javascript.Scriptable#get(String,Scriptable) + */ + public Object get(int index, Scriptable start); + + /** + * Indicates whether or not a named property is defined in an object. + * + * Does not traverse the prototype chain.

    + * + * The property is specified by a String name + * as defined for the get method.

    + * + * @param name the name of the property + * @param start the object in which the lookup began + * @return true if and only if the named property is found in the object + * @see org.mozilla.javascript.Scriptable#get(String, Scriptable) + * @see org.mozilla.javascript.ScriptableObject#getProperty(Scriptable, String) + */ + public boolean has(String name, Scriptable start); + + /** + * Indicates whether or not an indexed property is defined in an object. + * + * Does not traverse the prototype chain.

    + * + * The property is specified by an integral index + * as defined for the get method.

    + * + * @param index the numeric index for the property + * @param start the object in which the lookup began + * @return true if and only if the indexed property is found in the object + * @see org.mozilla.javascript.Scriptable#get(int, Scriptable) + * @see org.mozilla.javascript.ScriptableObject#getProperty(Scriptable, int) + */ + public boolean has(int index, Scriptable start); + + /** + * Sets a named property in this object. + *

    + * The property is specified by a string name + * as defined for get. + *

    + * The possible values that may be passed in are as defined for + * get. A class that implements this method may choose + * to ignore calls to set certain properties, in which case those + * properties are effectively read-only.

    + * For properties defined in a prototype chain, + * use putProperty in ScriptableObject.

    + * Note that if a property a is defined in the prototype p + * of an object o, then evaluating o.a = 23 will cause + * set to be called on the prototype p with + * o as the start parameter. + * To preserve JavaScript semantics, it is the Scriptable + * object's responsibility to modify o.

    + * This design allows properties to be defined in prototypes and implemented + * in terms of getters and setters of Java values without consuming slots + * in each instance. + *

    + * The values that may be set are limited to the following: + *

      + *
    • java.lang.Boolean objects
    • + *
    • java.lang.String objects
    • + *
    • java.lang.Number objects
    • + *
    • org.mozilla.javascript.Scriptable objects
    • + *
    • null
    • + *
    • The value returned by Context.getUndefinedValue()
    • + *

    + * Arbitrary Java objects may be wrapped in a Scriptable by first calling + * Context.toObject. This allows the property of a JavaScript + * object to contain an arbitrary Java object as a value.

    + * Note that has will be called by the runtime first before + * set is called to determine in which object the + * property is defined. + * Note that this method is not expected to traverse the prototype chain, + * which is different from the ECMA [[Put]] operation. + * @param name the name of the property + * @param start the object whose property is being set + * @param value value to set the property to + * @see org.mozilla.javascript.Scriptable#has(String, Scriptable) + * @see org.mozilla.javascript.Scriptable#get(String, Scriptable) + * @see org.mozilla.javascript.ScriptableObject#putProperty(Scriptable, String, Object) + * @see org.mozilla.javascript.Context#toObject(Object, Scriptable) + */ + public void put(String name, Scriptable start, Object value); + + /** + * Sets an indexed property in this object. + *

    + * The property is specified by an integral index + * as defined for get.

    + * + * Identical to put(String, Scriptable, Object) except that + * an integral index is used to select the property. + * + * @param index the numeric index for the property + * @param start the object whose property is being set + * @param value value to set the property to + * @see org.mozilla.javascript.Scriptable#has(int, Scriptable) + * @see org.mozilla.javascript.Scriptable#get(int, Scriptable) + * @see org.mozilla.javascript.ScriptableObject#putProperty(Scriptable, int, Object) + * @see org.mozilla.javascript.Context#toObject(Object, Scriptable) + */ + public void put(int index, Scriptable start, Object value); + + /** + * Removes a property from this object. + * This operation corresponds to the ECMA [[Delete]] except that + * the no result is returned. The runtime will guarantee that this + * method is called only if the property exists. After this method + * is called, the runtime will call Scriptable.has to see if the + * property has been removed in order to determine the boolean + * result of the delete operator as defined by ECMA 11.4.1. + *

    + * A property can be made permanent by ignoring calls to remove + * it.

    + * The property is specified by a String name + * as defined for get. + *

    + * To delete properties defined in a prototype chain, + * see deleteProperty in ScriptableObject. + * @param name the identifier for the property + * @see org.mozilla.javascript.Scriptable#get(String, Scriptable) + * @see org.mozilla.javascript.ScriptableObject#deleteProperty(Scriptable, String) + */ + public void delete(String name); + + /** + * Removes a property from this object. + * + * The property is specified by an integral index + * as defined for get. + *

    + * To delete properties defined in a prototype chain, + * see deleteProperty in ScriptableObject. + * + * Identical to delete(String) except that + * an integral index is used to select the property. + * + * @param index the numeric index for the property + * @see org.mozilla.javascript.Scriptable#get(int, Scriptable) + * @see org.mozilla.javascript.ScriptableObject#deleteProperty(Scriptable, int) + */ + public void delete(int index); + + /** + * Get the prototype of the object. + * @return the prototype + */ + public Scriptable getPrototype(); + + /** + * Set the prototype of the object. + * @param prototype the prototype to set + */ + public void setPrototype(Scriptable prototype); + + /** + * Get the parent scope of the object. + * @return the parent scope + */ + public Scriptable getParentScope(); + + /** + * Set the parent scope of the object. + * @param parent the parent scope to set + */ + public void setParentScope(Scriptable parent); + + /** + * Get an array of property ids. + * + * Not all property ids need be returned. Those properties + * whose ids are not returned are considered non-enumerable. + * + * @return an array of Objects. Each entry in the array is either + * a java.lang.String or a java.lang.Number + */ + public Object[] getIds(); + + /** + * Get the default value of the object with a given hint. + * The hints are String.class for type String, Number.class for type + * Number, Scriptable.class for type Object, and Boolean.class for + * type Boolean.

    + * + * A hint of null means "no hint". + * + * See ECMA 8.6.2.6. + * + * @param hint the type hint + * @return the default value + */ + public Object getDefaultValue(Class hint); + + /** + * The instanceof operator. + * + *

    + * The JavaScript code "lhs instanceof rhs" causes rhs.hasInstance(lhs) to + * be called. + * + *

    + * The return value is implementation dependent so that embedded host objects can + * return an appropriate value. See the JS 1.3 language documentation for more + * detail. + * + *

    This operator corresponds to the proposed EMCA [[HasInstance]] operator. + * + * @param instance The value that appeared on the LHS of the instanceof + * operator + * + * @return an implementation dependent value + */ + public boolean hasInstance(Scriptable instance); +} + diff --git a/java/ql/test/experimental/stubs/rhino-1.7.13/org/mozilla/javascript/ScriptableObject.java b/java/ql/test/experimental/stubs/rhino-1.7.13/org/mozilla/javascript/ScriptableObject.java new file mode 100644 index 00000000000..298c4fc7fb0 --- /dev/null +++ b/java/ql/test/experimental/stubs/rhino-1.7.13/org/mozilla/javascript/ScriptableObject.java @@ -0,0 +1,27 @@ +/* -*- Mode: java; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +// API class + +package org.mozilla.javascript; + +/** + * This is the default implementation of the Scriptable interface. This + * class provides convenient default behavior that makes it easier to + * define host objects. + *

    + * Various properties and methods of JavaScript objects can be conveniently + * defined using methods of ScriptableObject. + *

    + * Classes extending ScriptableObject must define the getClassName method. + * + * @see org.mozilla.javascript.Scriptable + * @author Norris Boyd + */ + +public abstract class ScriptableObject implements Scriptable +{ +} From 852bcfb5c7b35d98c99b0f4539f928fb8385058c Mon Sep 17 00:00:00 2001 From: luchua-bc Date: Fri, 30 Apr 2021 03:46:23 +0000 Subject: [PATCH 160/168] Refactor the ScriptEngine query and the Rhino code injection query into one --- .../Security/CWE/CWE-094/RhinoInjection.qhelp | 51 ---------- .../Security/CWE/CWE-094/RhinoInjection.ql | 17 ---- .../Security/CWE/CWE-094/RhinoInjection.qll | 56 ----------- .../Security/CWE/CWE-094/ScriptEngine.qhelp | 26 ------ .../Security/CWE/CWE-094/ScriptEngine.ql | 51 ---------- .../CWE/CWE-094/ScriptInjection.qhelp | 49 ++++++++++ .../Security/CWE/CWE-094/ScriptInjection.ql | 93 +++++++++++++++++++ .../security/CWE-094/RhinoInjection.expected | 7 -- .../security/CWE-094/RhinoInjection.qlref | 1 - .../security/CWE-094/ScriptEngine.qlref | 1 - ...gine.expected => ScriptInjection.expected} | 12 ++- .../security/CWE-094/ScriptInjection.qlref | 1 + 12 files changed, 151 insertions(+), 214 deletions(-) delete mode 100644 java/ql/src/experimental/Security/CWE/CWE-094/RhinoInjection.qhelp delete mode 100644 java/ql/src/experimental/Security/CWE/CWE-094/RhinoInjection.ql delete mode 100644 java/ql/src/experimental/Security/CWE/CWE-094/RhinoInjection.qll delete mode 100644 java/ql/src/experimental/Security/CWE/CWE-094/ScriptEngine.qhelp delete mode 100644 java/ql/src/experimental/Security/CWE/CWE-094/ScriptEngine.ql create mode 100644 java/ql/src/experimental/Security/CWE/CWE-094/ScriptInjection.qhelp create mode 100644 java/ql/src/experimental/Security/CWE/CWE-094/ScriptInjection.ql delete mode 100644 java/ql/test/experimental/query-tests/security/CWE-094/RhinoInjection.expected delete mode 100644 java/ql/test/experimental/query-tests/security/CWE-094/RhinoInjection.qlref delete mode 100644 java/ql/test/experimental/query-tests/security/CWE-094/ScriptEngine.qlref rename java/ql/test/experimental/query-tests/security/CWE-094/{ScriptEngine.expected => ScriptInjection.expected} (75%) create mode 100644 java/ql/test/experimental/query-tests/security/CWE-094/ScriptInjection.qlref diff --git a/java/ql/src/experimental/Security/CWE/CWE-094/RhinoInjection.qhelp b/java/ql/src/experimental/Security/CWE/CWE-094/RhinoInjection.qhelp deleted file mode 100644 index 73c252ed54c..00000000000 --- a/java/ql/src/experimental/Security/CWE/CWE-094/RhinoInjection.qhelp +++ /dev/null @@ -1,51 +0,0 @@ - - - - -

    -Rhino is a JavaScript engine written fully in Java and managed by the Mozilla Foundation. -It serves as an embedded scripting engine inside Java applications which allows -Java-to-JavaScript interoperability and provides a seamless integration between the two -languages. If an expression is built using attacker-controlled data, and then evaluated in -a powerful context, it may allow the attacker to run arbitrary code. -

    -

    -Typically an expression is evaluated in the powerful context initialized with -initStandardObjects that allows an expression of arbitrary Java code to -execute in the JVM. -

    - - - -

    -In general, including user input in a Rhino expression should be avoided. -If user input must be included in the expression, it should be then evaluated in a safe -context that doesn't allow arbitrary code invocation. -

    -
    - - -

    -The following example shows two ways of using Rhino expression. In the 'BAD' case, -an unsafe context is initialized with initStandardObjects. In the 'GOOD' case, -a safe context is initialized with initSafeStandardObjects or -setClassShutter. -

    - -
    - - -
  • - Mozilla Rhino: - Rhino: JavaScript in Java -
  • -
  • - Rhino Sandbox: - A sandbox to execute JavaScript code with Rhino in Java. -
  • -
  • - GuardRails: - Code Injection -
  • -
    - \ No newline at end of file diff --git a/java/ql/src/experimental/Security/CWE/CWE-094/RhinoInjection.ql b/java/ql/src/experimental/Security/CWE/CWE-094/RhinoInjection.ql deleted file mode 100644 index cac41244aa2..00000000000 --- a/java/ql/src/experimental/Security/CWE/CWE-094/RhinoInjection.ql +++ /dev/null @@ -1,17 +0,0 @@ -/** - * @name Injection in Mozilla Rhino JavaScript Engine - * @description Evaluation of a user-controlled JavaScript or Java expression in Rhino - * JavaScript Engine may lead to remote code execution. - * @kind path-problem - * @id java/rhino-injection - * @tags security - * external/cwe/cwe-094 - */ - -import java -import RhinoInjection -import DataFlow::PathGraph - -from DataFlow::PathNode source, DataFlow::PathNode sink, RhinoInjectionConfig conf -where conf.hasFlowPath(source, sink) -select sink.getNode(), source, sink, "Rhino injection from $@.", source.getNode(), " user input" diff --git a/java/ql/src/experimental/Security/CWE/CWE-094/RhinoInjection.qll b/java/ql/src/experimental/Security/CWE/CWE-094/RhinoInjection.qll deleted file mode 100644 index a560e340e85..00000000000 --- a/java/ql/src/experimental/Security/CWE/CWE-094/RhinoInjection.qll +++ /dev/null @@ -1,56 +0,0 @@ -import java -import semmle.code.java.dataflow.FlowSources -import semmle.code.java.dataflow.TaintTracking - -/** The class `org.mozilla.javascript.Context`. */ -class Context extends RefType { - Context() { this.hasQualifiedName("org.mozilla.javascript", "Context") } -} - -/** - * A method that evaluates a Rhino expression. - */ -class EvaluateExpressionMethod extends Method { - EvaluateExpressionMethod() { - this.getDeclaringType().getAnAncestor*() instanceof Context and - ( - hasName("evaluateString") or - hasName("evaluateReader") - ) - } -} - -/** - * A taint-tracking configuration for unsafe user input that is used to evaluate - * a Rhino expression. - */ -class RhinoInjectionConfig extends TaintTracking::Configuration { - RhinoInjectionConfig() { this = "RhinoInjectionConfig" } - - override predicate isSource(DataFlow::Node source) { - source instanceof RemoteFlowSource - or - source instanceof LocalUserInput - } - - override predicate isSink(DataFlow::Node sink) { sink instanceof EvaluateExpressionSink } -} - -/** - * A sink for Rhino code injection vulnerabilities. - */ -class EvaluateExpressionSink extends DataFlow::ExprNode { - EvaluateExpressionSink() { - exists(MethodAccess ea, EvaluateExpressionMethod m | m = ea.getMethod() | - this.asExpr() = ea.getArgument(1) and // The second argument is the JavaScript or Java input - not exists(MethodAccess ca | - ( - ca.getMethod().hasName("initSafeStandardObjects") // safe mode - or - ca.getMethod().hasName("setClassShutter") // `ClassShutter` constraint is enforced - ) and - ea.getQualifier() = ca.getQualifier().(VarAccess).getVariable().getAnAccess() - ) - ) - } -} diff --git a/java/ql/src/experimental/Security/CWE/CWE-094/ScriptEngine.qhelp b/java/ql/src/experimental/Security/CWE/CWE-094/ScriptEngine.qhelp deleted file mode 100644 index 74159c562c5..00000000000 --- a/java/ql/src/experimental/Security/CWE/CWE-094/ScriptEngine.qhelp +++ /dev/null @@ -1,26 +0,0 @@ - - - - -

    The ScriptEngine API has been available since the release of Java 6. -It allows applications to interact with scripts written in languages such as JavaScript.

    -
    - - -

    Use "Cloudbees Rhino Sandbox" or sandboxing with SecurityManager or use graalvm instead.

    -
    - - -

    The following code could execute random JavaScript code

    - - -
    - - -
  • -CERT coding standard: ScriptEngine code injection -
  • -
    -
    diff --git a/java/ql/src/experimental/Security/CWE/CWE-094/ScriptEngine.ql b/java/ql/src/experimental/Security/CWE/CWE-094/ScriptEngine.ql deleted file mode 100644 index 5e52a61b5c3..00000000000 --- a/java/ql/src/experimental/Security/CWE/CWE-094/ScriptEngine.ql +++ /dev/null @@ -1,51 +0,0 @@ -/** - * @name ScriptEngine evaluation - * @description Malicious Javascript code could cause arbitrary command execution at the OS level - * @kind path-problem - * @problem.severity error - * @precision high - * @id java/unsafe-eval - * @tags security - * external/cwe/cwe-094 - */ - -import java -import semmle.code.java.dataflow.FlowSources -import DataFlow::PathGraph - -class ScriptEngineMethod extends Method { - ScriptEngineMethod() { - this.getDeclaringType().getASupertype*().hasQualifiedName("javax.script", "ScriptEngine") and - this.hasName("eval") - } -} - -predicate scriptEngine(MethodAccess ma, Expr sink) { - exists(Method m | m = ma.getMethod() | - m instanceof ScriptEngineMethod and - sink = ma.getArgument(0) - ) -} - -class ScriptEngineSink extends DataFlow::ExprNode { - ScriptEngineSink() { scriptEngine(_, this.getExpr()) } - - MethodAccess getMethodAccess() { scriptEngine(result, this.getExpr()) } -} - -class ScriptEngineConfiguration extends TaintTracking::Configuration { - ScriptEngineConfiguration() { this = "ScriptEngineConfiguration" } - - override predicate isSource(DataFlow::Node source) { - source instanceof RemoteFlowSource - or - source instanceof LocalUserInput - } - - override predicate isSink(DataFlow::Node sink) { sink instanceof ScriptEngineSink } -} - -from DataFlow::PathNode source, DataFlow::PathNode sink, ScriptEngineConfiguration conf -where conf.hasFlowPath(source, sink) -select sink.getNode().(ScriptEngineSink).getMethodAccess(), source, sink, "ScriptEngine eval $@.", - source.getNode(), "user input" diff --git a/java/ql/src/experimental/Security/CWE/CWE-094/ScriptInjection.qhelp b/java/ql/src/experimental/Security/CWE/CWE-094/ScriptInjection.qhelp new file mode 100644 index 00000000000..ddc011fa658 --- /dev/null +++ b/java/ql/src/experimental/Security/CWE/CWE-094/ScriptInjection.qhelp @@ -0,0 +1,49 @@ + + + + +

    The JavaScript Engine API has been available since the release of Java 6, which allows + applications to interact with scripts written in languages such as JavaScript. It serves + as an embedded scripting engine inside Java applications which allows Java-to-JavaScript + interoperability and provides a seamless integration between the two languages. If an + expression is built using attacker-controlled data, and then evaluated in a powerful + context, it may allow the attacker to run arbitrary code.

    +
    + + +

    In general, including user input in a JavaScript Engine expression should be avoided. + If user input must be included in the expression, it should be then evaluated in a safe + context that doesn't allow arbitrary code invocation. Use "Cloudbees Rhino Sandbox" or + sandboxing with SecurityManager or use graalvm + instead.

    +
    + + +

    The following code could execute random JavaScript code in ScriptEngine

    + + + +

    The following example shows two ways of using Rhino expression. In the 'BAD' case, + an unsafe context is initialized with initStandardObjects that allows arbitrary + Java code to be executed. In the 'GOOD' case, a safe context is initialized with + initSafeStandardObjects or setClassShutter.

    + +
    + + +
  • +CERT coding standard: ScriptEngine code injection +
  • +
  • + Mozilla Rhino: Rhino: JavaScript in Java +
  • +
  • + Rhino Sandbox: A sandbox to execute JavaScript code with Rhino in Java +
  • +
  • + GuardRails: Code Injection +
  • +
    +
    diff --git a/java/ql/src/experimental/Security/CWE/CWE-094/ScriptInjection.ql b/java/ql/src/experimental/Security/CWE/CWE-094/ScriptInjection.ql new file mode 100644 index 00000000000..35f73fd5270 --- /dev/null +++ b/java/ql/src/experimental/Security/CWE/CWE-094/ScriptInjection.ql @@ -0,0 +1,93 @@ +/** + * @name Injection in JavaScript Engine + * @description Evaluation of a user-controlled malicious JavaScript or Java expression in + * JavaScript Engine may lead to remote code execution. + * @kind path-problem + * @problem.severity error + * @precision high + * @id java/unsafe-eval + * @tags security + * external/cwe/cwe-094 + */ + +import java +import semmle.code.java.dataflow.FlowSources +import DataFlow::PathGraph + +class ScriptEngineMethod extends Method { + ScriptEngineMethod() { + this.getDeclaringType().getASupertype*().hasQualifiedName("javax.script", "ScriptEngine") and + this.hasName("eval") + } +} + +/** The context class `org.mozilla.javascript.Context` of Rhino JavaScript Engine. */ +class RhinoContext extends RefType { + RhinoContext() { this.hasQualifiedName("org.mozilla.javascript", "Context") } +} + +/** + * A method that evaluates a Rhino expression. + */ +class RhinoEvaluateExpressionMethod extends Method { + RhinoEvaluateExpressionMethod() { + this.getDeclaringType().getAnAncestor*() instanceof RhinoContext and + ( + hasName("evaluateString") or + hasName("evaluateReader") + ) + } +} + +predicate scriptEngine(MethodAccess ma, Expr sink) { + exists(Method m | m = ma.getMethod() | + m instanceof ScriptEngineMethod and + sink = ma.getArgument(0) + ) +} + +/** + * Holds if `ma` has Rhino code injection vulnerabilities. + */ +predicate evaluateRhinoExpression(MethodAccess ma, Expr sink) { + exists(RhinoEvaluateExpressionMethod m | m = ma.getMethod() | + sink = ma.getArgument(1) and // The second argument is the JavaScript or Java input + not exists(MethodAccess ca | + ( + ca.getMethod().hasName("initSafeStandardObjects") // safe mode + or + ca.getMethod().hasName("setClassShutter") // `ClassShutter` constraint is enforced + ) and + ma.getQualifier() = ca.getQualifier().(VarAccess).getVariable().getAnAccess() + ) + ) +} + +class ScriptInjectionSink extends DataFlow::ExprNode { + ScriptInjectionSink() { + scriptEngine(_, this.getExpr()) or + evaluateRhinoExpression(_, this.getExpr()) + } + + MethodAccess getMethodAccess() { + scriptEngine(result, this.getExpr()) or + evaluateRhinoExpression(result, this.getExpr()) + } +} + +class ScriptInjectionConfiguration extends TaintTracking::Configuration { + ScriptInjectionConfiguration() { this = "ScriptInjectionConfiguration" } + + override predicate isSource(DataFlow::Node source) { + source instanceof RemoteFlowSource + or + source instanceof LocalUserInput + } + + override predicate isSink(DataFlow::Node sink) { sink instanceof ScriptInjectionSink } +} + +from DataFlow::PathNode source, DataFlow::PathNode sink, ScriptInjectionConfiguration conf +where conf.hasFlowPath(source, sink) +select sink.getNode().(ScriptInjectionSink).getMethodAccess(), source, sink, + "JavaScript Engine evaluate $@.", source.getNode(), "user input" diff --git a/java/ql/test/experimental/query-tests/security/CWE-094/RhinoInjection.expected b/java/ql/test/experimental/query-tests/security/CWE-094/RhinoInjection.expected deleted file mode 100644 index 4d2736c8230..00000000000 --- a/java/ql/test/experimental/query-tests/security/CWE-094/RhinoInjection.expected +++ /dev/null @@ -1,7 +0,0 @@ -edges -| RhinoServlet.java:25:23:25:50 | getParameter(...) : String | RhinoServlet.java:29:55:29:58 | code | -nodes -| RhinoServlet.java:25:23:25:50 | getParameter(...) : String | semmle.label | getParameter(...) : String | -| RhinoServlet.java:29:55:29:58 | code | semmle.label | code | -#select -| RhinoServlet.java:29:55:29:58 | code | RhinoServlet.java:25:23:25:50 | getParameter(...) : String | RhinoServlet.java:29:55:29:58 | code | Rhino injection from $@. | RhinoServlet.java:25:23:25:50 | getParameter(...) | user input | diff --git a/java/ql/test/experimental/query-tests/security/CWE-094/RhinoInjection.qlref b/java/ql/test/experimental/query-tests/security/CWE-094/RhinoInjection.qlref deleted file mode 100644 index e306dda44df..00000000000 --- a/java/ql/test/experimental/query-tests/security/CWE-094/RhinoInjection.qlref +++ /dev/null @@ -1 +0,0 @@ -experimental/Security/CWE/CWE-094/RhinoInjection.ql \ No newline at end of file diff --git a/java/ql/test/experimental/query-tests/security/CWE-094/ScriptEngine.qlref b/java/ql/test/experimental/query-tests/security/CWE-094/ScriptEngine.qlref deleted file mode 100644 index 9566c986778..00000000000 --- a/java/ql/test/experimental/query-tests/security/CWE-094/ScriptEngine.qlref +++ /dev/null @@ -1 +0,0 @@ -experimental/Security/CWE/CWE-094/ScriptEngine.ql diff --git a/java/ql/test/experimental/query-tests/security/CWE-094/ScriptEngine.expected b/java/ql/test/experimental/query-tests/security/CWE-094/ScriptInjection.expected similarity index 75% rename from java/ql/test/experimental/query-tests/security/CWE-094/ScriptEngine.expected rename to java/ql/test/experimental/query-tests/security/CWE-094/ScriptInjection.expected index a65faafc6bd..452c2d70c57 100644 --- a/java/ql/test/experimental/query-tests/security/CWE-094/ScriptEngine.expected +++ b/java/ql/test/experimental/query-tests/security/CWE-094/ScriptInjection.expected @@ -1,4 +1,5 @@ edges +| RhinoServlet.java:25:23:25:50 | getParameter(...) : String | RhinoServlet.java:29:55:29:58 | code | | ScriptEngineTest.java:8:44:8:55 | input : String | ScriptEngineTest.java:12:37:12:41 | input | | ScriptEngineTest.java:15:51:15:62 | input : String | ScriptEngineTest.java:19:31:19:35 | input | | ScriptEngineTest.java:23:58:23:69 | input : String | ScriptEngineTest.java:27:31:27:35 | input | @@ -12,6 +13,8 @@ edges | ScriptEngineTest.java:40:70:40:76 | ...[...] : String | ScriptEngineTest.java:23:58:23:69 | input : String | | ScriptEngineTest.java:41:58:41:64 | ...[...] : String | ScriptEngineTest.java:30:46:30:57 | input : String | nodes +| RhinoServlet.java:25:23:25:50 | getParameter(...) : String | semmle.label | getParameter(...) : String | +| RhinoServlet.java:29:55:29:58 | code | semmle.label | code | | ScriptEngineTest.java:8:44:8:55 | input : String | semmle.label | input : String | | ScriptEngineTest.java:12:37:12:41 | input | semmle.label | input | | ScriptEngineTest.java:15:51:15:62 | input : String | semmle.label | input : String | @@ -26,7 +29,8 @@ nodes | ScriptEngineTest.java:40:70:40:76 | ...[...] : String | semmle.label | ...[...] : String | | ScriptEngineTest.java:41:58:41:64 | ...[...] : String | semmle.label | ...[...] : String | #select -| ScriptEngineTest.java:12:19:12:42 | eval(...) | ScriptEngineTest.java:37:26:37:38 | args : String[] | ScriptEngineTest.java:12:37:12:41 | input | ScriptEngine eval $@. | ScriptEngineTest.java:37:26:37:38 | args | user input | -| ScriptEngineTest.java:19:19:19:36 | eval(...) | ScriptEngineTest.java:37:26:37:38 | args : String[] | ScriptEngineTest.java:19:31:19:35 | input | ScriptEngine eval $@. | ScriptEngineTest.java:37:26:37:38 | args | user input | -| ScriptEngineTest.java:27:19:27:36 | eval(...) | ScriptEngineTest.java:37:26:37:38 | args : String[] | ScriptEngineTest.java:27:31:27:35 | input | ScriptEngine eval $@. | ScriptEngineTest.java:37:26:37:38 | args | user input | -| ScriptEngineTest.java:34:19:34:36 | eval(...) | ScriptEngineTest.java:37:26:37:38 | args : String[] | ScriptEngineTest.java:34:31:34:35 | input | ScriptEngine eval $@. | ScriptEngineTest.java:37:26:37:38 | args | user input | +| RhinoServlet.java:29:29:29:78 | evaluateString(...) | RhinoServlet.java:25:23:25:50 | getParameter(...) : String | RhinoServlet.java:29:55:29:58 | code | JavaScript Engine evaluate $@. | RhinoServlet.java:25:23:25:50 | getParameter(...) | user input | +| ScriptEngineTest.java:12:19:12:42 | eval(...) | ScriptEngineTest.java:37:26:37:38 | args : String[] | ScriptEngineTest.java:12:37:12:41 | input | JavaScript Engine evaluate $@. | ScriptEngineTest.java:37:26:37:38 | args | user input | +| ScriptEngineTest.java:19:19:19:36 | eval(...) | ScriptEngineTest.java:37:26:37:38 | args : String[] | ScriptEngineTest.java:19:31:19:35 | input | JavaScript Engine evaluate $@. | ScriptEngineTest.java:37:26:37:38 | args | user input | +| ScriptEngineTest.java:27:19:27:36 | eval(...) | ScriptEngineTest.java:37:26:37:38 | args : String[] | ScriptEngineTest.java:27:31:27:35 | input | JavaScript Engine evaluate $@. | ScriptEngineTest.java:37:26:37:38 | args | user input | +| ScriptEngineTest.java:34:19:34:36 | eval(...) | ScriptEngineTest.java:37:26:37:38 | args : String[] | ScriptEngineTest.java:34:31:34:35 | input | JavaScript Engine evaluate $@. | ScriptEngineTest.java:37:26:37:38 | args | user input | diff --git a/java/ql/test/experimental/query-tests/security/CWE-094/ScriptInjection.qlref b/java/ql/test/experimental/query-tests/security/CWE-094/ScriptInjection.qlref new file mode 100644 index 00000000000..da2b4287d0c --- /dev/null +++ b/java/ql/test/experimental/query-tests/security/CWE-094/ScriptInjection.qlref @@ -0,0 +1 @@ +experimental/Security/CWE/CWE-094/ScriptInjection.ql \ No newline at end of file From d664aa6d6add71d2b0d9287dcae665f8ea7df91f Mon Sep 17 00:00:00 2001 From: luchua-bc Date: Wed, 5 May 2021 14:21:52 +0000 Subject: [PATCH 161/168] Include more scenarios and update qldoc --- .../CWE/CWE-094/ScriptInjection.qhelp | 15 ++- .../Security/CWE/CWE-094/ScriptInjection.ql | 86 +++++++++++--- .../security/CWE-094/RhinoServlet.java | 16 +++ .../security/CWE-094/ScriptEngineTest.java | 39 +++++- .../security/CWE-094/ScriptInjection.expected | 64 ++++++---- .../mozilla/javascript/CompilerEnvirons.java | 12 ++ .../org/mozilla/javascript/Context.java | 72 +++++++++++ .../javascript/DefiningClassLoader.java | 36 ++++++ .../org/mozilla/javascript/Function.java | 46 +++++++ .../javascript/GeneratedClassLoader.java | 34 ++++++ .../org/mozilla/javascript/Script.java | 41 +++++++ .../javascript/optimizer/ClassCompiler.java | 112 ++++++++++++++++++ .../scriptengine/javax/script/Bindings.java | 14 +++ .../scriptengine/javax/script/Compilable.java | 9 ++ .../javax/script/CompiledScript.java | 17 +++ .../javax/script/ScriptEngine.java | 2 + .../javax/script/ScriptEngineFactory.java | 7 +- .../nashorn/api/scripting/ClassFilter.java | 5 + .../api/scripting/NashornScriptEngine.java | 27 ++++- .../scripting/NashornScriptEngineFactory.java | 34 +++++- 20 files changed, 633 insertions(+), 55 deletions(-) create mode 100644 java/ql/test/experimental/stubs/rhino-1.7.13/org/mozilla/javascript/CompilerEnvirons.java create mode 100644 java/ql/test/experimental/stubs/rhino-1.7.13/org/mozilla/javascript/DefiningClassLoader.java create mode 100644 java/ql/test/experimental/stubs/rhino-1.7.13/org/mozilla/javascript/Function.java create mode 100644 java/ql/test/experimental/stubs/rhino-1.7.13/org/mozilla/javascript/GeneratedClassLoader.java create mode 100644 java/ql/test/experimental/stubs/rhino-1.7.13/org/mozilla/javascript/Script.java create mode 100644 java/ql/test/experimental/stubs/rhino-1.7.13/org/mozilla/javascript/optimizer/ClassCompiler.java create mode 100644 java/ql/test/stubs/scriptengine/javax/script/Bindings.java create mode 100644 java/ql/test/stubs/scriptengine/javax/script/Compilable.java create mode 100644 java/ql/test/stubs/scriptengine/javax/script/CompiledScript.java create mode 100644 java/ql/test/stubs/scriptengine/jdk/nashorn/api/scripting/ClassFilter.java diff --git a/java/ql/src/experimental/Security/CWE/CWE-094/ScriptInjection.qhelp b/java/ql/src/experimental/Security/CWE/CWE-094/ScriptInjection.qhelp index ddc011fa658..586304ebb7c 100644 --- a/java/ql/src/experimental/Security/CWE/CWE-094/ScriptInjection.qhelp +++ b/java/ql/src/experimental/Security/CWE/CWE-094/ScriptInjection.qhelp @@ -4,7 +4,7 @@ -

    The JavaScript Engine API has been available since the release of Java 6, which allows +

    The Java Scripting API has been available since the release of Java 6, which allows applications to interact with scripts written in languages such as JavaScript. It serves as an embedded scripting engine inside Java applications which allows Java-to-JavaScript interoperability and provides a seamless integration between the two languages. If an @@ -13,11 +13,11 @@ -

    In general, including user input in a JavaScript Engine expression should be avoided. +

    In general, including user input in a Java Script Engine expression should be avoided. If user input must be included in the expression, it should be then evaluated in a safe - context that doesn't allow arbitrary code invocation. Use "Cloudbees Rhino Sandbox" or - sandboxing with SecurityManager or use graalvm - instead.

    + context that doesn't allow arbitrary code invocation. Use "Cloudbees Rhino Sandbox" or + sandboxing with SecurityManager, which will be deprecated in a future release, or use + GraalVM instead.

    @@ -36,6 +36,9 @@
  • CERT coding standard: ScriptEngine code injection
  • +
  • +GraalVM: Secure by Default +
  • Mozilla Rhino: Rhino: JavaScript in Java
  • @@ -43,7 +46,7 @@ CERT coding standard: A sandbox to execute JavaScript code with Rhino in Java
  • - GuardRails: Code Injection + GuardRails: Code Injection
  • diff --git a/java/ql/src/experimental/Security/CWE/CWE-094/ScriptInjection.ql b/java/ql/src/experimental/Security/CWE/CWE-094/ScriptInjection.ql index 35f73fd5270..d4f8cfa5370 100644 --- a/java/ql/src/experimental/Security/CWE/CWE-094/ScriptInjection.ql +++ b/java/ql/src/experimental/Security/CWE/CWE-094/ScriptInjection.ql @@ -1,7 +1,7 @@ /** - * @name Injection in JavaScript Engine + * @name Injection in Java Script Engine * @description Evaluation of a user-controlled malicious JavaScript or Java expression in - * JavaScript Engine may lead to remote code execution. + * Java Script Engine may lead to remote code execution. * @kind path-problem * @problem.severity error * @precision high @@ -14,31 +14,62 @@ import java import semmle.code.java.dataflow.FlowSources import DataFlow::PathGraph +/** A method of ScriptEngine that allows code injection. */ class ScriptEngineMethod extends Method { ScriptEngineMethod() { this.getDeclaringType().getASupertype*().hasQualifiedName("javax.script", "ScriptEngine") and this.hasName("eval") + or + this.getDeclaringType().getASupertype*().hasQualifiedName("javax.script", "Compilable") and + this.hasName("compile") + or + this.getDeclaringType().getASupertype*().hasQualifiedName("javax.script", "ScriptEngineFactory") and + this.hasName(["getProgram", "getMethodCallSyntax"]) } } -/** The context class `org.mozilla.javascript.Context` of Rhino JavaScript Engine. */ +/** The context class `org.mozilla.javascript.Context` of Rhino Java Script Engine. */ class RhinoContext extends RefType { RhinoContext() { this.hasQualifiedName("org.mozilla.javascript", "Context") } } -/** - * A method that evaluates a Rhino expression. - */ +/** A method that evaluates a Rhino expression with `org.mozilla.javascript.Context`. */ class RhinoEvaluateExpressionMethod extends Method { RhinoEvaluateExpressionMethod() { this.getDeclaringType().getAnAncestor*() instanceof RhinoContext and - ( - hasName("evaluateString") or - hasName("evaluateReader") - ) + this.hasName([ + "evaluateString", "evaluateReader", "compileFunction", "compileReader", "compileString" + ]) } } +/** + * A method that compiles a Rhino expression with + * `org.mozilla.javascript.optimizer.ClassCompiler`. + */ +class RhinoCompileClassMethod extends Method { + RhinoCompileClassMethod() { + this.getDeclaringType() + .getASupertype*() + .hasQualifiedName("org.mozilla.javascript.optimizer", "ClassCompiler") and + this.hasName("compileToClassFiles") + } +} + +/** + * A method that defines a Java class from a Rhino expression with + * `org.mozilla.javascript.GeneratedClassLoader`. + */ +class RhinoDefineClassMethod extends Method { + RhinoDefineClassMethod() { + this.getDeclaringType() + .getASupertype*() + .hasQualifiedName("org.mozilla.javascript", "GeneratedClassLoader") and + this.hasName("defineClass") + } +} + +/** Holds if `ma` is a method access of `ScriptEngineMethod`. */ predicate scriptEngine(MethodAccess ma, Expr sink) { exists(Method m | m = ma.getMethod() | m instanceof ScriptEngineMethod and @@ -47,11 +78,17 @@ predicate scriptEngine(MethodAccess ma, Expr sink) { } /** - * Holds if `ma` has Rhino code injection vulnerabilities. + * Holds if a Rhino expression evaluation method has the code injection vulnerability. */ predicate evaluateRhinoExpression(MethodAccess ma, Expr sink) { exists(RhinoEvaluateExpressionMethod m | m = ma.getMethod() | - sink = ma.getArgument(1) and // The second argument is the JavaScript or Java input + ( + sink = ma.getArgument(1) and // The second argument is the JavaScript or Java input + not ma.getMethod().getName() = "compileReader" + or + sink = ma.getArgument(0) and // The first argument is the input reader + ma.getMethod().getName() = "compileReader" + ) and not exists(MethodAccess ca | ( ca.getMethod().hasName("initSafeStandardObjects") // safe mode @@ -63,15 +100,34 @@ predicate evaluateRhinoExpression(MethodAccess ma, Expr sink) { ) } +/** + * Holds if a Rhino expression compilation method has the code injection vulnerability. + */ +predicate compileScript(MethodAccess ma, Expr sink) { + exists(RhinoCompileClassMethod m | m = ma.getMethod() | sink = ma.getArgument(0)) +} + +/** + * Holds if a Rhino class loading method has the code injection vulnerability. + */ +predicate defineClass(MethodAccess ma, Expr sink) { + exists(RhinoDefineClassMethod m | m = ma.getMethod() | sink = ma.getArgument(1)) +} + +/** A sink of script injection. */ class ScriptInjectionSink extends DataFlow::ExprNode { ScriptInjectionSink() { scriptEngine(_, this.getExpr()) or - evaluateRhinoExpression(_, this.getExpr()) + evaluateRhinoExpression(_, this.getExpr()) or + compileScript(_, this.getExpr()) or + defineClass(_, this.getExpr()) } MethodAccess getMethodAccess() { scriptEngine(result, this.getExpr()) or - evaluateRhinoExpression(result, this.getExpr()) + evaluateRhinoExpression(result, this.getExpr()) or + compileScript(result, this.getExpr()) or + defineClass(result, this.getExpr()) } } @@ -90,4 +146,4 @@ class ScriptInjectionConfiguration extends TaintTracking::Configuration { from DataFlow::PathNode source, DataFlow::PathNode sink, ScriptInjectionConfiguration conf where conf.hasFlowPath(source, sink) select sink.getNode().(ScriptInjectionSink).getMethodAccess(), source, sink, - "JavaScript Engine evaluate $@.", source.getNode(), "user input" + "Java Script Engine evaluate $@.", source.getNode(), "user input" diff --git a/java/ql/test/experimental/query-tests/security/CWE-094/RhinoServlet.java b/java/ql/test/experimental/query-tests/security/CWE-094/RhinoServlet.java index f6f529785cc..2c863b6f62f 100644 --- a/java/ql/test/experimental/query-tests/security/CWE-094/RhinoServlet.java +++ b/java/ql/test/experimental/query-tests/security/CWE-094/RhinoServlet.java @@ -5,9 +5,12 @@ import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import org.mozilla.javascript.ClassShutter; +import org.mozilla.javascript.CompilerEnvirons; import org.mozilla.javascript.Context; +import org.mozilla.javascript.DefiningClassLoader; import org.mozilla.javascript.Scriptable; import org.mozilla.javascript.RhinoException; +import org.mozilla.javascript.optimizer.ClassCompiler; /** * Servlet implementation class RhinoServlet @@ -75,4 +78,17 @@ public class RhinoServlet extends HttpServlet { Context.exit(); } } + + // BAD: allow arbitrary code to be compiled for subsequent execution + protected void doGet2(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { + String code = request.getParameter("code"); + ClassCompiler compiler = new ClassCompiler(new CompilerEnvirons()); + Object[] objs = compiler.compileToClassFiles(code, "/sourceLocation", 1, "mainClassName"); + } + + // BAD: allow arbitrary code to be loaded for subsequent execution + protected void doPost2(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { + String code = request.getParameter("code"); + Class clazz = new DefiningClassLoader().defineClass("Powerfunc", code.getBytes()); + } } diff --git a/java/ql/test/experimental/query-tests/security/CWE-094/ScriptEngineTest.java b/java/ql/test/experimental/query-tests/security/CWE-094/ScriptEngineTest.java index e04ec615f30..42d9cad6e64 100755 --- a/java/ql/test/experimental/query-tests/security/CWE-094/ScriptEngineTest.java +++ b/java/ql/test/experimental/query-tests/security/CWE-094/ScriptEngineTest.java @@ -33,26 +33,53 @@ public class ScriptEngineTest { MyCustomScriptEngine engine = (MyCustomScriptEngine) factory.getScriptEngine(new String[] { "-scripting" }); Object result = engine.eval(input); } + + public void testScriptEngineCompilable(String input) throws ScriptException { + NashornScriptEngineFactory factory = new NashornScriptEngineFactory(); + Compilable engine = (Compilable) factory.getScriptEngine(new String[] { "-scripting" }); + CompiledScript script = engine.compile(input); + Object result = script.eval(); + } + + public void testScriptEngineGetProgram(String input) throws ScriptException { + ScriptEngineManager scriptEngineManager = new ScriptEngineManager(); + ScriptEngine engine = scriptEngineManager.getEngineByName("nashorn"); + String program = engine.getFactory().getProgram(input); + Object result = engine.eval(program); + } public static void main(String[] args) throws ScriptException { new ScriptEngineTest().testWithScriptEngineReference(args[0]); new ScriptEngineTest().testNashornWithScriptEngineReference(args[0]); new ScriptEngineTest().testNashornWithNashornScriptEngineReference(args[0]); new ScriptEngineTest().testCustomScriptEngineReference(args[0]); + new ScriptEngineTest().testScriptEngineCompilable(args[0]); + new ScriptEngineTest().testScriptEngineGetProgram(args[0]); } private static class MyCustomScriptEngine extends AbstractScriptEngine { - public Object eval(String var1) throws ScriptException { - return null; - } + public Object eval(String var1) throws ScriptException { return null; } + + @Override + public ScriptEngineFactory getFactory() { return null; } } private static class MyCustomFactory implements ScriptEngineFactory { public MyCustomFactory() { - } - - public ScriptEngine getScriptEngine() { return null; } + } + + @Override + public ScriptEngine getScriptEngine() { return null; } public ScriptEngine getScriptEngine(String... args) { return null; } + + @Override + public String getEngineName() { return null; } + + @Override + public String getMethodCallSyntax(final String obj, final String method, final String... args) { return null; } + + @Override + public String getProgram(final String... statements) { return null; } } } diff --git a/java/ql/test/experimental/query-tests/security/CWE-094/ScriptInjection.expected b/java/ql/test/experimental/query-tests/security/CWE-094/ScriptInjection.expected index 452c2d70c57..0c191a4f579 100644 --- a/java/ql/test/experimental/query-tests/security/CWE-094/ScriptInjection.expected +++ b/java/ql/test/experimental/query-tests/security/CWE-094/ScriptInjection.expected @@ -1,20 +1,32 @@ edges -| RhinoServlet.java:25:23:25:50 | getParameter(...) : String | RhinoServlet.java:29:55:29:58 | code | +| RhinoServlet.java:28:23:28:50 | getParameter(...) : String | RhinoServlet.java:32:55:32:58 | code | +| RhinoServlet.java:84:23:84:50 | getParameter(...) : String | RhinoServlet.java:86:54:86:57 | code | +| RhinoServlet.java:91:23:91:50 | getParameter(...) : String | RhinoServlet.java:92:74:92:88 | getBytes(...) | | ScriptEngineTest.java:8:44:8:55 | input : String | ScriptEngineTest.java:12:37:12:41 | input | | ScriptEngineTest.java:15:51:15:62 | input : String | ScriptEngineTest.java:19:31:19:35 | input | | ScriptEngineTest.java:23:58:23:69 | input : String | ScriptEngineTest.java:27:31:27:35 | input | | ScriptEngineTest.java:30:46:30:57 | input : String | ScriptEngineTest.java:34:31:34:35 | input | -| ScriptEngineTest.java:37:26:37:38 | args : String[] | ScriptEngineTest.java:38:56:38:62 | ...[...] : String | -| ScriptEngineTest.java:37:26:37:38 | args : String[] | ScriptEngineTest.java:39:63:39:69 | ...[...] : String | -| ScriptEngineTest.java:37:26:37:38 | args : String[] | ScriptEngineTest.java:40:70:40:76 | ...[...] : String | -| ScriptEngineTest.java:37:26:37:38 | args : String[] | ScriptEngineTest.java:41:58:41:64 | ...[...] : String | -| ScriptEngineTest.java:38:56:38:62 | ...[...] : String | ScriptEngineTest.java:8:44:8:55 | input : String | -| ScriptEngineTest.java:39:63:39:69 | ...[...] : String | ScriptEngineTest.java:15:51:15:62 | input : String | -| ScriptEngineTest.java:40:70:40:76 | ...[...] : String | ScriptEngineTest.java:23:58:23:69 | input : String | -| ScriptEngineTest.java:41:58:41:64 | ...[...] : String | ScriptEngineTest.java:30:46:30:57 | input : String | +| ScriptEngineTest.java:37:41:37:52 | input : String | ScriptEngineTest.java:40:42:40:46 | input | +| ScriptEngineTest.java:44:41:44:52 | input : String | ScriptEngineTest.java:47:51:47:55 | input | +| ScriptEngineTest.java:51:26:51:38 | args : String[] | ScriptEngineTest.java:52:56:52:62 | ...[...] : String | +| ScriptEngineTest.java:51:26:51:38 | args : String[] | ScriptEngineTest.java:53:63:53:69 | ...[...] : String | +| ScriptEngineTest.java:51:26:51:38 | args : String[] | ScriptEngineTest.java:54:70:54:76 | ...[...] : String | +| ScriptEngineTest.java:51:26:51:38 | args : String[] | ScriptEngineTest.java:55:58:55:64 | ...[...] : String | +| ScriptEngineTest.java:51:26:51:38 | args : String[] | ScriptEngineTest.java:56:53:56:59 | ...[...] : String | +| ScriptEngineTest.java:51:26:51:38 | args : String[] | ScriptEngineTest.java:57:53:57:59 | ...[...] : String | +| ScriptEngineTest.java:52:56:52:62 | ...[...] : String | ScriptEngineTest.java:8:44:8:55 | input : String | +| ScriptEngineTest.java:53:63:53:69 | ...[...] : String | ScriptEngineTest.java:15:51:15:62 | input : String | +| ScriptEngineTest.java:54:70:54:76 | ...[...] : String | ScriptEngineTest.java:23:58:23:69 | input : String | +| ScriptEngineTest.java:55:58:55:64 | ...[...] : String | ScriptEngineTest.java:30:46:30:57 | input : String | +| ScriptEngineTest.java:56:53:56:59 | ...[...] : String | ScriptEngineTest.java:37:41:37:52 | input : String | +| ScriptEngineTest.java:57:53:57:59 | ...[...] : String | ScriptEngineTest.java:44:41:44:52 | input : String | nodes -| RhinoServlet.java:25:23:25:50 | getParameter(...) : String | semmle.label | getParameter(...) : String | -| RhinoServlet.java:29:55:29:58 | code | semmle.label | code | +| RhinoServlet.java:28:23:28:50 | getParameter(...) : String | semmle.label | getParameter(...) : String | +| RhinoServlet.java:32:55:32:58 | code | semmle.label | code | +| RhinoServlet.java:84:23:84:50 | getParameter(...) : String | semmle.label | getParameter(...) : String | +| RhinoServlet.java:86:54:86:57 | code | semmle.label | code | +| RhinoServlet.java:91:23:91:50 | getParameter(...) : String | semmle.label | getParameter(...) : String | +| RhinoServlet.java:92:74:92:88 | getBytes(...) | semmle.label | getBytes(...) | | ScriptEngineTest.java:8:44:8:55 | input : String | semmle.label | input : String | | ScriptEngineTest.java:12:37:12:41 | input | semmle.label | input | | ScriptEngineTest.java:15:51:15:62 | input : String | semmle.label | input : String | @@ -23,14 +35,24 @@ nodes | ScriptEngineTest.java:27:31:27:35 | input | semmle.label | input | | ScriptEngineTest.java:30:46:30:57 | input : String | semmle.label | input : String | | ScriptEngineTest.java:34:31:34:35 | input | semmle.label | input | -| ScriptEngineTest.java:37:26:37:38 | args : String[] | semmle.label | args : String[] | -| ScriptEngineTest.java:38:56:38:62 | ...[...] : String | semmle.label | ...[...] : String | -| ScriptEngineTest.java:39:63:39:69 | ...[...] : String | semmle.label | ...[...] : String | -| ScriptEngineTest.java:40:70:40:76 | ...[...] : String | semmle.label | ...[...] : String | -| ScriptEngineTest.java:41:58:41:64 | ...[...] : String | semmle.label | ...[...] : String | +| ScriptEngineTest.java:37:41:37:52 | input : String | semmle.label | input : String | +| ScriptEngineTest.java:40:42:40:46 | input | semmle.label | input | +| ScriptEngineTest.java:44:41:44:52 | input : String | semmle.label | input : String | +| ScriptEngineTest.java:47:51:47:55 | input | semmle.label | input | +| ScriptEngineTest.java:51:26:51:38 | args : String[] | semmle.label | args : String[] | +| ScriptEngineTest.java:52:56:52:62 | ...[...] : String | semmle.label | ...[...] : String | +| ScriptEngineTest.java:53:63:53:69 | ...[...] : String | semmle.label | ...[...] : String | +| ScriptEngineTest.java:54:70:54:76 | ...[...] : String | semmle.label | ...[...] : String | +| ScriptEngineTest.java:55:58:55:64 | ...[...] : String | semmle.label | ...[...] : String | +| ScriptEngineTest.java:56:53:56:59 | ...[...] : String | semmle.label | ...[...] : String | +| ScriptEngineTest.java:57:53:57:59 | ...[...] : String | semmle.label | ...[...] : String | #select -| RhinoServlet.java:29:29:29:78 | evaluateString(...) | RhinoServlet.java:25:23:25:50 | getParameter(...) : String | RhinoServlet.java:29:55:29:58 | code | JavaScript Engine evaluate $@. | RhinoServlet.java:25:23:25:50 | getParameter(...) | user input | -| ScriptEngineTest.java:12:19:12:42 | eval(...) | ScriptEngineTest.java:37:26:37:38 | args : String[] | ScriptEngineTest.java:12:37:12:41 | input | JavaScript Engine evaluate $@. | ScriptEngineTest.java:37:26:37:38 | args | user input | -| ScriptEngineTest.java:19:19:19:36 | eval(...) | ScriptEngineTest.java:37:26:37:38 | args : String[] | ScriptEngineTest.java:19:31:19:35 | input | JavaScript Engine evaluate $@. | ScriptEngineTest.java:37:26:37:38 | args | user input | -| ScriptEngineTest.java:27:19:27:36 | eval(...) | ScriptEngineTest.java:37:26:37:38 | args : String[] | ScriptEngineTest.java:27:31:27:35 | input | JavaScript Engine evaluate $@. | ScriptEngineTest.java:37:26:37:38 | args | user input | -| ScriptEngineTest.java:34:19:34:36 | eval(...) | ScriptEngineTest.java:37:26:37:38 | args : String[] | ScriptEngineTest.java:34:31:34:35 | input | JavaScript Engine evaluate $@. | ScriptEngineTest.java:37:26:37:38 | args | user input | +| RhinoServlet.java:32:29:32:78 | evaluateString(...) | RhinoServlet.java:28:23:28:50 | getParameter(...) : String | RhinoServlet.java:32:55:32:58 | code | Java Script Engine evaluate $@. | RhinoServlet.java:28:23:28:50 | getParameter(...) | user input | +| RhinoServlet.java:86:25:86:97 | compileToClassFiles(...) | RhinoServlet.java:84:23:84:50 | getParameter(...) : String | RhinoServlet.java:86:54:86:57 | code | Java Script Engine evaluate $@. | RhinoServlet.java:84:23:84:50 | getParameter(...) | user input | +| RhinoServlet.java:92:23:92:89 | defineClass(...) | RhinoServlet.java:91:23:91:50 | getParameter(...) : String | RhinoServlet.java:92:74:92:88 | getBytes(...) | Java Script Engine evaluate $@. | RhinoServlet.java:91:23:91:50 | getParameter(...) | user input | +| ScriptEngineTest.java:12:19:12:42 | eval(...) | ScriptEngineTest.java:51:26:51:38 | args : String[] | ScriptEngineTest.java:12:37:12:41 | input | Java Script Engine evaluate $@. | ScriptEngineTest.java:51:26:51:38 | args | user input | +| ScriptEngineTest.java:19:19:19:36 | eval(...) | ScriptEngineTest.java:51:26:51:38 | args : String[] | ScriptEngineTest.java:19:31:19:35 | input | Java Script Engine evaluate $@. | ScriptEngineTest.java:51:26:51:38 | args | user input | +| ScriptEngineTest.java:27:19:27:36 | eval(...) | ScriptEngineTest.java:51:26:51:38 | args : String[] | ScriptEngineTest.java:27:31:27:35 | input | Java Script Engine evaluate $@. | ScriptEngineTest.java:51:26:51:38 | args | user input | +| ScriptEngineTest.java:34:19:34:36 | eval(...) | ScriptEngineTest.java:51:26:51:38 | args : String[] | ScriptEngineTest.java:34:31:34:35 | input | Java Script Engine evaluate $@. | ScriptEngineTest.java:51:26:51:38 | args | user input | +| ScriptEngineTest.java:40:27:40:47 | compile(...) | ScriptEngineTest.java:51:26:51:38 | args : String[] | ScriptEngineTest.java:40:42:40:46 | input | Java Script Engine evaluate $@. | ScriptEngineTest.java:51:26:51:38 | args | user input | +| ScriptEngineTest.java:47:20:47:56 | getProgram(...) | ScriptEngineTest.java:51:26:51:38 | args : String[] | ScriptEngineTest.java:47:51:47:55 | input | Java Script Engine evaluate $@. | ScriptEngineTest.java:51:26:51:38 | args | user input | diff --git a/java/ql/test/experimental/stubs/rhino-1.7.13/org/mozilla/javascript/CompilerEnvirons.java b/java/ql/test/experimental/stubs/rhino-1.7.13/org/mozilla/javascript/CompilerEnvirons.java new file mode 100644 index 00000000000..3cb0619499e --- /dev/null +++ b/java/ql/test/experimental/stubs/rhino-1.7.13/org/mozilla/javascript/CompilerEnvirons.java @@ -0,0 +1,12 @@ +/* -*- Mode: java; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + + package org.mozilla.javascript; + + public class CompilerEnvirons { + public CompilerEnvirons() { + } + } \ No newline at end of file diff --git a/java/ql/test/experimental/stubs/rhino-1.7.13/org/mozilla/javascript/Context.java b/java/ql/test/experimental/stubs/rhino-1.7.13/org/mozilla/javascript/Context.java index 57d30a3ab25..1bda212cfa4 100644 --- a/java/ql/test/experimental/stubs/rhino-1.7.13/org/mozilla/javascript/Context.java +++ b/java/ql/test/experimental/stubs/rhino-1.7.13/org/mozilla/javascript/Context.java @@ -480,6 +480,78 @@ public class Context return null; } + /** + * @deprecated + * @see #compileReader(Reader in, String sourceName, int lineno, Object securityDomain) + */ + @Deprecated + public final Script compileReader( + Scriptable scope, Reader in, String sourceName, int lineno, Object securityDomain) + throws IOException { + return null; + } + + /** + * Compiles the source in the given reader. + * + *

    Returns a script that may later be executed. Will consume all the source in the reader. + * + * @param in the input reader + * @param sourceName a string describing the source, such as a filename + * @param lineno the starting line number for reporting errors + * @param securityDomain an arbitrary object that specifies security information about the + * origin or owner of the script. For implementations that don't care about security, this + * value may be null. + * @return a script that may later be executed + * @exception IOException if an IOException was generated by the Reader + * @see org.mozilla.javascript.Script + */ + public final Script compileReader( + Reader in, String sourceName, int lineno, Object securityDomain) throws IOException { + return null; + } + + /** + * Compiles the source in the given string. + * + *

    Returns a script that may later be executed. + * + * @param source the source string + * @param sourceName a string describing the source, such as a filename + * @param lineno the starting line number for reporting errors. Use 0 if the line number is + * unknown. + * @param securityDomain an arbitrary object that specifies security information about the + * origin or owner of the script. For implementations that don't care about security, this + * value may be null. + * @return a script that may later be executed + * @see org.mozilla.javascript.Script + */ + public final Script compileString( + String source, String sourceName, int lineno, Object securityDomain) { + return null; + } + + /** + * Compile a JavaScript function. + * + *

    The function source must be a function definition as defined by ECMA (e.g., "function f(a) + * { return a; }"). + * + * @param scope the scope to compile relative to + * @param source the function definition source + * @param sourceName a string describing the source, such as a filename + * @param lineno the starting line number + * @param securityDomain an arbitrary object that specifies security information about the + * origin or owner of the script. For implementations that don't care about security, this + * value may be null. + * @return a Function that may later be called + * @see org.mozilla.javascript.Function + */ + public final Function compileFunction( + Scriptable scope, String source, String sourceName, int lineno, Object securityDomain) { + return null; + } + /** * Convert the value to a JavaScript boolean value. *

    diff --git a/java/ql/test/experimental/stubs/rhino-1.7.13/org/mozilla/javascript/DefiningClassLoader.java b/java/ql/test/experimental/stubs/rhino-1.7.13/org/mozilla/javascript/DefiningClassLoader.java new file mode 100644 index 00000000000..3819798c351 --- /dev/null +++ b/java/ql/test/experimental/stubs/rhino-1.7.13/org/mozilla/javascript/DefiningClassLoader.java @@ -0,0 +1,36 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + + package org.mozilla.javascript; + + /** + * Load generated classes. + * + * @author Norris Boyd + */ + public class DefiningClassLoader extends ClassLoader + implements GeneratedClassLoader + { + public DefiningClassLoader() { + } + + public DefiningClassLoader(ClassLoader parentLoader) { + } + + @Override + public Class defineClass(String name, byte[] data) { + return null; + } + + @Override + public void linkClass(Class cl) { + } + + @Override + public Class loadClass(String name, boolean resolve) + throws ClassNotFoundException + { + return null; + } + } \ No newline at end of file diff --git a/java/ql/test/experimental/stubs/rhino-1.7.13/org/mozilla/javascript/Function.java b/java/ql/test/experimental/stubs/rhino-1.7.13/org/mozilla/javascript/Function.java new file mode 100644 index 00000000000..a35a7c2dfba --- /dev/null +++ b/java/ql/test/experimental/stubs/rhino-1.7.13/org/mozilla/javascript/Function.java @@ -0,0 +1,46 @@ +/* -*- Mode: java; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +// API class + +package org.mozilla.javascript; + +/** + * This is interface that all functions in JavaScript must implement. The interface provides for + * calling functions and constructors. + * + * @see org.mozilla.javascript.Scriptable + * @author Norris Boyd + */ +public interface Function extends Scriptable { + /** + * Call the function. + * + *

    Note that the array of arguments is not guaranteed to have length greater than 0. + * + * @param cx the current Context for this thread + * @param scope the scope to execute the function relative to. This is set to the value returned + * by getParentScope() except when the function is called from a closure. + * @param thisObj the JavaScript this object + * @param args the array of arguments + * @return the result of the call + */ + Object call(Context cx, Scriptable scope, Scriptable thisObj, Object[] args); + + /** + * Call the function as a constructor. + * + *

    This method is invoked by the runtime in order to satisfy a use of the JavaScript + * new operator. This method is expected to create a new object and return it. + * + * @param cx the current Context for this thread + * @param scope an enclosing scope of the caller except when the function is called from a + * closure. + * @param args the array of arguments + * @return the allocated object + */ + Scriptable construct(Context cx, Scriptable scope, Object[] args); +} \ No newline at end of file diff --git a/java/ql/test/experimental/stubs/rhino-1.7.13/org/mozilla/javascript/GeneratedClassLoader.java b/java/ql/test/experimental/stubs/rhino-1.7.13/org/mozilla/javascript/GeneratedClassLoader.java new file mode 100644 index 00000000000..c7450862917 --- /dev/null +++ b/java/ql/test/experimental/stubs/rhino-1.7.13/org/mozilla/javascript/GeneratedClassLoader.java @@ -0,0 +1,34 @@ +/* -*- Mode: java; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +// API class + +package org.mozilla.javascript; + +/** + * Interface to define classes from generated byte code. + */ +public interface GeneratedClassLoader { + + /** + * Define a new Java class. + * Classes created via this method should have the same class loader. + * + * @param name fully qualified class name + * @param data class byte code + * @return new class object + */ + public Class defineClass(String name, byte[] data); + + /** + * Link the given class. + * + * @param cl Class instance returned from the previous call to + * {@link #defineClass(String, byte[])} + * @see java.lang.ClassLoader + */ + public void linkClass(Class cl); +} diff --git a/java/ql/test/experimental/stubs/rhino-1.7.13/org/mozilla/javascript/Script.java b/java/ql/test/experimental/stubs/rhino-1.7.13/org/mozilla/javascript/Script.java new file mode 100644 index 00000000000..824dc0241c1 --- /dev/null +++ b/java/ql/test/experimental/stubs/rhino-1.7.13/org/mozilla/javascript/Script.java @@ -0,0 +1,41 @@ +/* -*- Mode: java; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +// API class + +package org.mozilla.javascript; + +/** + * All compiled scripts implement this interface. + *

    + * This class encapsulates script execution relative to an + * object scope. + * @since 1.3 + * @author Norris Boyd + */ + +public interface Script { + + /** + * Execute the script. + *

    + * The script is executed in a particular runtime Context, which + * must be associated with the current thread. + * The script is executed relative to a scope--definitions and + * uses of global top-level variables and functions will access + * properties of the scope object. For compliant ECMA + * programs, the scope must be an object that has been initialized + * as a global object using Context.initStandardObjects. + *

    + * + * @param cx the Context associated with the current thread + * @param scope the scope to execute relative to + * @return the result of executing the script + * @see org.mozilla.javascript.Context#initStandardObjects() + */ + public Object exec(Context cx, Scriptable scope); + +} \ No newline at end of file diff --git a/java/ql/test/experimental/stubs/rhino-1.7.13/org/mozilla/javascript/optimizer/ClassCompiler.java b/java/ql/test/experimental/stubs/rhino-1.7.13/org/mozilla/javascript/optimizer/ClassCompiler.java new file mode 100644 index 00000000000..cb2332d3f61 --- /dev/null +++ b/java/ql/test/experimental/stubs/rhino-1.7.13/org/mozilla/javascript/optimizer/ClassCompiler.java @@ -0,0 +1,112 @@ +/* -*- Mode: java; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + + package org.mozilla.javascript.optimizer; + + import org.mozilla.javascript.CompilerEnvirons; + + /** + * Generates class files from script sources. + * + * since 1.5 Release 5 + * @author Igor Bukanov + */ + + public class ClassCompiler + { + /** + * Construct ClassCompiler that uses the specified compiler environment + * when generating classes. + */ + public ClassCompiler(CompilerEnvirons compilerEnv) + { + } + + /** + * Set the class name to use for main method implementation. + * The class must have a method matching + * public static void main(Script sc, String[] args), it will be + * called when main(String[] args) is called in the generated + * class. The class name should be fully qulified name and include the + * package name like in org.foo.Bar. + */ + public void setMainMethodClass(String className) + { + } + + /** + * Get the name of the class for main method implementation. + * @see #setMainMethodClass(String) + */ + public String getMainMethodClass() + { + return null; + } + + /** + * Get the compiler environment the compiler uses. + */ + public CompilerEnvirons getCompilerEnv() + { + return null; + } + + /** + * Get the class that the generated target will extend. + */ + public Class getTargetExtends() + { + return null; + } + + /** + * Set the class that the generated target will extend. + * + * @param extendsClass the class it extends + */ + public void setTargetExtends(Class extendsClass) + { + } + + /** + * Get the interfaces that the generated target will implement. + */ + public Class[] getTargetImplements() + { + return null; + } + + /** + * Set the interfaces that the generated target will implement. + * + * @param implementsClasses an array of Class objects, one for each + * interface the target will extend + */ + public void setTargetImplements(Class[] implementsClasses) + { + } + + /** + * Compile JavaScript source into one or more Java class files. + * The first compiled class will have name mainClassName. + * If the results of {@link #getTargetExtends()} or + * {@link #getTargetImplements()} are not null, then the first compiled + * class will extend the specified super class and implement + * specified interfaces. + * + * @return array where elements with even indexes specifies class name + * and the following odd index gives class file body as byte[] + * array. The initial element of the array always holds + * mainClassName and array[1] holds its byte code. + */ + public Object[] compileToClassFiles(String source, + String sourceLocation, + int lineno, + String mainClassName) + { + return null; + } + } \ No newline at end of file diff --git a/java/ql/test/stubs/scriptengine/javax/script/Bindings.java b/java/ql/test/stubs/scriptengine/javax/script/Bindings.java new file mode 100644 index 00000000000..a8eeeb6fe5e --- /dev/null +++ b/java/ql/test/stubs/scriptengine/javax/script/Bindings.java @@ -0,0 +1,14 @@ +package javax.script; +import java.util.Map; + +public interface Bindings extends Map { + public Object put(String name, Object value); + + public void putAll(Map toMerge); + + public boolean containsKey(Object key); + + public Object get(Object key); + + public Object remove(Object key); +} diff --git a/java/ql/test/stubs/scriptengine/javax/script/Compilable.java b/java/ql/test/stubs/scriptengine/javax/script/Compilable.java new file mode 100644 index 00000000000..ce6700c5a66 --- /dev/null +++ b/java/ql/test/stubs/scriptengine/javax/script/Compilable.java @@ -0,0 +1,9 @@ +package javax.script; + +import java.io.Reader; + +public interface Compilable { + public CompiledScript compile(String script) throws ScriptException; + + public CompiledScript compile(Reader script) throws ScriptException; +} diff --git a/java/ql/test/stubs/scriptengine/javax/script/CompiledScript.java b/java/ql/test/stubs/scriptengine/javax/script/CompiledScript.java new file mode 100644 index 00000000000..2f03d58c9a7 --- /dev/null +++ b/java/ql/test/stubs/scriptengine/javax/script/CompiledScript.java @@ -0,0 +1,17 @@ +package javax.script; + +public abstract class CompiledScript { + + public abstract Object eval(ScriptContext context) throws ScriptException; + + public Object eval(Bindings bindings) throws ScriptException { + return null; + } + + public Object eval() throws ScriptException { + return null; + } + + public abstract ScriptEngine getEngine(); + +} \ No newline at end of file diff --git a/java/ql/test/stubs/scriptengine/javax/script/ScriptEngine.java b/java/ql/test/stubs/scriptengine/javax/script/ScriptEngine.java index 4dc4f8c3186..35b91119e4f 100644 --- a/java/ql/test/stubs/scriptengine/javax/script/ScriptEngine.java +++ b/java/ql/test/stubs/scriptengine/javax/script/ScriptEngine.java @@ -2,5 +2,7 @@ package javax.script; public interface ScriptEngine { Object eval(String var1) throws ScriptException; + + public ScriptEngineFactory getFactory(); } diff --git a/java/ql/test/stubs/scriptengine/javax/script/ScriptEngineFactory.java b/java/ql/test/stubs/scriptengine/javax/script/ScriptEngineFactory.java index 7441c8a4ade..f802d86f80b 100644 --- a/java/ql/test/stubs/scriptengine/javax/script/ScriptEngineFactory.java +++ b/java/ql/test/stubs/scriptengine/javax/script/ScriptEngineFactory.java @@ -1,6 +1,11 @@ package javax.script; public interface ScriptEngineFactory { + public String getEngineName(); + + public String getMethodCallSyntax(String obj, String m, String... args); + + public String getProgram(String... statements); + ScriptEngine getScriptEngine(); } - diff --git a/java/ql/test/stubs/scriptengine/jdk/nashorn/api/scripting/ClassFilter.java b/java/ql/test/stubs/scriptengine/jdk/nashorn/api/scripting/ClassFilter.java new file mode 100644 index 00000000000..fcc624fc106 --- /dev/null +++ b/java/ql/test/stubs/scriptengine/jdk/nashorn/api/scripting/ClassFilter.java @@ -0,0 +1,5 @@ +package jdk.nashorn.api.scripting; + +public interface ClassFilter { + public boolean exposeToScripts(String className); +} diff --git a/java/ql/test/stubs/scriptengine/jdk/nashorn/api/scripting/NashornScriptEngine.java b/java/ql/test/stubs/scriptengine/jdk/nashorn/api/scripting/NashornScriptEngine.java index 8dc3c1afa10..e89282dfa27 100644 --- a/java/ql/test/stubs/scriptengine/jdk/nashorn/api/scripting/NashornScriptEngine.java +++ b/java/ql/test/stubs/scriptengine/jdk/nashorn/api/scripting/NashornScriptEngine.java @@ -1,10 +1,31 @@ package jdk.nashorn.api.scripting; -import javax.script.*; +import java.io.Reader; -public final class NashornScriptEngine extends AbstractScriptEngine { +import javax.script.AbstractScriptEngine; +import javax.script.Compilable; +import javax.script.CompiledScript; +import javax.script.ScriptEngine; +import javax.script.ScriptEngineFactory; +import javax.script.ScriptException; + +public final class NashornScriptEngine extends AbstractScriptEngine implements Compilable { public Object eval(String var1) throws ScriptException { return null; } -} + @Override + public ScriptEngineFactory getFactory() { + return null; + } + + @Override + public CompiledScript compile(final Reader reader) throws ScriptException { + return null; + } + + @Override + public CompiledScript compile(final String str) throws ScriptException { + return null; + } +} diff --git a/java/ql/test/stubs/scriptengine/jdk/nashorn/api/scripting/NashornScriptEngineFactory.java b/java/ql/test/stubs/scriptengine/jdk/nashorn/api/scripting/NashornScriptEngineFactory.java index 763e098ddbe..177bf463eb3 100644 --- a/java/ql/test/stubs/scriptengine/jdk/nashorn/api/scripting/NashornScriptEngineFactory.java +++ b/java/ql/test/stubs/scriptengine/jdk/nashorn/api/scripting/NashornScriptEngineFactory.java @@ -3,20 +3,48 @@ package jdk.nashorn.api.scripting; import javax.script.ScriptEngine; import javax.script.ScriptEngineFactory; - public final class NashornScriptEngineFactory implements ScriptEngineFactory { public NashornScriptEngineFactory() { } + @Override + public String getEngineName() { + return null; + } + @Override + public String getMethodCallSyntax(final String obj, final String method, final String... args) { + return null; + } + + @Override + public String getProgram(final String... statements) { + return null; + } + + @Override public ScriptEngine getScriptEngine() { return null; } + public ScriptEngine getScriptEngine(final ClassLoader appLoader) { + return null; + } - public ScriptEngine getScriptEngine(String... args) { + public ScriptEngine getScriptEngine(final ClassFilter classFilter) { + return null; + } + + public ScriptEngine getScriptEngine(final String... args) { + return null; + } + + public ScriptEngine getScriptEngine(final String[] args, final ClassLoader appLoader) { + return null; + } + + public ScriptEngine getScriptEngine(final String[] args, final ClassLoader appLoader, final ClassFilter classFilter) { return null; } } - From e4699f7fa9a142158c1207ccbc8ac31d667ed782 Mon Sep 17 00:00:00 2001 From: luchua-bc Date: Tue, 11 May 2021 20:09:37 +0000 Subject: [PATCH 162/168] Optimize the query --- .../Security/CWE/CWE-094/RhinoInjection.java | 9 +- .../Security/CWE/CWE-094/ScriptInjection.ql | 33 +++--- .../security/CWE-094/RhinoServlet.java | 5 +- .../security/CWE-094/ScriptEngineTest.java | 42 +++++--- .../security/CWE-094/ScriptInjection.expected | 102 +++++++++--------- 5 files changed, 99 insertions(+), 92 deletions(-) diff --git a/java/ql/src/experimental/Security/CWE/CWE-094/RhinoInjection.java b/java/ql/src/experimental/Security/CWE/CWE-094/RhinoInjection.java index 4a87d5bc354..15adfbe4524 100644 --- a/java/ql/src/experimental/Security/CWE/CWE-094/RhinoInjection.java +++ b/java/ql/src/experimental/Security/CWE/CWE-094/RhinoInjection.java @@ -1,3 +1,7 @@ +import org.mozilla.javascript.ClassShutter; +import org.mozilla.javascript.Context; +import org.mozilla.javascript.Scriptable; + public class RhinoInjection extends HttpServlet { protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { @@ -20,10 +24,7 @@ public class RhinoInjection extends HttpServlet { Scriptable scope = ctx.initStandardObjects(); ctx.setClassShutter(new ClassShutter() { public boolean visibleToScripts(String className) { - if(className.startsWith("com.example.")) { - return true; - } - return false; + return className.startsWith("com.example."); } }); } diff --git a/java/ql/src/experimental/Security/CWE/CWE-094/ScriptInjection.ql b/java/ql/src/experimental/Security/CWE/CWE-094/ScriptInjection.ql index d4f8cfa5370..0819f85c17e 100644 --- a/java/ql/src/experimental/Security/CWE/CWE-094/ScriptInjection.ql +++ b/java/ql/src/experimental/Security/CWE/CWE-094/ScriptInjection.ql @@ -1,7 +1,7 @@ /** * @name Injection in Java Script Engine - * @description Evaluation of a user-controlled malicious JavaScript or Java expression in - * Java Script Engine may lead to remote code execution. + * @description Evaluation of user-controlled data using the Java Script Engine may + * lead to remote code execution. * @kind path-problem * @problem.severity error * @precision high @@ -78,43 +78,37 @@ predicate scriptEngine(MethodAccess ma, Expr sink) { } /** - * Holds if a Rhino expression evaluation method has the code injection vulnerability. + * Holds if a Rhino expression evaluation method is vulnerable to code injection. */ predicate evaluateRhinoExpression(MethodAccess ma, Expr sink) { exists(RhinoEvaluateExpressionMethod m | m = ma.getMethod() | ( - sink = ma.getArgument(1) and // The second argument is the JavaScript or Java input - not ma.getMethod().getName() = "compileReader" - or - sink = ma.getArgument(0) and // The first argument is the input reader - ma.getMethod().getName() = "compileReader" + if ma.getMethod().getName() = "compileReader" + then sink = ma.getArgument(0) // The first argument is the input reader + else sink = ma.getArgument(1) // The second argument is the JavaScript or Java input ) and not exists(MethodAccess ca | - ( - ca.getMethod().hasName("initSafeStandardObjects") // safe mode - or - ca.getMethod().hasName("setClassShutter") // `ClassShutter` constraint is enforced - ) and + ca.getMethod().hasName(["initSafeStandardObjects", "setClassShutter"]) and // safe mode or `ClassShutter` constraint is enforced ma.getQualifier() = ca.getQualifier().(VarAccess).getVariable().getAnAccess() ) ) } /** - * Holds if a Rhino expression compilation method has the code injection vulnerability. + * Holds if a Rhino expression compilation method is vulnerable to code injection. */ predicate compileScript(MethodAccess ma, Expr sink) { exists(RhinoCompileClassMethod m | m = ma.getMethod() | sink = ma.getArgument(0)) } /** - * Holds if a Rhino class loading method has the code injection vulnerability. + * Holds if a Rhino class loading method is vulnerable to code injection. */ predicate defineClass(MethodAccess ma, Expr sink) { exists(RhinoDefineClassMethod m | m = ma.getMethod() | sink = ma.getArgument(1)) } -/** A sink of script injection. */ +/** A script injection sink. */ class ScriptInjectionSink extends DataFlow::ExprNode { ScriptInjectionSink() { scriptEngine(_, this.getExpr()) or @@ -123,6 +117,7 @@ class ScriptInjectionSink extends DataFlow::ExprNode { defineClass(_, this.getExpr()) } + /** An access to the method associated with this sink. */ MethodAccess getMethodAccess() { scriptEngine(result, this.getExpr()) or evaluateRhinoExpression(result, this.getExpr()) or @@ -134,11 +129,7 @@ class ScriptInjectionSink extends DataFlow::ExprNode { class ScriptInjectionConfiguration extends TaintTracking::Configuration { ScriptInjectionConfiguration() { this = "ScriptInjectionConfiguration" } - override predicate isSource(DataFlow::Node source) { - source instanceof RemoteFlowSource - or - source instanceof LocalUserInput - } + override predicate isSource(DataFlow::Node source) { source instanceof RemoteFlowSource } override predicate isSink(DataFlow::Node sink) { sink instanceof ScriptInjectionSink } } diff --git a/java/ql/test/experimental/query-tests/security/CWE-094/RhinoServlet.java b/java/ql/test/experimental/query-tests/security/CWE-094/RhinoServlet.java index 2c863b6f62f..e76a9543f87 100644 --- a/java/ql/test/experimental/query-tests/security/CWE-094/RhinoServlet.java +++ b/java/ql/test/experimental/query-tests/security/CWE-094/RhinoServlet.java @@ -63,10 +63,7 @@ public class RhinoServlet extends HttpServlet { Scriptable scope = ctx.initStandardObjects(); ctx.setClassShutter(new ClassShutter() { public boolean visibleToScripts(String className) { - if(className.startsWith("com.example.")) { - return true; - } - return false; + return className.startsWith("com.example."); } }); diff --git a/java/ql/test/experimental/query-tests/security/CWE-094/ScriptEngineTest.java b/java/ql/test/experimental/query-tests/security/CWE-094/ScriptEngineTest.java index 42d9cad6e64..ed7099d7598 100755 --- a/java/ql/test/experimental/query-tests/security/CWE-094/ScriptEngineTest.java +++ b/java/ql/test/experimental/query-tests/security/CWE-094/ScriptEngineTest.java @@ -1,9 +1,21 @@ +import javax.script.AbstractScriptEngine; +import javax.script.Compilable; +import javax.script.CompiledScript; +import javax.script.ScriptEngine; +import javax.script.ScriptEngineManager; +import javax.script.ScriptEngineFactory; +import javax.script.ScriptException; + import jdk.nashorn.api.scripting.NashornScriptEngine; import jdk.nashorn.api.scripting.NashornScriptEngineFactory; -import javax.script.*; +import java.io.IOException; +import javax.servlet.ServletException; +import javax.servlet.http.HttpServlet; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; -public class ScriptEngineTest { +public class ScriptEngineTest extends HttpServlet { public void testWithScriptEngineReference(String input) throws ScriptException { ScriptEngineManager scriptEngineManager = new ScriptEngineManager(); @@ -47,16 +59,7 @@ public class ScriptEngineTest { String program = engine.getFactory().getProgram(input); Object result = engine.eval(program); } - - public static void main(String[] args) throws ScriptException { - new ScriptEngineTest().testWithScriptEngineReference(args[0]); - new ScriptEngineTest().testNashornWithScriptEngineReference(args[0]); - new ScriptEngineTest().testNashornWithNashornScriptEngineReference(args[0]); - new ScriptEngineTest().testCustomScriptEngineReference(args[0]); - new ScriptEngineTest().testScriptEngineCompilable(args[0]); - new ScriptEngineTest().testScriptEngineGetProgram(args[0]); - } - + private static class MyCustomScriptEngine extends AbstractScriptEngine { public Object eval(String var1) throws ScriptException { return null; } @@ -82,4 +85,19 @@ public class ScriptEngineTest { @Override public String getProgram(final String... statements) { return null; } } + + protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { + try { + String code = request.getParameter("code"); + + new ScriptEngineTest().testWithScriptEngineReference(code); + new ScriptEngineTest().testNashornWithScriptEngineReference(code); + new ScriptEngineTest().testNashornWithNashornScriptEngineReference(code); + new ScriptEngineTest().testCustomScriptEngineReference(code); + new ScriptEngineTest().testScriptEngineCompilable(code); + new ScriptEngineTest().testScriptEngineGetProgram(code); + } catch (ScriptException se) { + throw new IOException(se.getMessage()); + } + } } diff --git a/java/ql/test/experimental/query-tests/security/CWE-094/ScriptInjection.expected b/java/ql/test/experimental/query-tests/security/CWE-094/ScriptInjection.expected index 0c191a4f579..5f1d250e9a2 100644 --- a/java/ql/test/experimental/query-tests/security/CWE-094/ScriptInjection.expected +++ b/java/ql/test/experimental/query-tests/security/CWE-094/ScriptInjection.expected @@ -1,58 +1,58 @@ edges | RhinoServlet.java:28:23:28:50 | getParameter(...) : String | RhinoServlet.java:32:55:32:58 | code | -| RhinoServlet.java:84:23:84:50 | getParameter(...) : String | RhinoServlet.java:86:54:86:57 | code | -| RhinoServlet.java:91:23:91:50 | getParameter(...) : String | RhinoServlet.java:92:74:92:88 | getBytes(...) | -| ScriptEngineTest.java:8:44:8:55 | input : String | ScriptEngineTest.java:12:37:12:41 | input | -| ScriptEngineTest.java:15:51:15:62 | input : String | ScriptEngineTest.java:19:31:19:35 | input | -| ScriptEngineTest.java:23:58:23:69 | input : String | ScriptEngineTest.java:27:31:27:35 | input | -| ScriptEngineTest.java:30:46:30:57 | input : String | ScriptEngineTest.java:34:31:34:35 | input | -| ScriptEngineTest.java:37:41:37:52 | input : String | ScriptEngineTest.java:40:42:40:46 | input | -| ScriptEngineTest.java:44:41:44:52 | input : String | ScriptEngineTest.java:47:51:47:55 | input | -| ScriptEngineTest.java:51:26:51:38 | args : String[] | ScriptEngineTest.java:52:56:52:62 | ...[...] : String | -| ScriptEngineTest.java:51:26:51:38 | args : String[] | ScriptEngineTest.java:53:63:53:69 | ...[...] : String | -| ScriptEngineTest.java:51:26:51:38 | args : String[] | ScriptEngineTest.java:54:70:54:76 | ...[...] : String | -| ScriptEngineTest.java:51:26:51:38 | args : String[] | ScriptEngineTest.java:55:58:55:64 | ...[...] : String | -| ScriptEngineTest.java:51:26:51:38 | args : String[] | ScriptEngineTest.java:56:53:56:59 | ...[...] : String | -| ScriptEngineTest.java:51:26:51:38 | args : String[] | ScriptEngineTest.java:57:53:57:59 | ...[...] : String | -| ScriptEngineTest.java:52:56:52:62 | ...[...] : String | ScriptEngineTest.java:8:44:8:55 | input : String | -| ScriptEngineTest.java:53:63:53:69 | ...[...] : String | ScriptEngineTest.java:15:51:15:62 | input : String | -| ScriptEngineTest.java:54:70:54:76 | ...[...] : String | ScriptEngineTest.java:23:58:23:69 | input : String | -| ScriptEngineTest.java:55:58:55:64 | ...[...] : String | ScriptEngineTest.java:30:46:30:57 | input : String | -| ScriptEngineTest.java:56:53:56:59 | ...[...] : String | ScriptEngineTest.java:37:41:37:52 | input : String | -| ScriptEngineTest.java:57:53:57:59 | ...[...] : String | ScriptEngineTest.java:44:41:44:52 | input : String | +| RhinoServlet.java:81:23:81:50 | getParameter(...) : String | RhinoServlet.java:83:54:83:57 | code | +| RhinoServlet.java:88:23:88:50 | getParameter(...) : String | RhinoServlet.java:89:74:89:88 | getBytes(...) | +| ScriptEngineTest.java:20:44:20:55 | input : String | ScriptEngineTest.java:24:37:24:41 | input | +| ScriptEngineTest.java:27:51:27:62 | input : String | ScriptEngineTest.java:31:31:31:35 | input | +| ScriptEngineTest.java:35:58:35:69 | input : String | ScriptEngineTest.java:39:31:39:35 | input | +| ScriptEngineTest.java:42:46:42:57 | input : String | ScriptEngineTest.java:46:31:46:35 | input | +| ScriptEngineTest.java:49:41:49:52 | input : String | ScriptEngineTest.java:52:42:52:46 | input | +| ScriptEngineTest.java:56:41:56:52 | input : String | ScriptEngineTest.java:59:51:59:55 | input | +| ScriptEngineTest.java:91:18:91:45 | getParameter(...) : String | ScriptEngineTest.java:93:57:93:60 | code : String | +| ScriptEngineTest.java:91:18:91:45 | getParameter(...) : String | ScriptEngineTest.java:94:64:94:67 | code : String | +| ScriptEngineTest.java:91:18:91:45 | getParameter(...) : String | ScriptEngineTest.java:95:71:95:74 | code : String | +| ScriptEngineTest.java:91:18:91:45 | getParameter(...) : String | ScriptEngineTest.java:96:59:96:62 | code : String | +| ScriptEngineTest.java:91:18:91:45 | getParameter(...) : String | ScriptEngineTest.java:97:54:97:57 | code : String | +| ScriptEngineTest.java:91:18:91:45 | getParameter(...) : String | ScriptEngineTest.java:98:54:98:57 | code : String | +| ScriptEngineTest.java:93:57:93:60 | code : String | ScriptEngineTest.java:20:44:20:55 | input : String | +| ScriptEngineTest.java:94:64:94:67 | code : String | ScriptEngineTest.java:27:51:27:62 | input : String | +| ScriptEngineTest.java:95:71:95:74 | code : String | ScriptEngineTest.java:35:58:35:69 | input : String | +| ScriptEngineTest.java:96:59:96:62 | code : String | ScriptEngineTest.java:42:46:42:57 | input : String | +| ScriptEngineTest.java:97:54:97:57 | code : String | ScriptEngineTest.java:49:41:49:52 | input : String | +| ScriptEngineTest.java:98:54:98:57 | code : String | ScriptEngineTest.java:56:41:56:52 | input : String | nodes | RhinoServlet.java:28:23:28:50 | getParameter(...) : String | semmle.label | getParameter(...) : String | | RhinoServlet.java:32:55:32:58 | code | semmle.label | code | -| RhinoServlet.java:84:23:84:50 | getParameter(...) : String | semmle.label | getParameter(...) : String | -| RhinoServlet.java:86:54:86:57 | code | semmle.label | code | -| RhinoServlet.java:91:23:91:50 | getParameter(...) : String | semmle.label | getParameter(...) : String | -| RhinoServlet.java:92:74:92:88 | getBytes(...) | semmle.label | getBytes(...) | -| ScriptEngineTest.java:8:44:8:55 | input : String | semmle.label | input : String | -| ScriptEngineTest.java:12:37:12:41 | input | semmle.label | input | -| ScriptEngineTest.java:15:51:15:62 | input : String | semmle.label | input : String | -| ScriptEngineTest.java:19:31:19:35 | input | semmle.label | input | -| ScriptEngineTest.java:23:58:23:69 | input : String | semmle.label | input : String | -| ScriptEngineTest.java:27:31:27:35 | input | semmle.label | input | -| ScriptEngineTest.java:30:46:30:57 | input : String | semmle.label | input : String | -| ScriptEngineTest.java:34:31:34:35 | input | semmle.label | input | -| ScriptEngineTest.java:37:41:37:52 | input : String | semmle.label | input : String | -| ScriptEngineTest.java:40:42:40:46 | input | semmle.label | input | -| ScriptEngineTest.java:44:41:44:52 | input : String | semmle.label | input : String | -| ScriptEngineTest.java:47:51:47:55 | input | semmle.label | input | -| ScriptEngineTest.java:51:26:51:38 | args : String[] | semmle.label | args : String[] | -| ScriptEngineTest.java:52:56:52:62 | ...[...] : String | semmle.label | ...[...] : String | -| ScriptEngineTest.java:53:63:53:69 | ...[...] : String | semmle.label | ...[...] : String | -| ScriptEngineTest.java:54:70:54:76 | ...[...] : String | semmle.label | ...[...] : String | -| ScriptEngineTest.java:55:58:55:64 | ...[...] : String | semmle.label | ...[...] : String | -| ScriptEngineTest.java:56:53:56:59 | ...[...] : String | semmle.label | ...[...] : String | -| ScriptEngineTest.java:57:53:57:59 | ...[...] : String | semmle.label | ...[...] : String | +| RhinoServlet.java:81:23:81:50 | getParameter(...) : String | semmle.label | getParameter(...) : String | +| RhinoServlet.java:83:54:83:57 | code | semmle.label | code | +| RhinoServlet.java:88:23:88:50 | getParameter(...) : String | semmle.label | getParameter(...) : String | +| RhinoServlet.java:89:74:89:88 | getBytes(...) | semmle.label | getBytes(...) | +| ScriptEngineTest.java:20:44:20:55 | input : String | semmle.label | input : String | +| ScriptEngineTest.java:24:37:24:41 | input | semmle.label | input | +| ScriptEngineTest.java:27:51:27:62 | input : String | semmle.label | input : String | +| ScriptEngineTest.java:31:31:31:35 | input | semmle.label | input | +| ScriptEngineTest.java:35:58:35:69 | input : String | semmle.label | input : String | +| ScriptEngineTest.java:39:31:39:35 | input | semmle.label | input | +| ScriptEngineTest.java:42:46:42:57 | input : String | semmle.label | input : String | +| ScriptEngineTest.java:46:31:46:35 | input | semmle.label | input | +| ScriptEngineTest.java:49:41:49:52 | input : String | semmle.label | input : String | +| ScriptEngineTest.java:52:42:52:46 | input | semmle.label | input | +| ScriptEngineTest.java:56:41:56:52 | input : String | semmle.label | input : String | +| ScriptEngineTest.java:59:51:59:55 | input | semmle.label | input | +| ScriptEngineTest.java:91:18:91:45 | getParameter(...) : String | semmle.label | getParameter(...) : String | +| ScriptEngineTest.java:93:57:93:60 | code : String | semmle.label | code : String | +| ScriptEngineTest.java:94:64:94:67 | code : String | semmle.label | code : String | +| ScriptEngineTest.java:95:71:95:74 | code : String | semmle.label | code : String | +| ScriptEngineTest.java:96:59:96:62 | code : String | semmle.label | code : String | +| ScriptEngineTest.java:97:54:97:57 | code : String | semmle.label | code : String | +| ScriptEngineTest.java:98:54:98:57 | code : String | semmle.label | code : String | #select | RhinoServlet.java:32:29:32:78 | evaluateString(...) | RhinoServlet.java:28:23:28:50 | getParameter(...) : String | RhinoServlet.java:32:55:32:58 | code | Java Script Engine evaluate $@. | RhinoServlet.java:28:23:28:50 | getParameter(...) | user input | -| RhinoServlet.java:86:25:86:97 | compileToClassFiles(...) | RhinoServlet.java:84:23:84:50 | getParameter(...) : String | RhinoServlet.java:86:54:86:57 | code | Java Script Engine evaluate $@. | RhinoServlet.java:84:23:84:50 | getParameter(...) | user input | -| RhinoServlet.java:92:23:92:89 | defineClass(...) | RhinoServlet.java:91:23:91:50 | getParameter(...) : String | RhinoServlet.java:92:74:92:88 | getBytes(...) | Java Script Engine evaluate $@. | RhinoServlet.java:91:23:91:50 | getParameter(...) | user input | -| ScriptEngineTest.java:12:19:12:42 | eval(...) | ScriptEngineTest.java:51:26:51:38 | args : String[] | ScriptEngineTest.java:12:37:12:41 | input | Java Script Engine evaluate $@. | ScriptEngineTest.java:51:26:51:38 | args | user input | -| ScriptEngineTest.java:19:19:19:36 | eval(...) | ScriptEngineTest.java:51:26:51:38 | args : String[] | ScriptEngineTest.java:19:31:19:35 | input | Java Script Engine evaluate $@. | ScriptEngineTest.java:51:26:51:38 | args | user input | -| ScriptEngineTest.java:27:19:27:36 | eval(...) | ScriptEngineTest.java:51:26:51:38 | args : String[] | ScriptEngineTest.java:27:31:27:35 | input | Java Script Engine evaluate $@. | ScriptEngineTest.java:51:26:51:38 | args | user input | -| ScriptEngineTest.java:34:19:34:36 | eval(...) | ScriptEngineTest.java:51:26:51:38 | args : String[] | ScriptEngineTest.java:34:31:34:35 | input | Java Script Engine evaluate $@. | ScriptEngineTest.java:51:26:51:38 | args | user input | -| ScriptEngineTest.java:40:27:40:47 | compile(...) | ScriptEngineTest.java:51:26:51:38 | args : String[] | ScriptEngineTest.java:40:42:40:46 | input | Java Script Engine evaluate $@. | ScriptEngineTest.java:51:26:51:38 | args | user input | -| ScriptEngineTest.java:47:20:47:56 | getProgram(...) | ScriptEngineTest.java:51:26:51:38 | args : String[] | ScriptEngineTest.java:47:51:47:55 | input | Java Script Engine evaluate $@. | ScriptEngineTest.java:51:26:51:38 | args | user input | +| RhinoServlet.java:83:25:83:97 | compileToClassFiles(...) | RhinoServlet.java:81:23:81:50 | getParameter(...) : String | RhinoServlet.java:83:54:83:57 | code | Java Script Engine evaluate $@. | RhinoServlet.java:81:23:81:50 | getParameter(...) | user input | +| RhinoServlet.java:89:23:89:89 | defineClass(...) | RhinoServlet.java:88:23:88:50 | getParameter(...) : String | RhinoServlet.java:89:74:89:88 | getBytes(...) | Java Script Engine evaluate $@. | RhinoServlet.java:88:23:88:50 | getParameter(...) | user input | +| ScriptEngineTest.java:24:19:24:42 | eval(...) | ScriptEngineTest.java:91:18:91:45 | getParameter(...) : String | ScriptEngineTest.java:24:37:24:41 | input | Java Script Engine evaluate $@. | ScriptEngineTest.java:91:18:91:45 | getParameter(...) | user input | +| ScriptEngineTest.java:31:19:31:36 | eval(...) | ScriptEngineTest.java:91:18:91:45 | getParameter(...) : String | ScriptEngineTest.java:31:31:31:35 | input | Java Script Engine evaluate $@. | ScriptEngineTest.java:91:18:91:45 | getParameter(...) | user input | +| ScriptEngineTest.java:39:19:39:36 | eval(...) | ScriptEngineTest.java:91:18:91:45 | getParameter(...) : String | ScriptEngineTest.java:39:31:39:35 | input | Java Script Engine evaluate $@. | ScriptEngineTest.java:91:18:91:45 | getParameter(...) | user input | +| ScriptEngineTest.java:46:19:46:36 | eval(...) | ScriptEngineTest.java:91:18:91:45 | getParameter(...) : String | ScriptEngineTest.java:46:31:46:35 | input | Java Script Engine evaluate $@. | ScriptEngineTest.java:91:18:91:45 | getParameter(...) | user input | +| ScriptEngineTest.java:52:27:52:47 | compile(...) | ScriptEngineTest.java:91:18:91:45 | getParameter(...) : String | ScriptEngineTest.java:52:42:52:46 | input | Java Script Engine evaluate $@. | ScriptEngineTest.java:91:18:91:45 | getParameter(...) | user input | +| ScriptEngineTest.java:59:20:59:56 | getProgram(...) | ScriptEngineTest.java:91:18:91:45 | getParameter(...) : String | ScriptEngineTest.java:59:51:59:55 | input | Java Script Engine evaluate $@. | ScriptEngineTest.java:91:18:91:45 | getParameter(...) | user input | From 0ac8453398d44181ba9c0334fc8dd3e82ff31298 Mon Sep 17 00:00:00 2001 From: luchua-bc Date: Fri, 14 May 2021 12:50:24 +0000 Subject: [PATCH 163/168] Allow all arguments of methods in ScriptEngineFactory --- .../Security/CWE/CWE-094/ScriptInjection.ql | 18 +++++++++++++++--- 1 file changed, 15 insertions(+), 3 deletions(-) diff --git a/java/ql/src/experimental/Security/CWE/CWE-094/ScriptInjection.ql b/java/ql/src/experimental/Security/CWE/CWE-094/ScriptInjection.ql index 0819f85c17e..9faca9b8630 100644 --- a/java/ql/src/experimental/Security/CWE/CWE-094/ScriptInjection.ql +++ b/java/ql/src/experimental/Security/CWE/CWE-094/ScriptInjection.ql @@ -26,6 +26,17 @@ class ScriptEngineMethod extends Method { this.getDeclaringType().getASupertype*().hasQualifiedName("javax.script", "ScriptEngineFactory") and this.hasName(["getProgram", "getMethodCallSyntax"]) } + + /** Holds if the index is for an injectable parameter. */ + bindingset[index] + predicate isInjectableArgIndex(int index) { + if + this.getDeclaringType() + .getASupertype*() + .hasQualifiedName("javax.script", "ScriptEngineFactory") + then any() + else index = 0 + } } /** The context class `org.mozilla.javascript.Context` of Rhino Java Script Engine. */ @@ -71,9 +82,10 @@ class RhinoDefineClassMethod extends Method { /** Holds if `ma` is a method access of `ScriptEngineMethod`. */ predicate scriptEngine(MethodAccess ma, Expr sink) { - exists(Method m | m = ma.getMethod() | - m instanceof ScriptEngineMethod and - sink = ma.getArgument(0) + exists(ScriptEngineMethod m, int index | + m = ma.getMethod() and + m.isInjectableArgIndex(index) and + sink = ma.getArgument(index) ) } From 2c1374bdcf416823fda65365d2296c0961537b31 Mon Sep 17 00:00:00 2001 From: luchua-bc Date: Fri, 14 May 2021 20:05:56 +0000 Subject: [PATCH 164/168] Use inline implementation for ScriptEngineFactory --- .../Security/CWE/CWE-094/ScriptInjection.ql | 18 ++++-------------- 1 file changed, 4 insertions(+), 14 deletions(-) diff --git a/java/ql/src/experimental/Security/CWE/CWE-094/ScriptInjection.ql b/java/ql/src/experimental/Security/CWE/CWE-094/ScriptInjection.ql index 9faca9b8630..5274140158c 100644 --- a/java/ql/src/experimental/Security/CWE/CWE-094/ScriptInjection.ql +++ b/java/ql/src/experimental/Security/CWE/CWE-094/ScriptInjection.ql @@ -26,17 +26,6 @@ class ScriptEngineMethod extends Method { this.getDeclaringType().getASupertype*().hasQualifiedName("javax.script", "ScriptEngineFactory") and this.hasName(["getProgram", "getMethodCallSyntax"]) } - - /** Holds if the index is for an injectable parameter. */ - bindingset[index] - predicate isInjectableArgIndex(int index) { - if - this.getDeclaringType() - .getASupertype*() - .hasQualifiedName("javax.script", "ScriptEngineFactory") - then any() - else index = 0 - } } /** The context class `org.mozilla.javascript.Context` of Rhino Java Script Engine. */ @@ -82,10 +71,11 @@ class RhinoDefineClassMethod extends Method { /** Holds if `ma` is a method access of `ScriptEngineMethod`. */ predicate scriptEngine(MethodAccess ma, Expr sink) { - exists(ScriptEngineMethod m, int index | + exists(ScriptEngineMethod m | m = ma.getMethod() and - m.isInjectableArgIndex(index) and - sink = ma.getArgument(index) + if m.getDeclaringType().getASupertype*().hasQualifiedName("javax.script", "ScriptEngineFactory") + then sink = ma.getArgument(_) // all arguments allow script injection + else sink = ma.getArgument(0) ) } From 2fa249a8eba1314acca9e7a953f5c3ed658c5b39 Mon Sep 17 00:00:00 2001 From: luchua-bc Date: Fri, 14 May 2021 20:31:05 +0000 Subject: [PATCH 165/168] Update method name and qldoc --- .../Security/CWE/CWE-094/ScriptInjection.ql | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/java/ql/src/experimental/Security/CWE/CWE-094/ScriptInjection.ql b/java/ql/src/experimental/Security/CWE/CWE-094/ScriptInjection.ql index 5274140158c..71accb28c37 100644 --- a/java/ql/src/experimental/Security/CWE/CWE-094/ScriptInjection.ql +++ b/java/ql/src/experimental/Security/CWE/CWE-094/ScriptInjection.ql @@ -69,8 +69,11 @@ class RhinoDefineClassMethod extends Method { } } -/** Holds if `ma` is a method access of `ScriptEngineMethod`. */ -predicate scriptEngine(MethodAccess ma, Expr sink) { +/** + * Holds if `ma` is a call to a `ScriptEngineMethod` and `sink` is an argument that + * will be executed. + */ +predicate isScriptArgument(MethodAccess ma, Expr sink) { exists(ScriptEngineMethod m | m = ma.getMethod() and if m.getDeclaringType().getASupertype*().hasQualifiedName("javax.script", "ScriptEngineFactory") @@ -113,7 +116,7 @@ predicate defineClass(MethodAccess ma, Expr sink) { /** A script injection sink. */ class ScriptInjectionSink extends DataFlow::ExprNode { ScriptInjectionSink() { - scriptEngine(_, this.getExpr()) or + isScriptArgument(_, this.getExpr()) or evaluateRhinoExpression(_, this.getExpr()) or compileScript(_, this.getExpr()) or defineClass(_, this.getExpr()) @@ -121,7 +124,7 @@ class ScriptInjectionSink extends DataFlow::ExprNode { /** An access to the method associated with this sink. */ MethodAccess getMethodAccess() { - scriptEngine(result, this.getExpr()) or + isScriptArgument(result, this.getExpr()) or evaluateRhinoExpression(result, this.getExpr()) or compileScript(result, this.getExpr()) or defineClass(result, this.getExpr()) From 9d392263a51e77783b78022839aa52ab37c7ba64 Mon Sep 17 00:00:00 2001 From: luchua-bc Date: Mon, 17 May 2021 16:07:00 +0000 Subject: [PATCH 166/168] Refactor inconsistent method names --- .../Security/CWE/CWE-094/ScriptInjection.ql | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/java/ql/src/experimental/Security/CWE/CWE-094/ScriptInjection.ql b/java/ql/src/experimental/Security/CWE/CWE-094/ScriptInjection.ql index 71accb28c37..aa5a8b74f93 100644 --- a/java/ql/src/experimental/Security/CWE/CWE-094/ScriptInjection.ql +++ b/java/ql/src/experimental/Security/CWE/CWE-094/ScriptInjection.ql @@ -85,7 +85,7 @@ predicate isScriptArgument(MethodAccess ma, Expr sink) { /** * Holds if a Rhino expression evaluation method is vulnerable to code injection. */ -predicate evaluateRhinoExpression(MethodAccess ma, Expr sink) { +predicate evaluatesRhinoExpression(MethodAccess ma, Expr sink) { exists(RhinoEvaluateExpressionMethod m | m = ma.getMethod() | ( if ma.getMethod().getName() = "compileReader" @@ -102,14 +102,14 @@ predicate evaluateRhinoExpression(MethodAccess ma, Expr sink) { /** * Holds if a Rhino expression compilation method is vulnerable to code injection. */ -predicate compileScript(MethodAccess ma, Expr sink) { +predicate compilesScript(MethodAccess ma, Expr sink) { exists(RhinoCompileClassMethod m | m = ma.getMethod() | sink = ma.getArgument(0)) } /** * Holds if a Rhino class loading method is vulnerable to code injection. */ -predicate defineClass(MethodAccess ma, Expr sink) { +predicate definesRhinoClass(MethodAccess ma, Expr sink) { exists(RhinoDefineClassMethod m | m = ma.getMethod() | sink = ma.getArgument(1)) } @@ -117,17 +117,17 @@ predicate defineClass(MethodAccess ma, Expr sink) { class ScriptInjectionSink extends DataFlow::ExprNode { ScriptInjectionSink() { isScriptArgument(_, this.getExpr()) or - evaluateRhinoExpression(_, this.getExpr()) or - compileScript(_, this.getExpr()) or - defineClass(_, this.getExpr()) + evaluatesRhinoExpression(_, this.getExpr()) or + compilesScript(_, this.getExpr()) or + definesRhinoClass(_, this.getExpr()) } /** An access to the method associated with this sink. */ MethodAccess getMethodAccess() { isScriptArgument(result, this.getExpr()) or - evaluateRhinoExpression(result, this.getExpr()) or - compileScript(result, this.getExpr()) or - defineClass(result, this.getExpr()) + evaluatesRhinoExpression(result, this.getExpr()) or + compilesScript(result, this.getExpr()) or + definesRhinoClass(result, this.getExpr()) } } From d4323a4a540372b0d3a38ebe8a8e53d2af3de462 Mon Sep 17 00:00:00 2001 From: luchua-bc Date: Mon, 17 May 2021 20:15:09 +0000 Subject: [PATCH 167/168] Update qldoc --- .../experimental/Security/CWE/CWE-094/ScriptInjection.qhelp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/java/ql/src/experimental/Security/CWE/CWE-094/ScriptInjection.qhelp b/java/ql/src/experimental/Security/CWE/CWE-094/ScriptInjection.qhelp index 586304ebb7c..2683cf9ad29 100644 --- a/java/ql/src/experimental/Security/CWE/CWE-094/ScriptInjection.qhelp +++ b/java/ql/src/experimental/Security/CWE/CWE-094/ScriptInjection.qhelp @@ -4,7 +4,7 @@ -

    The Java Scripting API has been available since the release of Java 6, which allows +

    The Java Scripting API has been available since the release of Java 6. It allows applications to interact with scripts written in languages such as JavaScript. It serves as an embedded scripting engine inside Java applications which allows Java-to-JavaScript interoperability and provides a seamless integration between the two languages. If an @@ -21,7 +21,7 @@ -

    The following code could execute random JavaScript code in ScriptEngine

    +

    The following code could execute user-supplied JavaScript code in ScriptEngine

    From 02aa9c6fc7bde93ab56a1dc78423499d023978fe Mon Sep 17 00:00:00 2001 From: luchua-bc Date: Tue, 18 May 2021 12:09:52 +0000 Subject: [PATCH 168/168] Optimize the sink and update qldoc --- .../Security/CWE/CWE-094/ScriptInjection.ql | 21 ++++++++++--------- 1 file changed, 11 insertions(+), 10 deletions(-) diff --git a/java/ql/src/experimental/Security/CWE/CWE-094/ScriptInjection.ql b/java/ql/src/experimental/Security/CWE/CWE-094/ScriptInjection.ql index aa5a8b74f93..fb8bf867501 100644 --- a/java/ql/src/experimental/Security/CWE/CWE-094/ScriptInjection.ql +++ b/java/ql/src/experimental/Security/CWE/CWE-094/ScriptInjection.ql @@ -115,22 +115,23 @@ predicate definesRhinoClass(MethodAccess ma, Expr sink) { /** A script injection sink. */ class ScriptInjectionSink extends DataFlow::ExprNode { + MethodAccess methodAccess; + ScriptInjectionSink() { - isScriptArgument(_, this.getExpr()) or - evaluatesRhinoExpression(_, this.getExpr()) or - compilesScript(_, this.getExpr()) or - definesRhinoClass(_, this.getExpr()) + isScriptArgument(methodAccess, this.getExpr()) or + evaluatesRhinoExpression(methodAccess, this.getExpr()) or + compilesScript(methodAccess, this.getExpr()) or + definesRhinoClass(methodAccess, this.getExpr()) } /** An access to the method associated with this sink. */ - MethodAccess getMethodAccess() { - isScriptArgument(result, this.getExpr()) or - evaluatesRhinoExpression(result, this.getExpr()) or - compilesScript(result, this.getExpr()) or - definesRhinoClass(result, this.getExpr()) - } + MethodAccess getMethodAccess() { result = methodAccess } } +/** + * A taint tracking configuration that tracks flow from `RemoteFlowSource` to an argument + * of a method call that executes injected script. + */ class ScriptInjectionConfiguration extends TaintTracking::Configuration { ScriptInjectionConfiguration() { this = "ScriptInjectionConfiguration" }