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. 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. 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. 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/src/Security/CWE/CWE-190/TaintedAllocationSize.ql b/cpp/ql/src/Security/CWE/CWE-190/TaintedAllocationSize.ql index cc2d52385c7..75cac365a1a 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,27 @@ 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) + 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() + } } predicate taintedAllocSize( 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/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..53a50841977 --- /dev/null +++ b/cpp/ql/src/experimental/Security/CWE/CWE-1126/DeclarationOfVariableWithUnnecessarilyWideScope.c @@ -0,0 +1,14 @@ +while(intIndex > 2) +{ + ... + intIndex--; + ... +} // GOOD: correct 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..5234212f7ca --- /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 create an infinite loop exhausting resources. 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..e73f36145c6 --- /dev/null +++ b/cpp/ql/src/experimental/Security/CWE/CWE-1126/DeclarationOfVariableWithUnnecessarilyWideScope.ql @@ -0,0 +1,60 @@ +/** + * @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 create an infinite loop exhausting resources. + * 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.(VariableAccess).getTarget().getName() = dl.getName() 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 | + this = aexp.getEnclosingStmt().getParentStmt*() and + ( + aexp.getLValue().(ArrayExpr).getArrayBase().(VariableAccess).getTarget() = v + or + aexp.getLValue().(VariableAccess).getTarget() = v + ) + ) + or + exists(CrementOperation crm | + this = crm.getEnclosingStmt().getParentStmt*() 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 $@ condition.", lp, "loop" 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..058d66b1496 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(ArgNode arg | fwdFlow(arg, cc, config) and viableParamArg(call, _, arg) ) @@ -515,24 +512,22 @@ private module Stage1 { pragma[nomagic] predicate viableParamArgNodeCandFwd1( - DataFlowCall call, ParameterNode p, ArgumentNode 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, ArgumentNode arg, boolean toReturn, Configuration config - ) { - exists(ParameterNode 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, ArgumentNode arg, Configuration config) { + private predicate revFlowInToReturn(DataFlowCall call, ArgNode arg, Configuration config) { revFlowIn(call, arg, true, config) } @@ -597,7 +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(ParameterNode 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 @@ -663,7 +658,7 @@ private predicate flowOutOfCallNodeCand1( pragma[nomagic] private predicate viableParamArgNodeCand1( - DataFlowCall call, ParameterNode p, ArgumentNode arg, Configuration config + DataFlowCall call, ParamNode p, ArgNode arg, Configuration config ) { Stage1::viableParamArgNodeCandFwd1(call, p, arg, config) and Stage1::revFlow(arg, config) @@ -675,7 +670,7 @@ private predicate viableParamArgNodeCand1( */ pragma[nomagic] private predicate flowIntoCallNodeCand1( - DataFlowCall call, ArgumentNode arg, ParameterNode p, Configuration config + DataFlowCall call, ArgNode arg, ParamNode p, Configuration config ) { viableParamArgNodeCand1(call, p, arg, config) and Stage1::revFlow(p, config) and @@ -735,8 +730,7 @@ private predicate flowOutOfCallNodeCand1( */ pragma[nomagic] private predicate flowIntoCallNodeCand1( - DataFlowCall call, ArgumentNode arg, ParameterNode 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 | @@ -944,10 +938,10 @@ private module Stage2 { pragma[nomagic] private predicate fwdFlowIn( - DataFlowCall call, ParameterNode p, Cc outercc, Cc innercc, ApOption argAp, Ap ap, + DataFlowCall call, ParamNode p, Cc outercc, Cc innercc, ApOption argAp, Ap ap, Configuration config ) { - exists(ArgumentNode 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) @@ -992,7 +986,7 @@ private module Stage2 { private predicate fwdFlowIsEntered( DataFlowCall call, Cc cc, ApOption argAp, Ap ap, Configuration config ) { - exists(ParameterNode p | + exists(ParamNode p | fwdFlowIn(call, p, cc, _, argAp, ap, config) and PrevStage::parameterMayFlowThrough(p, _, getApprox(ap), config) ) @@ -1133,10 +1127,9 @@ private module Stage2 { pragma[nomagic] private predicate revFlowIn( - DataFlowCall call, ArgumentNode arg, boolean toReturn, ApOption returnAp, Ap ap, - Configuration config + DataFlowCall call, ArgNode arg, boolean toReturn, ApOption returnAp, Ap ap, Configuration config ) { - exists(ParameterNode p, boolean allowsFieldFlow | + exists(ParamNode p, boolean allowsFieldFlow | revFlow(p, toReturn, returnAp, ap, config) and flowIntoCall(call, arg, p, allowsFieldFlow, config) | @@ -1146,7 +1139,7 @@ private module Stage2 { pragma[nomagic] private predicate revFlowInToReturn( - DataFlowCall call, ArgumentNode 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) } @@ -1199,13 +1192,13 @@ private module Stage2 { pragma[noinline] private predicate parameterFlow( - ParameterNode 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(ParameterNode 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 @@ -1245,8 +1238,7 @@ private predicate flowOutOfCallNodeCand2( pragma[nomagic] private predicate flowIntoCallNodeCand2( - DataFlowCall call, ArgumentNode node1, ParameterNode 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 @@ -1260,8 +1252,8 @@ private module LocalFlowBigStep { */ private class FlowCheckNode extends Node { FlowCheckNode() { - this instanceof CastNode or - clearsContent(this, _) + castNode(this) or + clearsContentCached(this, _) } } @@ -1275,7 +1267,7 @@ private module LocalFlowBigStep { config.isSource(node) or jumpStep(_, node, config) or additionalJumpStep(_, node, config) or - node instanceof ParameterNode or + node instanceof ParamNode or node instanceof OutNodeExt or store(_, _, node, _) or read(_, _, node) or @@ -1321,21 +1313,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 +1342,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 +1376,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 +1435,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 +1577,10 @@ private module Stage3 { pragma[nomagic] private predicate fwdFlowIn( - DataFlowCall call, ParameterNode p, Cc outercc, Cc innercc, ApOption argAp, Ap ap, + DataFlowCall call, ParamNode p, Cc outercc, Cc innercc, ApOption argAp, Ap ap, Configuration config ) { - exists(ArgumentNode 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) @@ -1631,7 +1625,7 @@ private module Stage3 { private predicate fwdFlowIsEntered( DataFlowCall call, Cc cc, ApOption argAp, Ap ap, Configuration config ) { - exists(ParameterNode p | + exists(ParamNode p | fwdFlowIn(call, p, cc, _, argAp, ap, config) and PrevStage::parameterMayFlowThrough(p, _, unbindApa(getApprox(ap)), config) ) @@ -1772,10 +1766,9 @@ private module Stage3 { pragma[nomagic] private predicate revFlowIn( - DataFlowCall call, ArgumentNode arg, boolean toReturn, ApOption returnAp, Ap ap, - Configuration config + DataFlowCall call, ArgNode arg, boolean toReturn, ApOption returnAp, Ap ap, Configuration config ) { - exists(ParameterNode p, boolean allowsFieldFlow | + exists(ParamNode p, boolean allowsFieldFlow | revFlow(p, toReturn, returnAp, ap, config) and flowIntoCall(call, arg, p, allowsFieldFlow, config) | @@ -1785,7 +1778,7 @@ private module Stage3 { pragma[nomagic] private predicate revFlowInToReturn( - DataFlowCall call, ArgumentNode 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) } @@ -1838,13 +1831,13 @@ private module Stage3 { pragma[noinline] private predicate parameterFlow( - ParameterNode 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(ParameterNode 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 @@ -2088,7 +2081,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,8 +2148,7 @@ private module Stage4 { pragma[nomagic] private predicate flowIntoCall( - DataFlowCall call, ArgumentNode node1, ParameterNode 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 @@ -2300,10 +2292,10 @@ private module Stage4 { pragma[nomagic] private predicate fwdFlowIn( - DataFlowCall call, ParameterNode p, Cc outercc, Cc innercc, ApOption argAp, Ap ap, + DataFlowCall call, ParamNode p, Cc outercc, Cc innercc, ApOption argAp, Ap ap, Configuration config ) { - exists(ArgumentNode 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) @@ -2348,7 +2340,7 @@ private module Stage4 { private predicate fwdFlowIsEntered( DataFlowCall call, Cc cc, ApOption argAp, Ap ap, Configuration config ) { - exists(ParameterNode p | + exists(ParamNode p | fwdFlowIn(call, p, cc, _, argAp, ap, config) and PrevStage::parameterMayFlowThrough(p, _, unbindApa(getApprox(ap)), config) ) @@ -2489,10 +2481,9 @@ private module Stage4 { pragma[nomagic] private predicate revFlowIn( - DataFlowCall call, ArgumentNode arg, boolean toReturn, ApOption returnAp, Ap ap, - Configuration config + DataFlowCall call, ArgNode arg, boolean toReturn, ApOption returnAp, Ap ap, Configuration config ) { - exists(ParameterNode p, boolean allowsFieldFlow | + exists(ParamNode p, boolean allowsFieldFlow | revFlow(p, toReturn, returnAp, ap, config) and flowIntoCall(call, arg, p, allowsFieldFlow, config) | @@ -2502,7 +2493,7 @@ private module Stage4 { pragma[nomagic] private predicate revFlowInToReturn( - DataFlowCall call, ArgumentNode 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) } @@ -2555,13 +2546,13 @@ private module Stage4 { pragma[noinline] private predicate parameterFlow( - ParameterNode 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(ParameterNode 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 @@ -2606,7 +2597,7 @@ private predicate nodeMayUseSummary(Node n, AccessPathApprox apa, Configuration private newtype TSummaryCtx = TSummaryCtxNone() or - TSummaryCtxSome(ParameterNode p, AccessPath ap) { + TSummaryCtxSome(ParamNode p, AccessPath ap) { Stage4::parameterMayFlowThrough(p, _, ap.getApprox(), _) } @@ -2627,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 ParameterNode p; + private ParamNode p; private AccessPath ap; SummaryCtxSome() { this = TSummaryCtxSome(p, ap) } @@ -2758,7 +2749,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 +2970,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 +3139,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 +3226,7 @@ pragma[noinline] private predicate pathIntoArg( PathNodeMid mid, int i, CallContext cc, DataFlowCall call, AccessPath ap, AccessPathApprox apa ) { - exists(ArgumentNode arg | + exists(ArgNode arg | arg = mid.getNode() and cc = mid.getCallContext() and arg.argumentOf(call, i) and @@ -3248,7 +3239,7 @@ pragma[noinline] private predicate parameterCand( DataFlowCallable callable, int i, AccessPathApprox apa, Configuration config ) { - exists(ParameterNode p | + exists(ParamNode p | Stage4::revFlow(p, _, _, apa, config) and p.isParameterOf(callable, i) ) @@ -3272,7 +3263,7 @@ private predicate pathIntoCallable0( * respectively. */ private predicate pathIntoCallable( - PathNodeMid mid, ParameterNode 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 | @@ -3568,7 +3559,7 @@ private module FlowExploration { private newtype TSummaryCtx1 = TSummaryCtx1None() or - TSummaryCtx1Param(ParameterNode p) + TSummaryCtx1Param(ParamNode p) private newtype TSummaryCtx2 = TSummaryCtx2None() or @@ -3591,7 +3582,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 +3602,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 +3616,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 +3774,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 +3788,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 +3804,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 +3818,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 +3915,7 @@ private module FlowExploration { PartialPathNodeFwd mid, int i, CallContext cc, DataFlowCall call, PartialAccessPath ap, Configuration config ) { - exists(ArgumentNode arg | + exists(ArgNode arg | arg = mid.getNode() and cc = mid.getCallContext() and arg.argumentOf(call, i) and @@ -3943,7 +3934,7 @@ private module FlowExploration { } private predicate partialPathIntoCallable( - PartialPathNodeFwd mid, ParameterNode p, CallContext outercc, CallContextCall innercc, + PartialPathNodeFwd mid, ParamNode p, CallContext outercc, CallContextCall innercc, TSummaryCtx1 sc1, TSummaryCtx2 sc2, DataFlowCall call, PartialAccessPath ap, Configuration config ) { @@ -3980,7 +3971,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(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) ) @@ -4037,7 +4028,7 @@ private module FlowExploration { apConsRev(ap, c, ap0, config) ) or - exists(ParameterNode p | + exists(ParamNode p | mid.getNode() = p and viableParamArg(_, p, node) and sc1 = mid.getSummaryCtx1() and @@ -4115,7 +4106,7 @@ private module FlowExploration { int pos, TRevSummaryCtx1Some sc1, TRevSummaryCtx2Some sc2, RevPartialAccessPath ap, Configuration config ) { - exists(PartialPathNodeRev mid, ParameterNode p | + exists(PartialPathNodeRev mid, ParamNode p | mid.getNode() = p and p.isParameterOf(_, pos) and sc1 = mid.getSummaryCtx1() and @@ -4138,7 +4129,7 @@ private module FlowExploration { pragma[nomagic] private predicate revPartialPathThroughCallable( - PartialPathNodeRev mid, ArgumentNode 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 9498e51e7e6..058d66b1496 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(ArgNode arg | fwdFlow(arg, cc, config) and viableParamArg(call, _, arg) ) @@ -515,24 +512,22 @@ private module Stage1 { pragma[nomagic] predicate viableParamArgNodeCandFwd1( - DataFlowCall call, ParameterNode p, ArgumentNode 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, ArgumentNode arg, boolean toReturn, Configuration config - ) { - exists(ParameterNode 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, ArgumentNode arg, Configuration config) { + private predicate revFlowInToReturn(DataFlowCall call, ArgNode arg, Configuration config) { revFlowIn(call, arg, true, config) } @@ -597,7 +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(ParameterNode 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 @@ -663,7 +658,7 @@ private predicate flowOutOfCallNodeCand1( pragma[nomagic] private predicate viableParamArgNodeCand1( - DataFlowCall call, ParameterNode p, ArgumentNode arg, Configuration config + DataFlowCall call, ParamNode p, ArgNode arg, Configuration config ) { Stage1::viableParamArgNodeCandFwd1(call, p, arg, config) and Stage1::revFlow(arg, config) @@ -675,7 +670,7 @@ private predicate viableParamArgNodeCand1( */ pragma[nomagic] private predicate flowIntoCallNodeCand1( - DataFlowCall call, ArgumentNode arg, ParameterNode p, Configuration config + DataFlowCall call, ArgNode arg, ParamNode p, Configuration config ) { viableParamArgNodeCand1(call, p, arg, config) and Stage1::revFlow(p, config) and @@ -735,8 +730,7 @@ private predicate flowOutOfCallNodeCand1( */ pragma[nomagic] private predicate flowIntoCallNodeCand1( - DataFlowCall call, ArgumentNode arg, ParameterNode 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 | @@ -944,10 +938,10 @@ private module Stage2 { pragma[nomagic] private predicate fwdFlowIn( - DataFlowCall call, ParameterNode p, Cc outercc, Cc innercc, ApOption argAp, Ap ap, + DataFlowCall call, ParamNode p, Cc outercc, Cc innercc, ApOption argAp, Ap ap, Configuration config ) { - exists(ArgumentNode 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) @@ -992,7 +986,7 @@ private module Stage2 { private predicate fwdFlowIsEntered( DataFlowCall call, Cc cc, ApOption argAp, Ap ap, Configuration config ) { - exists(ParameterNode p | + exists(ParamNode p | fwdFlowIn(call, p, cc, _, argAp, ap, config) and PrevStage::parameterMayFlowThrough(p, _, getApprox(ap), config) ) @@ -1133,10 +1127,9 @@ private module Stage2 { pragma[nomagic] private predicate revFlowIn( - DataFlowCall call, ArgumentNode arg, boolean toReturn, ApOption returnAp, Ap ap, - Configuration config + DataFlowCall call, ArgNode arg, boolean toReturn, ApOption returnAp, Ap ap, Configuration config ) { - exists(ParameterNode p, boolean allowsFieldFlow | + exists(ParamNode p, boolean allowsFieldFlow | revFlow(p, toReturn, returnAp, ap, config) and flowIntoCall(call, arg, p, allowsFieldFlow, config) | @@ -1146,7 +1139,7 @@ private module Stage2 { pragma[nomagic] private predicate revFlowInToReturn( - DataFlowCall call, ArgumentNode 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) } @@ -1199,13 +1192,13 @@ private module Stage2 { pragma[noinline] private predicate parameterFlow( - ParameterNode 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(ParameterNode 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 @@ -1245,8 +1238,7 @@ private predicate flowOutOfCallNodeCand2( pragma[nomagic] private predicate flowIntoCallNodeCand2( - DataFlowCall call, ArgumentNode node1, ParameterNode 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 @@ -1260,8 +1252,8 @@ private module LocalFlowBigStep { */ private class FlowCheckNode extends Node { FlowCheckNode() { - this instanceof CastNode or - clearsContent(this, _) + castNode(this) or + clearsContentCached(this, _) } } @@ -1275,7 +1267,7 @@ private module LocalFlowBigStep { config.isSource(node) or jumpStep(_, node, config) or additionalJumpStep(_, node, config) or - node instanceof ParameterNode or + node instanceof ParamNode or node instanceof OutNodeExt or store(_, _, node, _) or read(_, _, node) or @@ -1321,21 +1313,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 +1342,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 +1376,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 +1435,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 +1577,10 @@ private module Stage3 { pragma[nomagic] private predicate fwdFlowIn( - DataFlowCall call, ParameterNode p, Cc outercc, Cc innercc, ApOption argAp, Ap ap, + DataFlowCall call, ParamNode p, Cc outercc, Cc innercc, ApOption argAp, Ap ap, Configuration config ) { - exists(ArgumentNode 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) @@ -1631,7 +1625,7 @@ private module Stage3 { private predicate fwdFlowIsEntered( DataFlowCall call, Cc cc, ApOption argAp, Ap ap, Configuration config ) { - exists(ParameterNode p | + exists(ParamNode p | fwdFlowIn(call, p, cc, _, argAp, ap, config) and PrevStage::parameterMayFlowThrough(p, _, unbindApa(getApprox(ap)), config) ) @@ -1772,10 +1766,9 @@ private module Stage3 { pragma[nomagic] private predicate revFlowIn( - DataFlowCall call, ArgumentNode arg, boolean toReturn, ApOption returnAp, Ap ap, - Configuration config + DataFlowCall call, ArgNode arg, boolean toReturn, ApOption returnAp, Ap ap, Configuration config ) { - exists(ParameterNode p, boolean allowsFieldFlow | + exists(ParamNode p, boolean allowsFieldFlow | revFlow(p, toReturn, returnAp, ap, config) and flowIntoCall(call, arg, p, allowsFieldFlow, config) | @@ -1785,7 +1778,7 @@ private module Stage3 { pragma[nomagic] private predicate revFlowInToReturn( - DataFlowCall call, ArgumentNode 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) } @@ -1838,13 +1831,13 @@ private module Stage3 { pragma[noinline] private predicate parameterFlow( - ParameterNode 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(ParameterNode 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 @@ -2088,7 +2081,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,8 +2148,7 @@ private module Stage4 { pragma[nomagic] private predicate flowIntoCall( - DataFlowCall call, ArgumentNode node1, ParameterNode 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 @@ -2300,10 +2292,10 @@ private module Stage4 { pragma[nomagic] private predicate fwdFlowIn( - DataFlowCall call, ParameterNode p, Cc outercc, Cc innercc, ApOption argAp, Ap ap, + DataFlowCall call, ParamNode p, Cc outercc, Cc innercc, ApOption argAp, Ap ap, Configuration config ) { - exists(ArgumentNode 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) @@ -2348,7 +2340,7 @@ private module Stage4 { private predicate fwdFlowIsEntered( DataFlowCall call, Cc cc, ApOption argAp, Ap ap, Configuration config ) { - exists(ParameterNode p | + exists(ParamNode p | fwdFlowIn(call, p, cc, _, argAp, ap, config) and PrevStage::parameterMayFlowThrough(p, _, unbindApa(getApprox(ap)), config) ) @@ -2489,10 +2481,9 @@ private module Stage4 { pragma[nomagic] private predicate revFlowIn( - DataFlowCall call, ArgumentNode arg, boolean toReturn, ApOption returnAp, Ap ap, - Configuration config + DataFlowCall call, ArgNode arg, boolean toReturn, ApOption returnAp, Ap ap, Configuration config ) { - exists(ParameterNode p, boolean allowsFieldFlow | + exists(ParamNode p, boolean allowsFieldFlow | revFlow(p, toReturn, returnAp, ap, config) and flowIntoCall(call, arg, p, allowsFieldFlow, config) | @@ -2502,7 +2493,7 @@ private module Stage4 { pragma[nomagic] private predicate revFlowInToReturn( - DataFlowCall call, ArgumentNode 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) } @@ -2555,13 +2546,13 @@ private module Stage4 { pragma[noinline] private predicate parameterFlow( - ParameterNode 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(ParameterNode 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 @@ -2606,7 +2597,7 @@ private predicate nodeMayUseSummary(Node n, AccessPathApprox apa, Configuration private newtype TSummaryCtx = TSummaryCtxNone() or - TSummaryCtxSome(ParameterNode p, AccessPath ap) { + TSummaryCtxSome(ParamNode p, AccessPath ap) { Stage4::parameterMayFlowThrough(p, _, ap.getApprox(), _) } @@ -2627,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 ParameterNode p; + private ParamNode p; private AccessPath ap; SummaryCtxSome() { this = TSummaryCtxSome(p, ap) } @@ -2758,7 +2749,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 +2970,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 +3139,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 +3226,7 @@ pragma[noinline] private predicate pathIntoArg( PathNodeMid mid, int i, CallContext cc, DataFlowCall call, AccessPath ap, AccessPathApprox apa ) { - exists(ArgumentNode arg | + exists(ArgNode arg | arg = mid.getNode() and cc = mid.getCallContext() and arg.argumentOf(call, i) and @@ -3248,7 +3239,7 @@ pragma[noinline] private predicate parameterCand( DataFlowCallable callable, int i, AccessPathApprox apa, Configuration config ) { - exists(ParameterNode p | + exists(ParamNode p | Stage4::revFlow(p, _, _, apa, config) and p.isParameterOf(callable, i) ) @@ -3272,7 +3263,7 @@ private predicate pathIntoCallable0( * respectively. */ private predicate pathIntoCallable( - PathNodeMid mid, ParameterNode 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 | @@ -3568,7 +3559,7 @@ private module FlowExploration { private newtype TSummaryCtx1 = TSummaryCtx1None() or - TSummaryCtx1Param(ParameterNode p) + TSummaryCtx1Param(ParamNode p) private newtype TSummaryCtx2 = TSummaryCtx2None() or @@ -3591,7 +3582,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 +3602,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 +3616,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 +3774,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 +3788,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 +3804,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 +3818,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 +3915,7 @@ private module FlowExploration { PartialPathNodeFwd mid, int i, CallContext cc, DataFlowCall call, PartialAccessPath ap, Configuration config ) { - exists(ArgumentNode arg | + exists(ArgNode arg | arg = mid.getNode() and cc = mid.getCallContext() and arg.argumentOf(call, i) and @@ -3943,7 +3934,7 @@ private module FlowExploration { } private predicate partialPathIntoCallable( - PartialPathNodeFwd mid, ParameterNode p, CallContext outercc, CallContextCall innercc, + PartialPathNodeFwd mid, ParamNode p, CallContext outercc, CallContextCall innercc, TSummaryCtx1 sc1, TSummaryCtx2 sc2, DataFlowCall call, PartialAccessPath ap, Configuration config ) { @@ -3980,7 +3971,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(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) ) @@ -4037,7 +4028,7 @@ private module FlowExploration { apConsRev(ap, c, ap0, config) ) or - exists(ParameterNode p | + exists(ParamNode p | mid.getNode() = p and viableParamArg(_, p, node) and sc1 = mid.getSummaryCtx1() and @@ -4115,7 +4106,7 @@ private module FlowExploration { int pos, TRevSummaryCtx1Some sc1, TRevSummaryCtx2Some sc2, RevPartialAccessPath ap, Configuration config ) { - exists(PartialPathNodeRev mid, ParameterNode p | + exists(PartialPathNodeRev mid, ParamNode p | mid.getNode() = p and p.isParameterOf(_, pos) and sc1 = mid.getSummaryCtx1() and @@ -4138,7 +4129,7 @@ private module FlowExploration { pragma[nomagic] private predicate revPartialPathThroughCallable( - PartialPathNodeRev mid, ArgumentNode 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 9498e51e7e6..058d66b1496 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(ArgNode arg | fwdFlow(arg, cc, config) and viableParamArg(call, _, arg) ) @@ -515,24 +512,22 @@ private module Stage1 { pragma[nomagic] predicate viableParamArgNodeCandFwd1( - DataFlowCall call, ParameterNode p, ArgumentNode 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, ArgumentNode arg, boolean toReturn, Configuration config - ) { - exists(ParameterNode 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, ArgumentNode arg, Configuration config) { + private predicate revFlowInToReturn(DataFlowCall call, ArgNode arg, Configuration config) { revFlowIn(call, arg, true, config) } @@ -597,7 +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(ParameterNode 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 @@ -663,7 +658,7 @@ private predicate flowOutOfCallNodeCand1( pragma[nomagic] private predicate viableParamArgNodeCand1( - DataFlowCall call, ParameterNode p, ArgumentNode arg, Configuration config + DataFlowCall call, ParamNode p, ArgNode arg, Configuration config ) { Stage1::viableParamArgNodeCandFwd1(call, p, arg, config) and Stage1::revFlow(arg, config) @@ -675,7 +670,7 @@ private predicate viableParamArgNodeCand1( */ pragma[nomagic] private predicate flowIntoCallNodeCand1( - DataFlowCall call, ArgumentNode arg, ParameterNode p, Configuration config + DataFlowCall call, ArgNode arg, ParamNode p, Configuration config ) { viableParamArgNodeCand1(call, p, arg, config) and Stage1::revFlow(p, config) and @@ -735,8 +730,7 @@ private predicate flowOutOfCallNodeCand1( */ pragma[nomagic] private predicate flowIntoCallNodeCand1( - DataFlowCall call, ArgumentNode arg, ParameterNode 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 | @@ -944,10 +938,10 @@ private module Stage2 { pragma[nomagic] private predicate fwdFlowIn( - DataFlowCall call, ParameterNode p, Cc outercc, Cc innercc, ApOption argAp, Ap ap, + DataFlowCall call, ParamNode p, Cc outercc, Cc innercc, ApOption argAp, Ap ap, Configuration config ) { - exists(ArgumentNode 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) @@ -992,7 +986,7 @@ private module Stage2 { private predicate fwdFlowIsEntered( DataFlowCall call, Cc cc, ApOption argAp, Ap ap, Configuration config ) { - exists(ParameterNode p | + exists(ParamNode p | fwdFlowIn(call, p, cc, _, argAp, ap, config) and PrevStage::parameterMayFlowThrough(p, _, getApprox(ap), config) ) @@ -1133,10 +1127,9 @@ private module Stage2 { pragma[nomagic] private predicate revFlowIn( - DataFlowCall call, ArgumentNode arg, boolean toReturn, ApOption returnAp, Ap ap, - Configuration config + DataFlowCall call, ArgNode arg, boolean toReturn, ApOption returnAp, Ap ap, Configuration config ) { - exists(ParameterNode p, boolean allowsFieldFlow | + exists(ParamNode p, boolean allowsFieldFlow | revFlow(p, toReturn, returnAp, ap, config) and flowIntoCall(call, arg, p, allowsFieldFlow, config) | @@ -1146,7 +1139,7 @@ private module Stage2 { pragma[nomagic] private predicate revFlowInToReturn( - DataFlowCall call, ArgumentNode 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) } @@ -1199,13 +1192,13 @@ private module Stage2 { pragma[noinline] private predicate parameterFlow( - ParameterNode 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(ParameterNode 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 @@ -1245,8 +1238,7 @@ private predicate flowOutOfCallNodeCand2( pragma[nomagic] private predicate flowIntoCallNodeCand2( - DataFlowCall call, ArgumentNode node1, ParameterNode 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 @@ -1260,8 +1252,8 @@ private module LocalFlowBigStep { */ private class FlowCheckNode extends Node { FlowCheckNode() { - this instanceof CastNode or - clearsContent(this, _) + castNode(this) or + clearsContentCached(this, _) } } @@ -1275,7 +1267,7 @@ private module LocalFlowBigStep { config.isSource(node) or jumpStep(_, node, config) or additionalJumpStep(_, node, config) or - node instanceof ParameterNode or + node instanceof ParamNode or node instanceof OutNodeExt or store(_, _, node, _) or read(_, _, node) or @@ -1321,21 +1313,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 +1342,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 +1376,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 +1435,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 +1577,10 @@ private module Stage3 { pragma[nomagic] private predicate fwdFlowIn( - DataFlowCall call, ParameterNode p, Cc outercc, Cc innercc, ApOption argAp, Ap ap, + DataFlowCall call, ParamNode p, Cc outercc, Cc innercc, ApOption argAp, Ap ap, Configuration config ) { - exists(ArgumentNode 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) @@ -1631,7 +1625,7 @@ private module Stage3 { private predicate fwdFlowIsEntered( DataFlowCall call, Cc cc, ApOption argAp, Ap ap, Configuration config ) { - exists(ParameterNode p | + exists(ParamNode p | fwdFlowIn(call, p, cc, _, argAp, ap, config) and PrevStage::parameterMayFlowThrough(p, _, unbindApa(getApprox(ap)), config) ) @@ -1772,10 +1766,9 @@ private module Stage3 { pragma[nomagic] private predicate revFlowIn( - DataFlowCall call, ArgumentNode arg, boolean toReturn, ApOption returnAp, Ap ap, - Configuration config + DataFlowCall call, ArgNode arg, boolean toReturn, ApOption returnAp, Ap ap, Configuration config ) { - exists(ParameterNode p, boolean allowsFieldFlow | + exists(ParamNode p, boolean allowsFieldFlow | revFlow(p, toReturn, returnAp, ap, config) and flowIntoCall(call, arg, p, allowsFieldFlow, config) | @@ -1785,7 +1778,7 @@ private module Stage3 { pragma[nomagic] private predicate revFlowInToReturn( - DataFlowCall call, ArgumentNode 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) } @@ -1838,13 +1831,13 @@ private module Stage3 { pragma[noinline] private predicate parameterFlow( - ParameterNode 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(ParameterNode 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 @@ -2088,7 +2081,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,8 +2148,7 @@ private module Stage4 { pragma[nomagic] private predicate flowIntoCall( - DataFlowCall call, ArgumentNode node1, ParameterNode 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 @@ -2300,10 +2292,10 @@ private module Stage4 { pragma[nomagic] private predicate fwdFlowIn( - DataFlowCall call, ParameterNode p, Cc outercc, Cc innercc, ApOption argAp, Ap ap, + DataFlowCall call, ParamNode p, Cc outercc, Cc innercc, ApOption argAp, Ap ap, Configuration config ) { - exists(ArgumentNode 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) @@ -2348,7 +2340,7 @@ private module Stage4 { private predicate fwdFlowIsEntered( DataFlowCall call, Cc cc, ApOption argAp, Ap ap, Configuration config ) { - exists(ParameterNode p | + exists(ParamNode p | fwdFlowIn(call, p, cc, _, argAp, ap, config) and PrevStage::parameterMayFlowThrough(p, _, unbindApa(getApprox(ap)), config) ) @@ -2489,10 +2481,9 @@ private module Stage4 { pragma[nomagic] private predicate revFlowIn( - DataFlowCall call, ArgumentNode arg, boolean toReturn, ApOption returnAp, Ap ap, - Configuration config + DataFlowCall call, ArgNode arg, boolean toReturn, ApOption returnAp, Ap ap, Configuration config ) { - exists(ParameterNode p, boolean allowsFieldFlow | + exists(ParamNode p, boolean allowsFieldFlow | revFlow(p, toReturn, returnAp, ap, config) and flowIntoCall(call, arg, p, allowsFieldFlow, config) | @@ -2502,7 +2493,7 @@ private module Stage4 { pragma[nomagic] private predicate revFlowInToReturn( - DataFlowCall call, ArgumentNode 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) } @@ -2555,13 +2546,13 @@ private module Stage4 { pragma[noinline] private predicate parameterFlow( - ParameterNode 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(ParameterNode 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 @@ -2606,7 +2597,7 @@ private predicate nodeMayUseSummary(Node n, AccessPathApprox apa, Configuration private newtype TSummaryCtx = TSummaryCtxNone() or - TSummaryCtxSome(ParameterNode p, AccessPath ap) { + TSummaryCtxSome(ParamNode p, AccessPath ap) { Stage4::parameterMayFlowThrough(p, _, ap.getApprox(), _) } @@ -2627,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 ParameterNode p; + private ParamNode p; private AccessPath ap; SummaryCtxSome() { this = TSummaryCtxSome(p, ap) } @@ -2758,7 +2749,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 +2970,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 +3139,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 +3226,7 @@ pragma[noinline] private predicate pathIntoArg( PathNodeMid mid, int i, CallContext cc, DataFlowCall call, AccessPath ap, AccessPathApprox apa ) { - exists(ArgumentNode arg | + exists(ArgNode arg | arg = mid.getNode() and cc = mid.getCallContext() and arg.argumentOf(call, i) and @@ -3248,7 +3239,7 @@ pragma[noinline] private predicate parameterCand( DataFlowCallable callable, int i, AccessPathApprox apa, Configuration config ) { - exists(ParameterNode p | + exists(ParamNode p | Stage4::revFlow(p, _, _, apa, config) and p.isParameterOf(callable, i) ) @@ -3272,7 +3263,7 @@ private predicate pathIntoCallable0( * respectively. */ private predicate pathIntoCallable( - PathNodeMid mid, ParameterNode 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 | @@ -3568,7 +3559,7 @@ private module FlowExploration { private newtype TSummaryCtx1 = TSummaryCtx1None() or - TSummaryCtx1Param(ParameterNode p) + TSummaryCtx1Param(ParamNode p) private newtype TSummaryCtx2 = TSummaryCtx2None() or @@ -3591,7 +3582,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 +3602,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 +3616,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 +3774,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 +3788,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 +3804,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 +3818,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 +3915,7 @@ private module FlowExploration { PartialPathNodeFwd mid, int i, CallContext cc, DataFlowCall call, PartialAccessPath ap, Configuration config ) { - exists(ArgumentNode arg | + exists(ArgNode arg | arg = mid.getNode() and cc = mid.getCallContext() and arg.argumentOf(call, i) and @@ -3943,7 +3934,7 @@ private module FlowExploration { } private predicate partialPathIntoCallable( - PartialPathNodeFwd mid, ParameterNode p, CallContext outercc, CallContextCall innercc, + PartialPathNodeFwd mid, ParamNode p, CallContext outercc, CallContextCall innercc, TSummaryCtx1 sc1, TSummaryCtx2 sc2, DataFlowCall call, PartialAccessPath ap, Configuration config ) { @@ -3980,7 +3971,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(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) ) @@ -4037,7 +4028,7 @@ private module FlowExploration { apConsRev(ap, c, ap0, config) ) or - exists(ParameterNode p | + exists(ParamNode p | mid.getNode() = p and viableParamArg(_, p, node) and sc1 = mid.getSummaryCtx1() and @@ -4115,7 +4106,7 @@ private module FlowExploration { int pos, TRevSummaryCtx1Some sc1, TRevSummaryCtx2Some sc2, RevPartialAccessPath ap, Configuration config ) { - exists(PartialPathNodeRev mid, ParameterNode p | + exists(PartialPathNodeRev mid, ParamNode p | mid.getNode() = p and p.isParameterOf(_, pos) and sc1 = mid.getSummaryCtx1() and @@ -4138,7 +4129,7 @@ private module FlowExploration { pragma[nomagic] private predicate revPartialPathThroughCallable( - PartialPathNodeRev mid, ArgumentNode 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 9498e51e7e6..058d66b1496 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(ArgNode arg | fwdFlow(arg, cc, config) and viableParamArg(call, _, arg) ) @@ -515,24 +512,22 @@ private module Stage1 { pragma[nomagic] predicate viableParamArgNodeCandFwd1( - DataFlowCall call, ParameterNode p, ArgumentNode 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, ArgumentNode arg, boolean toReturn, Configuration config - ) { - exists(ParameterNode 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, ArgumentNode arg, Configuration config) { + private predicate revFlowInToReturn(DataFlowCall call, ArgNode arg, Configuration config) { revFlowIn(call, arg, true, config) } @@ -597,7 +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(ParameterNode 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 @@ -663,7 +658,7 @@ private predicate flowOutOfCallNodeCand1( pragma[nomagic] private predicate viableParamArgNodeCand1( - DataFlowCall call, ParameterNode p, ArgumentNode arg, Configuration config + DataFlowCall call, ParamNode p, ArgNode arg, Configuration config ) { Stage1::viableParamArgNodeCandFwd1(call, p, arg, config) and Stage1::revFlow(arg, config) @@ -675,7 +670,7 @@ private predicate viableParamArgNodeCand1( */ pragma[nomagic] private predicate flowIntoCallNodeCand1( - DataFlowCall call, ArgumentNode arg, ParameterNode p, Configuration config + DataFlowCall call, ArgNode arg, ParamNode p, Configuration config ) { viableParamArgNodeCand1(call, p, arg, config) and Stage1::revFlow(p, config) and @@ -735,8 +730,7 @@ private predicate flowOutOfCallNodeCand1( */ pragma[nomagic] private predicate flowIntoCallNodeCand1( - DataFlowCall call, ArgumentNode arg, ParameterNode 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 | @@ -944,10 +938,10 @@ private module Stage2 { pragma[nomagic] private predicate fwdFlowIn( - DataFlowCall call, ParameterNode p, Cc outercc, Cc innercc, ApOption argAp, Ap ap, + DataFlowCall call, ParamNode p, Cc outercc, Cc innercc, ApOption argAp, Ap ap, Configuration config ) { - exists(ArgumentNode 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) @@ -992,7 +986,7 @@ private module Stage2 { private predicate fwdFlowIsEntered( DataFlowCall call, Cc cc, ApOption argAp, Ap ap, Configuration config ) { - exists(ParameterNode p | + exists(ParamNode p | fwdFlowIn(call, p, cc, _, argAp, ap, config) and PrevStage::parameterMayFlowThrough(p, _, getApprox(ap), config) ) @@ -1133,10 +1127,9 @@ private module Stage2 { pragma[nomagic] private predicate revFlowIn( - DataFlowCall call, ArgumentNode arg, boolean toReturn, ApOption returnAp, Ap ap, - Configuration config + DataFlowCall call, ArgNode arg, boolean toReturn, ApOption returnAp, Ap ap, Configuration config ) { - exists(ParameterNode p, boolean allowsFieldFlow | + exists(ParamNode p, boolean allowsFieldFlow | revFlow(p, toReturn, returnAp, ap, config) and flowIntoCall(call, arg, p, allowsFieldFlow, config) | @@ -1146,7 +1139,7 @@ private module Stage2 { pragma[nomagic] private predicate revFlowInToReturn( - DataFlowCall call, ArgumentNode 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) } @@ -1199,13 +1192,13 @@ private module Stage2 { pragma[noinline] private predicate parameterFlow( - ParameterNode 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(ParameterNode 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 @@ -1245,8 +1238,7 @@ private predicate flowOutOfCallNodeCand2( pragma[nomagic] private predicate flowIntoCallNodeCand2( - DataFlowCall call, ArgumentNode node1, ParameterNode 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 @@ -1260,8 +1252,8 @@ private module LocalFlowBigStep { */ private class FlowCheckNode extends Node { FlowCheckNode() { - this instanceof CastNode or - clearsContent(this, _) + castNode(this) or + clearsContentCached(this, _) } } @@ -1275,7 +1267,7 @@ private module LocalFlowBigStep { config.isSource(node) or jumpStep(_, node, config) or additionalJumpStep(_, node, config) or - node instanceof ParameterNode or + node instanceof ParamNode or node instanceof OutNodeExt or store(_, _, node, _) or read(_, _, node) or @@ -1321,21 +1313,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 +1342,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 +1376,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 +1435,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 +1577,10 @@ private module Stage3 { pragma[nomagic] private predicate fwdFlowIn( - DataFlowCall call, ParameterNode p, Cc outercc, Cc innercc, ApOption argAp, Ap ap, + DataFlowCall call, ParamNode p, Cc outercc, Cc innercc, ApOption argAp, Ap ap, Configuration config ) { - exists(ArgumentNode 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) @@ -1631,7 +1625,7 @@ private module Stage3 { private predicate fwdFlowIsEntered( DataFlowCall call, Cc cc, ApOption argAp, Ap ap, Configuration config ) { - exists(ParameterNode p | + exists(ParamNode p | fwdFlowIn(call, p, cc, _, argAp, ap, config) and PrevStage::parameterMayFlowThrough(p, _, unbindApa(getApprox(ap)), config) ) @@ -1772,10 +1766,9 @@ private module Stage3 { pragma[nomagic] private predicate revFlowIn( - DataFlowCall call, ArgumentNode arg, boolean toReturn, ApOption returnAp, Ap ap, - Configuration config + DataFlowCall call, ArgNode arg, boolean toReturn, ApOption returnAp, Ap ap, Configuration config ) { - exists(ParameterNode p, boolean allowsFieldFlow | + exists(ParamNode p, boolean allowsFieldFlow | revFlow(p, toReturn, returnAp, ap, config) and flowIntoCall(call, arg, p, allowsFieldFlow, config) | @@ -1785,7 +1778,7 @@ private module Stage3 { pragma[nomagic] private predicate revFlowInToReturn( - DataFlowCall call, ArgumentNode 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) } @@ -1838,13 +1831,13 @@ private module Stage3 { pragma[noinline] private predicate parameterFlow( - ParameterNode 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(ParameterNode 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 @@ -2088,7 +2081,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,8 +2148,7 @@ private module Stage4 { pragma[nomagic] private predicate flowIntoCall( - DataFlowCall call, ArgumentNode node1, ParameterNode 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 @@ -2300,10 +2292,10 @@ private module Stage4 { pragma[nomagic] private predicate fwdFlowIn( - DataFlowCall call, ParameterNode p, Cc outercc, Cc innercc, ApOption argAp, Ap ap, + DataFlowCall call, ParamNode p, Cc outercc, Cc innercc, ApOption argAp, Ap ap, Configuration config ) { - exists(ArgumentNode 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) @@ -2348,7 +2340,7 @@ private module Stage4 { private predicate fwdFlowIsEntered( DataFlowCall call, Cc cc, ApOption argAp, Ap ap, Configuration config ) { - exists(ParameterNode p | + exists(ParamNode p | fwdFlowIn(call, p, cc, _, argAp, ap, config) and PrevStage::parameterMayFlowThrough(p, _, unbindApa(getApprox(ap)), config) ) @@ -2489,10 +2481,9 @@ private module Stage4 { pragma[nomagic] private predicate revFlowIn( - DataFlowCall call, ArgumentNode arg, boolean toReturn, ApOption returnAp, Ap ap, - Configuration config + DataFlowCall call, ArgNode arg, boolean toReturn, ApOption returnAp, Ap ap, Configuration config ) { - exists(ParameterNode p, boolean allowsFieldFlow | + exists(ParamNode p, boolean allowsFieldFlow | revFlow(p, toReturn, returnAp, ap, config) and flowIntoCall(call, arg, p, allowsFieldFlow, config) | @@ -2502,7 +2493,7 @@ private module Stage4 { pragma[nomagic] private predicate revFlowInToReturn( - DataFlowCall call, ArgumentNode 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) } @@ -2555,13 +2546,13 @@ private module Stage4 { pragma[noinline] private predicate parameterFlow( - ParameterNode 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(ParameterNode 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 @@ -2606,7 +2597,7 @@ private predicate nodeMayUseSummary(Node n, AccessPathApprox apa, Configuration private newtype TSummaryCtx = TSummaryCtxNone() or - TSummaryCtxSome(ParameterNode p, AccessPath ap) { + TSummaryCtxSome(ParamNode p, AccessPath ap) { Stage4::parameterMayFlowThrough(p, _, ap.getApprox(), _) } @@ -2627,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 ParameterNode p; + private ParamNode p; private AccessPath ap; SummaryCtxSome() { this = TSummaryCtxSome(p, ap) } @@ -2758,7 +2749,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 +2970,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 +3139,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 +3226,7 @@ pragma[noinline] private predicate pathIntoArg( PathNodeMid mid, int i, CallContext cc, DataFlowCall call, AccessPath ap, AccessPathApprox apa ) { - exists(ArgumentNode arg | + exists(ArgNode arg | arg = mid.getNode() and cc = mid.getCallContext() and arg.argumentOf(call, i) and @@ -3248,7 +3239,7 @@ pragma[noinline] private predicate parameterCand( DataFlowCallable callable, int i, AccessPathApprox apa, Configuration config ) { - exists(ParameterNode p | + exists(ParamNode p | Stage4::revFlow(p, _, _, apa, config) and p.isParameterOf(callable, i) ) @@ -3272,7 +3263,7 @@ private predicate pathIntoCallable0( * respectively. */ private predicate pathIntoCallable( - PathNodeMid mid, ParameterNode 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 | @@ -3568,7 +3559,7 @@ private module FlowExploration { private newtype TSummaryCtx1 = TSummaryCtx1None() or - TSummaryCtx1Param(ParameterNode p) + TSummaryCtx1Param(ParamNode p) private newtype TSummaryCtx2 = TSummaryCtx2None() or @@ -3591,7 +3582,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 +3602,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 +3616,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 +3774,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 +3788,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 +3804,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 +3818,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 +3915,7 @@ private module FlowExploration { PartialPathNodeFwd mid, int i, CallContext cc, DataFlowCall call, PartialAccessPath ap, Configuration config ) { - exists(ArgumentNode arg | + exists(ArgNode arg | arg = mid.getNode() and cc = mid.getCallContext() and arg.argumentOf(call, i) and @@ -3943,7 +3934,7 @@ private module FlowExploration { } private predicate partialPathIntoCallable( - PartialPathNodeFwd mid, ParameterNode p, CallContext outercc, CallContextCall innercc, + PartialPathNodeFwd mid, ParamNode p, CallContext outercc, CallContextCall innercc, TSummaryCtx1 sc1, TSummaryCtx2 sc2, DataFlowCall call, PartialAccessPath ap, Configuration config ) { @@ -3980,7 +3971,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(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) ) @@ -4037,7 +4028,7 @@ private module FlowExploration { apConsRev(ap, c, ap0, config) ) or - exists(ParameterNode p | + exists(ParamNode p | mid.getNode() = p and viableParamArg(_, p, node) and sc1 = mid.getSummaryCtx1() and @@ -4115,7 +4106,7 @@ private module FlowExploration { int pos, TRevSummaryCtx1Some sc1, TRevSummaryCtx2Some sc2, RevPartialAccessPath ap, Configuration config ) { - exists(PartialPathNodeRev mid, ParameterNode p | + exists(PartialPathNodeRev mid, ParamNode p | mid.getNode() = p and p.isParameterOf(_, pos) and sc1 = mid.getSummaryCtx1() and @@ -4138,7 +4129,7 @@ private module FlowExploration { pragma[nomagic] private predicate revPartialPathThroughCallable( - PartialPathNodeRev mid, ArgumentNode 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 966c30038cc..462e89ac9ed 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,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, ParameterNode p) { + private predicate viableParamNonLambda(DataFlowCall call, int i, ParamNode p) { p.isParameterOf(viableCallable(call), i) } - private predicate viableParamLambda(DataFlowCall call, int i, ParameterNode p) { + private predicate viableParamLambda(DataFlowCall call, int i, ParamNode p) { p.isParameterOf(viableCallableLambda(call, _), i) } - private predicate viableParamArgNonLambda(DataFlowCall call, ParameterNode p, ArgumentNode 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, ParameterNode p, ArgumentNode arg) { + private predicate viableParamArgLambda(DataFlowCall call, ParamNode p, ArgNode arg) { exists(int i | viableParamLambda(call, i, p) and arg.argumentOf(call, i) @@ -118,8 +118,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 ArgNode or node instanceof ReturnNode + 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 @@ -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 | @@ -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 @@ -176,7 +176,7 @@ private module LambdaFlow { ) or // flow into a callable - exists(ParameterNode 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 @@ -227,7 +227,7 @@ private module LambdaFlow { pragma[nomagic] predicate revLambdaFlowIn( - DataFlowCall lambdaCall, LambdaCallKind kind, ParameterNode p, DataFlowType t, boolean toJump, + DataFlowCall lambdaCall, LambdaCallKind kind, ParamNode p, DataFlowType t, boolean toJump, DataFlowCallOption lastCall ) { revLambdaFlow(lambdaCall, kind, p, t, false, toJump, lastCall) @@ -242,6 +242,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 ArgNode + } + + cached + predicate hiddenNode(Node n) { nodeIsHidden(n) } + + cached + OutNodeExt getAnOutNodeExt(DataFlowCall call, ReturnKindExt k) { + result = getAnOutNode(call, k.(ValueReturnKind).getKind()) + or + exists(ArgNode 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(ParamNode 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 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 + // 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 +344,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, ParamNode p) { p.isParameterOf(viableCallableExt(call), i) } @@ -270,11 +353,11 @@ private module Cached { * dispatch into account. */ cached - predicate viableParamArg(DataFlowCall call, ParameterNode p, ArgumentNode arg) { + predicate viableParamArg(DataFlowCall call, ParamNode p, ArgNode 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 +395,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(ParamNode p, Node node, boolean read) { p = node and read = false or @@ -325,30 +408,30 @@ 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(ArgNode arg | parameterValueFlowArgCand(p, arg, false) and argumentValueFlowsThroughCand(arg, node, read) ) or // flow through: no read inside method - exists(ArgumentNode arg | + exists(ArgNode arg | parameterValueFlowArgCand(p, arg, read) and argumentValueFlowsThroughCand(arg, node, false) ) } pragma[nomagic] - private predicate parameterValueFlowArgCand(ParameterNode p, ArgumentNode arg, boolean read) { + private predicate parameterValueFlowArgCand(ParamNode p, ArgNode arg, boolean read) { parameterValueFlowCand(p, arg, read) } pragma[nomagic] - predicate parameterValueFlowsToPreUpdateCand(ParameterNode p, PostUpdateNode n) { + predicate parameterValueFlowsToPreUpdateCand(ParamNode p, PostUpdateNode n) { parameterValueFlowCand(p, n.getPreUpdateNode(), false) } @@ -360,7 +443,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(ParamNode p, ReturnKind kind, boolean read) { exists(ReturnNode ret | parameterValueFlowCand(p, ret, read) and kind = ret.getKind() @@ -369,9 +452,9 @@ private module Cached { pragma[nomagic] private predicate argumentValueFlowsThroughCand0( - DataFlowCall call, ArgumentNode arg, ReturnKind kind, boolean read + DataFlowCall call, ArgNode arg, ReturnKind kind, boolean read ) { - exists(ParameterNode param | viableParamArg(call, param, arg) | + exists(ParamNode param | viableParamArg(call, param, arg) | parameterValueFlowReturnCand(param, kind, read) ) } @@ -382,14 +465,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(ArgNode 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(ParamNode p, Node n) { parameterValueFlowCand(p, n, _) and ( parameterValueFlowReturnCand(p, _, _) @@ -416,21 +499,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(ParamNode 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(ParamNode p, Node node, ReadStepTypesOption read) { p = node and Cand::cand(p, _) and read = TReadStepTypesNone() @@ -447,7 +530,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,34 +538,32 @@ private module Cached { pragma[nomagic] private predicate parameterValueFlow0_0( - ReadStepTypesOption mustBeNone, ParameterNode p, Node node, ReadStepTypesOption read + ReadStepTypesOption mustBeNone, ParamNode p, Node node, ReadStepTypesOption read ) { // flow through: no prior read - exists(ArgumentNode arg | + exists(ArgNode arg | parameterValueFlowArg(p, arg, mustBeNone) and argumentValueFlowsThrough(arg, read, node) ) or // flow through: no read inside method - exists(ArgumentNode arg | + exists(ArgNode arg | parameterValueFlowArg(p, arg, read) and argumentValueFlowsThrough(arg, mustBeNone, node) ) } pragma[nomagic] - private predicate parameterValueFlowArg( - ParameterNode p, ArgumentNode 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, ArgumentNode arg, ReturnKind kind, ReadStepTypesOption read + DataFlowCall call, ArgNode arg, ReturnKind kind, ReadStepTypesOption read ) { - exists(ParameterNode param | viableParamArg(call, param, arg) | + exists(ParamNode param | viableParamArg(call, param, arg) | parameterValueFlowReturn(param, kind, read) ) } @@ -496,18 +577,18 @@ private module Cached { * container type, and the content type. */ pragma[nomagic] - predicate argumentValueFlowsThrough(ArgumentNode 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) | // 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 +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(ArgumentNode arg, Content c, Node out) { + predicate getterStep(ArgNode arg, Content c, Node out) { argumentValueFlowsThrough(arg, TReadStepTypesSome(_, c, _), out) } @@ -529,7 +610,7 @@ private module Cached { * container type, and the content type. */ private predicate parameterValueFlowReturn( - ParameterNode p, ReturnKind kind, ReadStepTypesOption read + ParamNode p, ReturnKind kind, ReadStepTypesOption read ) { exists(ReturnNode ret | parameterValueFlow(p, ret, read) and @@ -553,7 +634,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 +692,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 +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. */ - cached - predicate parameterValueFlowsToPreUpdate(ParameterNode p, PostUpdateNode n) { + private predicate parameterValueFlowsToPreUpdate(ParamNode p, PostUpdateNode n) { parameterValueFlow(p, n.getPreUpdateNode(), TReadStepTypesNone()) } @@ -644,9 +724,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 +734,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 +761,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 +772,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.(ArgNode).argumentOf(c, _) and + simpleLocalFlowStep(toPre.(ArgNode), 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 +794,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 +816,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(ParamNode p | p.isParameterOf(_, pos)) } cached newtype TBooleanOption = @@ -761,23 +851,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 +936,7 @@ class CallContextSomeCall extends CallContextCall, TSomeCall { override string toString() { result = "CcSomeCall" } override predicate relevantFor(DataFlowCallable callable) { - exists(ParameterNode p | getNodeEnclosingCallable(p) = callable) + exists(ParamNode p | getNodeEnclosingCallable(p) = callable) } override predicate matchesCall(DataFlowCall call) { any() } @@ -866,7 +948,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 +981,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 +995,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 ParamNode extends Node { + ParamNode() { 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 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) } +} + /** * 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 +1033,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 +1046,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 +1057,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 +1067,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 +1093,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 +1123,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 +1144,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 +1195,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..058d66b1496 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(ArgNode arg | fwdFlow(arg, cc, config) and viableParamArg(call, _, arg) ) @@ -515,24 +512,22 @@ private module Stage1 { pragma[nomagic] predicate viableParamArgNodeCandFwd1( - DataFlowCall call, ParameterNode p, ArgumentNode 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, ArgumentNode arg, boolean toReturn, Configuration config - ) { - exists(ParameterNode 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, ArgumentNode arg, Configuration config) { + private predicate revFlowInToReturn(DataFlowCall call, ArgNode arg, Configuration config) { revFlowIn(call, arg, true, config) } @@ -597,7 +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(ParameterNode 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 @@ -663,7 +658,7 @@ private predicate flowOutOfCallNodeCand1( pragma[nomagic] private predicate viableParamArgNodeCand1( - DataFlowCall call, ParameterNode p, ArgumentNode arg, Configuration config + DataFlowCall call, ParamNode p, ArgNode arg, Configuration config ) { Stage1::viableParamArgNodeCandFwd1(call, p, arg, config) and Stage1::revFlow(arg, config) @@ -675,7 +670,7 @@ private predicate viableParamArgNodeCand1( */ pragma[nomagic] private predicate flowIntoCallNodeCand1( - DataFlowCall call, ArgumentNode arg, ParameterNode p, Configuration config + DataFlowCall call, ArgNode arg, ParamNode p, Configuration config ) { viableParamArgNodeCand1(call, p, arg, config) and Stage1::revFlow(p, config) and @@ -735,8 +730,7 @@ private predicate flowOutOfCallNodeCand1( */ pragma[nomagic] private predicate flowIntoCallNodeCand1( - DataFlowCall call, ArgumentNode arg, ParameterNode 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 | @@ -944,10 +938,10 @@ private module Stage2 { pragma[nomagic] private predicate fwdFlowIn( - DataFlowCall call, ParameterNode p, Cc outercc, Cc innercc, ApOption argAp, Ap ap, + DataFlowCall call, ParamNode p, Cc outercc, Cc innercc, ApOption argAp, Ap ap, Configuration config ) { - exists(ArgumentNode 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) @@ -992,7 +986,7 @@ private module Stage2 { private predicate fwdFlowIsEntered( DataFlowCall call, Cc cc, ApOption argAp, Ap ap, Configuration config ) { - exists(ParameterNode p | + exists(ParamNode p | fwdFlowIn(call, p, cc, _, argAp, ap, config) and PrevStage::parameterMayFlowThrough(p, _, getApprox(ap), config) ) @@ -1133,10 +1127,9 @@ private module Stage2 { pragma[nomagic] private predicate revFlowIn( - DataFlowCall call, ArgumentNode arg, boolean toReturn, ApOption returnAp, Ap ap, - Configuration config + DataFlowCall call, ArgNode arg, boolean toReturn, ApOption returnAp, Ap ap, Configuration config ) { - exists(ParameterNode p, boolean allowsFieldFlow | + exists(ParamNode p, boolean allowsFieldFlow | revFlow(p, toReturn, returnAp, ap, config) and flowIntoCall(call, arg, p, allowsFieldFlow, config) | @@ -1146,7 +1139,7 @@ private module Stage2 { pragma[nomagic] private predicate revFlowInToReturn( - DataFlowCall call, ArgumentNode 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) } @@ -1199,13 +1192,13 @@ private module Stage2 { pragma[noinline] private predicate parameterFlow( - ParameterNode 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(ParameterNode 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 @@ -1245,8 +1238,7 @@ private predicate flowOutOfCallNodeCand2( pragma[nomagic] private predicate flowIntoCallNodeCand2( - DataFlowCall call, ArgumentNode node1, ParameterNode 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 @@ -1260,8 +1252,8 @@ private module LocalFlowBigStep { */ private class FlowCheckNode extends Node { FlowCheckNode() { - this instanceof CastNode or - clearsContent(this, _) + castNode(this) or + clearsContentCached(this, _) } } @@ -1275,7 +1267,7 @@ private module LocalFlowBigStep { config.isSource(node) or jumpStep(_, node, config) or additionalJumpStep(_, node, config) or - node instanceof ParameterNode or + node instanceof ParamNode or node instanceof OutNodeExt or store(_, _, node, _) or read(_, _, node) or @@ -1321,21 +1313,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 +1342,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 +1376,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 +1435,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 +1577,10 @@ private module Stage3 { pragma[nomagic] private predicate fwdFlowIn( - DataFlowCall call, ParameterNode p, Cc outercc, Cc innercc, ApOption argAp, Ap ap, + DataFlowCall call, ParamNode p, Cc outercc, Cc innercc, ApOption argAp, Ap ap, Configuration config ) { - exists(ArgumentNode 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) @@ -1631,7 +1625,7 @@ private module Stage3 { private predicate fwdFlowIsEntered( DataFlowCall call, Cc cc, ApOption argAp, Ap ap, Configuration config ) { - exists(ParameterNode p | + exists(ParamNode p | fwdFlowIn(call, p, cc, _, argAp, ap, config) and PrevStage::parameterMayFlowThrough(p, _, unbindApa(getApprox(ap)), config) ) @@ -1772,10 +1766,9 @@ private module Stage3 { pragma[nomagic] private predicate revFlowIn( - DataFlowCall call, ArgumentNode arg, boolean toReturn, ApOption returnAp, Ap ap, - Configuration config + DataFlowCall call, ArgNode arg, boolean toReturn, ApOption returnAp, Ap ap, Configuration config ) { - exists(ParameterNode p, boolean allowsFieldFlow | + exists(ParamNode p, boolean allowsFieldFlow | revFlow(p, toReturn, returnAp, ap, config) and flowIntoCall(call, arg, p, allowsFieldFlow, config) | @@ -1785,7 +1778,7 @@ private module Stage3 { pragma[nomagic] private predicate revFlowInToReturn( - DataFlowCall call, ArgumentNode 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) } @@ -1838,13 +1831,13 @@ private module Stage3 { pragma[noinline] private predicate parameterFlow( - ParameterNode 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(ParameterNode 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 @@ -2088,7 +2081,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,8 +2148,7 @@ private module Stage4 { pragma[nomagic] private predicate flowIntoCall( - DataFlowCall call, ArgumentNode node1, ParameterNode 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 @@ -2300,10 +2292,10 @@ private module Stage4 { pragma[nomagic] private predicate fwdFlowIn( - DataFlowCall call, ParameterNode p, Cc outercc, Cc innercc, ApOption argAp, Ap ap, + DataFlowCall call, ParamNode p, Cc outercc, Cc innercc, ApOption argAp, Ap ap, Configuration config ) { - exists(ArgumentNode 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) @@ -2348,7 +2340,7 @@ private module Stage4 { private predicate fwdFlowIsEntered( DataFlowCall call, Cc cc, ApOption argAp, Ap ap, Configuration config ) { - exists(ParameterNode p | + exists(ParamNode p | fwdFlowIn(call, p, cc, _, argAp, ap, config) and PrevStage::parameterMayFlowThrough(p, _, unbindApa(getApprox(ap)), config) ) @@ -2489,10 +2481,9 @@ private module Stage4 { pragma[nomagic] private predicate revFlowIn( - DataFlowCall call, ArgumentNode arg, boolean toReturn, ApOption returnAp, Ap ap, - Configuration config + DataFlowCall call, ArgNode arg, boolean toReturn, ApOption returnAp, Ap ap, Configuration config ) { - exists(ParameterNode p, boolean allowsFieldFlow | + exists(ParamNode p, boolean allowsFieldFlow | revFlow(p, toReturn, returnAp, ap, config) and flowIntoCall(call, arg, p, allowsFieldFlow, config) | @@ -2502,7 +2493,7 @@ private module Stage4 { pragma[nomagic] private predicate revFlowInToReturn( - DataFlowCall call, ArgumentNode 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) } @@ -2555,13 +2546,13 @@ private module Stage4 { pragma[noinline] private predicate parameterFlow( - ParameterNode 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(ParameterNode 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 @@ -2606,7 +2597,7 @@ private predicate nodeMayUseSummary(Node n, AccessPathApprox apa, Configuration private newtype TSummaryCtx = TSummaryCtxNone() or - TSummaryCtxSome(ParameterNode p, AccessPath ap) { + TSummaryCtxSome(ParamNode p, AccessPath ap) { Stage4::parameterMayFlowThrough(p, _, ap.getApprox(), _) } @@ -2627,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 ParameterNode p; + private ParamNode p; private AccessPath ap; SummaryCtxSome() { this = TSummaryCtxSome(p, ap) } @@ -2758,7 +2749,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 +2970,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 +3139,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 +3226,7 @@ pragma[noinline] private predicate pathIntoArg( PathNodeMid mid, int i, CallContext cc, DataFlowCall call, AccessPath ap, AccessPathApprox apa ) { - exists(ArgumentNode arg | + exists(ArgNode arg | arg = mid.getNode() and cc = mid.getCallContext() and arg.argumentOf(call, i) and @@ -3248,7 +3239,7 @@ pragma[noinline] private predicate parameterCand( DataFlowCallable callable, int i, AccessPathApprox apa, Configuration config ) { - exists(ParameterNode p | + exists(ParamNode p | Stage4::revFlow(p, _, _, apa, config) and p.isParameterOf(callable, i) ) @@ -3272,7 +3263,7 @@ private predicate pathIntoCallable0( * respectively. */ private predicate pathIntoCallable( - PathNodeMid mid, ParameterNode 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 | @@ -3568,7 +3559,7 @@ private module FlowExploration { private newtype TSummaryCtx1 = TSummaryCtx1None() or - TSummaryCtx1Param(ParameterNode p) + TSummaryCtx1Param(ParamNode p) private newtype TSummaryCtx2 = TSummaryCtx2None() or @@ -3591,7 +3582,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 +3602,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 +3616,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 +3774,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 +3788,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 +3804,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 +3818,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 +3915,7 @@ private module FlowExploration { PartialPathNodeFwd mid, int i, CallContext cc, DataFlowCall call, PartialAccessPath ap, Configuration config ) { - exists(ArgumentNode arg | + exists(ArgNode arg | arg = mid.getNode() and cc = mid.getCallContext() and arg.argumentOf(call, i) and @@ -3943,7 +3934,7 @@ private module FlowExploration { } private predicate partialPathIntoCallable( - PartialPathNodeFwd mid, ParameterNode p, CallContext outercc, CallContextCall innercc, + PartialPathNodeFwd mid, ParamNode p, CallContext outercc, CallContextCall innercc, TSummaryCtx1 sc1, TSummaryCtx2 sc2, DataFlowCall call, PartialAccessPath ap, Configuration config ) { @@ -3980,7 +3971,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(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) ) @@ -4037,7 +4028,7 @@ private module FlowExploration { apConsRev(ap, c, ap0, config) ) or - exists(ParameterNode p | + exists(ParamNode p | mid.getNode() = p and viableParamArg(_, p, node) and sc1 = mid.getSummaryCtx1() and @@ -4115,7 +4106,7 @@ private module FlowExploration { int pos, TRevSummaryCtx1Some sc1, TRevSummaryCtx2Some sc2, RevPartialAccessPath ap, Configuration config ) { - exists(PartialPathNodeRev mid, ParameterNode p | + exists(PartialPathNodeRev mid, ParamNode p | mid.getNode() = p and p.isParameterOf(_, pos) and sc1 = mid.getSummaryCtx1() and @@ -4138,7 +4129,7 @@ private module FlowExploration { pragma[nomagic] private predicate revPartialPathThroughCallable( - PartialPathNodeRev mid, ArgumentNode 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/DataFlowUtil.qll b/cpp/ql/src/semmle/code/cpp/dataflow/internal/DataFlowUtil.qll index 558b43cb0da..5db9a84c80a 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/DataFlowImpl.qll b/cpp/ql/src/semmle/code/cpp/ir/dataflow/internal/DataFlowImpl.qll index 9498e51e7e6..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 @@ -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(ArgNode arg | fwdFlow(arg, cc, config) and viableParamArg(call, _, arg) ) @@ -515,24 +512,22 @@ private module Stage1 { pragma[nomagic] predicate viableParamArgNodeCandFwd1( - DataFlowCall call, ParameterNode p, ArgumentNode 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, ArgumentNode arg, boolean toReturn, Configuration config - ) { - exists(ParameterNode 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, ArgumentNode arg, Configuration config) { + private predicate revFlowInToReturn(DataFlowCall call, ArgNode arg, Configuration config) { revFlowIn(call, arg, true, config) } @@ -597,7 +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(ParameterNode 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 @@ -663,7 +658,7 @@ private predicate flowOutOfCallNodeCand1( pragma[nomagic] private predicate viableParamArgNodeCand1( - DataFlowCall call, ParameterNode p, ArgumentNode arg, Configuration config + DataFlowCall call, ParamNode p, ArgNode arg, Configuration config ) { Stage1::viableParamArgNodeCandFwd1(call, p, arg, config) and Stage1::revFlow(arg, config) @@ -675,7 +670,7 @@ private predicate viableParamArgNodeCand1( */ pragma[nomagic] private predicate flowIntoCallNodeCand1( - DataFlowCall call, ArgumentNode arg, ParameterNode p, Configuration config + DataFlowCall call, ArgNode arg, ParamNode p, Configuration config ) { viableParamArgNodeCand1(call, p, arg, config) and Stage1::revFlow(p, config) and @@ -735,8 +730,7 @@ private predicate flowOutOfCallNodeCand1( */ pragma[nomagic] private predicate flowIntoCallNodeCand1( - DataFlowCall call, ArgumentNode arg, ParameterNode 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 | @@ -944,10 +938,10 @@ private module Stage2 { pragma[nomagic] private predicate fwdFlowIn( - DataFlowCall call, ParameterNode p, Cc outercc, Cc innercc, ApOption argAp, Ap ap, + DataFlowCall call, ParamNode p, Cc outercc, Cc innercc, ApOption argAp, Ap ap, Configuration config ) { - exists(ArgumentNode 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) @@ -992,7 +986,7 @@ private module Stage2 { private predicate fwdFlowIsEntered( DataFlowCall call, Cc cc, ApOption argAp, Ap ap, Configuration config ) { - exists(ParameterNode p | + exists(ParamNode p | fwdFlowIn(call, p, cc, _, argAp, ap, config) and PrevStage::parameterMayFlowThrough(p, _, getApprox(ap), config) ) @@ -1133,10 +1127,9 @@ private module Stage2 { pragma[nomagic] private predicate revFlowIn( - DataFlowCall call, ArgumentNode arg, boolean toReturn, ApOption returnAp, Ap ap, - Configuration config + DataFlowCall call, ArgNode arg, boolean toReturn, ApOption returnAp, Ap ap, Configuration config ) { - exists(ParameterNode p, boolean allowsFieldFlow | + exists(ParamNode p, boolean allowsFieldFlow | revFlow(p, toReturn, returnAp, ap, config) and flowIntoCall(call, arg, p, allowsFieldFlow, config) | @@ -1146,7 +1139,7 @@ private module Stage2 { pragma[nomagic] private predicate revFlowInToReturn( - DataFlowCall call, ArgumentNode 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) } @@ -1199,13 +1192,13 @@ private module Stage2 { pragma[noinline] private predicate parameterFlow( - ParameterNode 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(ParameterNode 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 @@ -1245,8 +1238,7 @@ private predicate flowOutOfCallNodeCand2( pragma[nomagic] private predicate flowIntoCallNodeCand2( - DataFlowCall call, ArgumentNode node1, ParameterNode 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 @@ -1260,8 +1252,8 @@ private module LocalFlowBigStep { */ private class FlowCheckNode extends Node { FlowCheckNode() { - this instanceof CastNode or - clearsContent(this, _) + castNode(this) or + clearsContentCached(this, _) } } @@ -1275,7 +1267,7 @@ private module LocalFlowBigStep { config.isSource(node) or jumpStep(_, node, config) or additionalJumpStep(_, node, config) or - node instanceof ParameterNode or + node instanceof ParamNode or node instanceof OutNodeExt or store(_, _, node, _) or read(_, _, node) or @@ -1321,21 +1313,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 +1342,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 +1376,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 +1435,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 +1577,10 @@ private module Stage3 { pragma[nomagic] private predicate fwdFlowIn( - DataFlowCall call, ParameterNode p, Cc outercc, Cc innercc, ApOption argAp, Ap ap, + DataFlowCall call, ParamNode p, Cc outercc, Cc innercc, ApOption argAp, Ap ap, Configuration config ) { - exists(ArgumentNode 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) @@ -1631,7 +1625,7 @@ private module Stage3 { private predicate fwdFlowIsEntered( DataFlowCall call, Cc cc, ApOption argAp, Ap ap, Configuration config ) { - exists(ParameterNode p | + exists(ParamNode p | fwdFlowIn(call, p, cc, _, argAp, ap, config) and PrevStage::parameterMayFlowThrough(p, _, unbindApa(getApprox(ap)), config) ) @@ -1772,10 +1766,9 @@ private module Stage3 { pragma[nomagic] private predicate revFlowIn( - DataFlowCall call, ArgumentNode arg, boolean toReturn, ApOption returnAp, Ap ap, - Configuration config + DataFlowCall call, ArgNode arg, boolean toReturn, ApOption returnAp, Ap ap, Configuration config ) { - exists(ParameterNode p, boolean allowsFieldFlow | + exists(ParamNode p, boolean allowsFieldFlow | revFlow(p, toReturn, returnAp, ap, config) and flowIntoCall(call, arg, p, allowsFieldFlow, config) | @@ -1785,7 +1778,7 @@ private module Stage3 { pragma[nomagic] private predicate revFlowInToReturn( - DataFlowCall call, ArgumentNode 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) } @@ -1838,13 +1831,13 @@ private module Stage3 { pragma[noinline] private predicate parameterFlow( - ParameterNode 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(ParameterNode 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 @@ -2088,7 +2081,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,8 +2148,7 @@ private module Stage4 { pragma[nomagic] private predicate flowIntoCall( - DataFlowCall call, ArgumentNode node1, ParameterNode 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 @@ -2300,10 +2292,10 @@ private module Stage4 { pragma[nomagic] private predicate fwdFlowIn( - DataFlowCall call, ParameterNode p, Cc outercc, Cc innercc, ApOption argAp, Ap ap, + DataFlowCall call, ParamNode p, Cc outercc, Cc innercc, ApOption argAp, Ap ap, Configuration config ) { - exists(ArgumentNode 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) @@ -2348,7 +2340,7 @@ private module Stage4 { private predicate fwdFlowIsEntered( DataFlowCall call, Cc cc, ApOption argAp, Ap ap, Configuration config ) { - exists(ParameterNode p | + exists(ParamNode p | fwdFlowIn(call, p, cc, _, argAp, ap, config) and PrevStage::parameterMayFlowThrough(p, _, unbindApa(getApprox(ap)), config) ) @@ -2489,10 +2481,9 @@ private module Stage4 { pragma[nomagic] private predicate revFlowIn( - DataFlowCall call, ArgumentNode arg, boolean toReturn, ApOption returnAp, Ap ap, - Configuration config + DataFlowCall call, ArgNode arg, boolean toReturn, ApOption returnAp, Ap ap, Configuration config ) { - exists(ParameterNode p, boolean allowsFieldFlow | + exists(ParamNode p, boolean allowsFieldFlow | revFlow(p, toReturn, returnAp, ap, config) and flowIntoCall(call, arg, p, allowsFieldFlow, config) | @@ -2502,7 +2493,7 @@ private module Stage4 { pragma[nomagic] private predicate revFlowInToReturn( - DataFlowCall call, ArgumentNode 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) } @@ -2555,13 +2546,13 @@ private module Stage4 { pragma[noinline] private predicate parameterFlow( - ParameterNode 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(ParameterNode 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 @@ -2606,7 +2597,7 @@ private predicate nodeMayUseSummary(Node n, AccessPathApprox apa, Configuration private newtype TSummaryCtx = TSummaryCtxNone() or - TSummaryCtxSome(ParameterNode p, AccessPath ap) { + TSummaryCtxSome(ParamNode p, AccessPath ap) { Stage4::parameterMayFlowThrough(p, _, ap.getApprox(), _) } @@ -2627,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 ParameterNode p; + private ParamNode p; private AccessPath ap; SummaryCtxSome() { this = TSummaryCtxSome(p, ap) } @@ -2758,7 +2749,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 +2970,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 +3139,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 +3226,7 @@ pragma[noinline] private predicate pathIntoArg( PathNodeMid mid, int i, CallContext cc, DataFlowCall call, AccessPath ap, AccessPathApprox apa ) { - exists(ArgumentNode arg | + exists(ArgNode arg | arg = mid.getNode() and cc = mid.getCallContext() and arg.argumentOf(call, i) and @@ -3248,7 +3239,7 @@ pragma[noinline] private predicate parameterCand( DataFlowCallable callable, int i, AccessPathApprox apa, Configuration config ) { - exists(ParameterNode p | + exists(ParamNode p | Stage4::revFlow(p, _, _, apa, config) and p.isParameterOf(callable, i) ) @@ -3272,7 +3263,7 @@ private predicate pathIntoCallable0( * respectively. */ private predicate pathIntoCallable( - PathNodeMid mid, ParameterNode 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 | @@ -3568,7 +3559,7 @@ private module FlowExploration { private newtype TSummaryCtx1 = TSummaryCtx1None() or - TSummaryCtx1Param(ParameterNode p) + TSummaryCtx1Param(ParamNode p) private newtype TSummaryCtx2 = TSummaryCtx2None() or @@ -3591,7 +3582,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 +3602,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 +3616,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 +3774,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 +3788,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 +3804,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 +3818,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 +3915,7 @@ private module FlowExploration { PartialPathNodeFwd mid, int i, CallContext cc, DataFlowCall call, PartialAccessPath ap, Configuration config ) { - exists(ArgumentNode arg | + exists(ArgNode arg | arg = mid.getNode() and cc = mid.getCallContext() and arg.argumentOf(call, i) and @@ -3943,7 +3934,7 @@ private module FlowExploration { } private predicate partialPathIntoCallable( - PartialPathNodeFwd mid, ParameterNode p, CallContext outercc, CallContextCall innercc, + PartialPathNodeFwd mid, ParamNode p, CallContext outercc, CallContextCall innercc, TSummaryCtx1 sc1, TSummaryCtx2 sc2, DataFlowCall call, PartialAccessPath ap, Configuration config ) { @@ -3980,7 +3971,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(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) ) @@ -4037,7 +4028,7 @@ private module FlowExploration { apConsRev(ap, c, ap0, config) ) or - exists(ParameterNode p | + exists(ParamNode p | mid.getNode() = p and viableParamArg(_, p, node) and sc1 = mid.getSummaryCtx1() and @@ -4115,7 +4106,7 @@ private module FlowExploration { int pos, TRevSummaryCtx1Some sc1, TRevSummaryCtx2Some sc2, RevPartialAccessPath ap, Configuration config ) { - exists(PartialPathNodeRev mid, ParameterNode p | + exists(PartialPathNodeRev mid, ParamNode p | mid.getNode() = p and p.isParameterOf(_, pos) and sc1 = mid.getSummaryCtx1() and @@ -4138,7 +4129,7 @@ private module FlowExploration { pragma[nomagic] private predicate revPartialPathThroughCallable( - PartialPathNodeRev mid, ArgumentNode 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 9498e51e7e6..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 @@ -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(ArgNode arg | fwdFlow(arg, cc, config) and viableParamArg(call, _, arg) ) @@ -515,24 +512,22 @@ private module Stage1 { pragma[nomagic] predicate viableParamArgNodeCandFwd1( - DataFlowCall call, ParameterNode p, ArgumentNode 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, ArgumentNode arg, boolean toReturn, Configuration config - ) { - exists(ParameterNode 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, ArgumentNode arg, Configuration config) { + private predicate revFlowInToReturn(DataFlowCall call, ArgNode arg, Configuration config) { revFlowIn(call, arg, true, config) } @@ -597,7 +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(ParameterNode 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 @@ -663,7 +658,7 @@ private predicate flowOutOfCallNodeCand1( pragma[nomagic] private predicate viableParamArgNodeCand1( - DataFlowCall call, ParameterNode p, ArgumentNode arg, Configuration config + DataFlowCall call, ParamNode p, ArgNode arg, Configuration config ) { Stage1::viableParamArgNodeCandFwd1(call, p, arg, config) and Stage1::revFlow(arg, config) @@ -675,7 +670,7 @@ private predicate viableParamArgNodeCand1( */ pragma[nomagic] private predicate flowIntoCallNodeCand1( - DataFlowCall call, ArgumentNode arg, ParameterNode p, Configuration config + DataFlowCall call, ArgNode arg, ParamNode p, Configuration config ) { viableParamArgNodeCand1(call, p, arg, config) and Stage1::revFlow(p, config) and @@ -735,8 +730,7 @@ private predicate flowOutOfCallNodeCand1( */ pragma[nomagic] private predicate flowIntoCallNodeCand1( - DataFlowCall call, ArgumentNode arg, ParameterNode 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 | @@ -944,10 +938,10 @@ private module Stage2 { pragma[nomagic] private predicate fwdFlowIn( - DataFlowCall call, ParameterNode p, Cc outercc, Cc innercc, ApOption argAp, Ap ap, + DataFlowCall call, ParamNode p, Cc outercc, Cc innercc, ApOption argAp, Ap ap, Configuration config ) { - exists(ArgumentNode 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) @@ -992,7 +986,7 @@ private module Stage2 { private predicate fwdFlowIsEntered( DataFlowCall call, Cc cc, ApOption argAp, Ap ap, Configuration config ) { - exists(ParameterNode p | + exists(ParamNode p | fwdFlowIn(call, p, cc, _, argAp, ap, config) and PrevStage::parameterMayFlowThrough(p, _, getApprox(ap), config) ) @@ -1133,10 +1127,9 @@ private module Stage2 { pragma[nomagic] private predicate revFlowIn( - DataFlowCall call, ArgumentNode arg, boolean toReturn, ApOption returnAp, Ap ap, - Configuration config + DataFlowCall call, ArgNode arg, boolean toReturn, ApOption returnAp, Ap ap, Configuration config ) { - exists(ParameterNode p, boolean allowsFieldFlow | + exists(ParamNode p, boolean allowsFieldFlow | revFlow(p, toReturn, returnAp, ap, config) and flowIntoCall(call, arg, p, allowsFieldFlow, config) | @@ -1146,7 +1139,7 @@ private module Stage2 { pragma[nomagic] private predicate revFlowInToReturn( - DataFlowCall call, ArgumentNode 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) } @@ -1199,13 +1192,13 @@ private module Stage2 { pragma[noinline] private predicate parameterFlow( - ParameterNode 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(ParameterNode 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 @@ -1245,8 +1238,7 @@ private predicate flowOutOfCallNodeCand2( pragma[nomagic] private predicate flowIntoCallNodeCand2( - DataFlowCall call, ArgumentNode node1, ParameterNode 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 @@ -1260,8 +1252,8 @@ private module LocalFlowBigStep { */ private class FlowCheckNode extends Node { FlowCheckNode() { - this instanceof CastNode or - clearsContent(this, _) + castNode(this) or + clearsContentCached(this, _) } } @@ -1275,7 +1267,7 @@ private module LocalFlowBigStep { config.isSource(node) or jumpStep(_, node, config) or additionalJumpStep(_, node, config) or - node instanceof ParameterNode or + node instanceof ParamNode or node instanceof OutNodeExt or store(_, _, node, _) or read(_, _, node) or @@ -1321,21 +1313,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 +1342,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 +1376,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 +1435,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 +1577,10 @@ private module Stage3 { pragma[nomagic] private predicate fwdFlowIn( - DataFlowCall call, ParameterNode p, Cc outercc, Cc innercc, ApOption argAp, Ap ap, + DataFlowCall call, ParamNode p, Cc outercc, Cc innercc, ApOption argAp, Ap ap, Configuration config ) { - exists(ArgumentNode 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) @@ -1631,7 +1625,7 @@ private module Stage3 { private predicate fwdFlowIsEntered( DataFlowCall call, Cc cc, ApOption argAp, Ap ap, Configuration config ) { - exists(ParameterNode p | + exists(ParamNode p | fwdFlowIn(call, p, cc, _, argAp, ap, config) and PrevStage::parameterMayFlowThrough(p, _, unbindApa(getApprox(ap)), config) ) @@ -1772,10 +1766,9 @@ private module Stage3 { pragma[nomagic] private predicate revFlowIn( - DataFlowCall call, ArgumentNode arg, boolean toReturn, ApOption returnAp, Ap ap, - Configuration config + DataFlowCall call, ArgNode arg, boolean toReturn, ApOption returnAp, Ap ap, Configuration config ) { - exists(ParameterNode p, boolean allowsFieldFlow | + exists(ParamNode p, boolean allowsFieldFlow | revFlow(p, toReturn, returnAp, ap, config) and flowIntoCall(call, arg, p, allowsFieldFlow, config) | @@ -1785,7 +1778,7 @@ private module Stage3 { pragma[nomagic] private predicate revFlowInToReturn( - DataFlowCall call, ArgumentNode 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) } @@ -1838,13 +1831,13 @@ private module Stage3 { pragma[noinline] private predicate parameterFlow( - ParameterNode 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(ParameterNode 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 @@ -2088,7 +2081,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,8 +2148,7 @@ private module Stage4 { pragma[nomagic] private predicate flowIntoCall( - DataFlowCall call, ArgumentNode node1, ParameterNode 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 @@ -2300,10 +2292,10 @@ private module Stage4 { pragma[nomagic] private predicate fwdFlowIn( - DataFlowCall call, ParameterNode p, Cc outercc, Cc innercc, ApOption argAp, Ap ap, + DataFlowCall call, ParamNode p, Cc outercc, Cc innercc, ApOption argAp, Ap ap, Configuration config ) { - exists(ArgumentNode 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) @@ -2348,7 +2340,7 @@ private module Stage4 { private predicate fwdFlowIsEntered( DataFlowCall call, Cc cc, ApOption argAp, Ap ap, Configuration config ) { - exists(ParameterNode p | + exists(ParamNode p | fwdFlowIn(call, p, cc, _, argAp, ap, config) and PrevStage::parameterMayFlowThrough(p, _, unbindApa(getApprox(ap)), config) ) @@ -2489,10 +2481,9 @@ private module Stage4 { pragma[nomagic] private predicate revFlowIn( - DataFlowCall call, ArgumentNode arg, boolean toReturn, ApOption returnAp, Ap ap, - Configuration config + DataFlowCall call, ArgNode arg, boolean toReturn, ApOption returnAp, Ap ap, Configuration config ) { - exists(ParameterNode p, boolean allowsFieldFlow | + exists(ParamNode p, boolean allowsFieldFlow | revFlow(p, toReturn, returnAp, ap, config) and flowIntoCall(call, arg, p, allowsFieldFlow, config) | @@ -2502,7 +2493,7 @@ private module Stage4 { pragma[nomagic] private predicate revFlowInToReturn( - DataFlowCall call, ArgumentNode 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) } @@ -2555,13 +2546,13 @@ private module Stage4 { pragma[noinline] private predicate parameterFlow( - ParameterNode 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(ParameterNode 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 @@ -2606,7 +2597,7 @@ private predicate nodeMayUseSummary(Node n, AccessPathApprox apa, Configuration private newtype TSummaryCtx = TSummaryCtxNone() or - TSummaryCtxSome(ParameterNode p, AccessPath ap) { + TSummaryCtxSome(ParamNode p, AccessPath ap) { Stage4::parameterMayFlowThrough(p, _, ap.getApprox(), _) } @@ -2627,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 ParameterNode p; + private ParamNode p; private AccessPath ap; SummaryCtxSome() { this = TSummaryCtxSome(p, ap) } @@ -2758,7 +2749,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 +2970,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 +3139,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 +3226,7 @@ pragma[noinline] private predicate pathIntoArg( PathNodeMid mid, int i, CallContext cc, DataFlowCall call, AccessPath ap, AccessPathApprox apa ) { - exists(ArgumentNode arg | + exists(ArgNode arg | arg = mid.getNode() and cc = mid.getCallContext() and arg.argumentOf(call, i) and @@ -3248,7 +3239,7 @@ pragma[noinline] private predicate parameterCand( DataFlowCallable callable, int i, AccessPathApprox apa, Configuration config ) { - exists(ParameterNode p | + exists(ParamNode p | Stage4::revFlow(p, _, _, apa, config) and p.isParameterOf(callable, i) ) @@ -3272,7 +3263,7 @@ private predicate pathIntoCallable0( * respectively. */ private predicate pathIntoCallable( - PathNodeMid mid, ParameterNode 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 | @@ -3568,7 +3559,7 @@ private module FlowExploration { private newtype TSummaryCtx1 = TSummaryCtx1None() or - TSummaryCtx1Param(ParameterNode p) + TSummaryCtx1Param(ParamNode p) private newtype TSummaryCtx2 = TSummaryCtx2None() or @@ -3591,7 +3582,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 +3602,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 +3616,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 +3774,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 +3788,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 +3804,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 +3818,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 +3915,7 @@ private module FlowExploration { PartialPathNodeFwd mid, int i, CallContext cc, DataFlowCall call, PartialAccessPath ap, Configuration config ) { - exists(ArgumentNode arg | + exists(ArgNode arg | arg = mid.getNode() and cc = mid.getCallContext() and arg.argumentOf(call, i) and @@ -3943,7 +3934,7 @@ private module FlowExploration { } private predicate partialPathIntoCallable( - PartialPathNodeFwd mid, ParameterNode p, CallContext outercc, CallContextCall innercc, + PartialPathNodeFwd mid, ParamNode p, CallContext outercc, CallContextCall innercc, TSummaryCtx1 sc1, TSummaryCtx2 sc2, DataFlowCall call, PartialAccessPath ap, Configuration config ) { @@ -3980,7 +3971,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(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) ) @@ -4037,7 +4028,7 @@ private module FlowExploration { apConsRev(ap, c, ap0, config) ) or - exists(ParameterNode p | + exists(ParamNode p | mid.getNode() = p and viableParamArg(_, p, node) and sc1 = mid.getSummaryCtx1() and @@ -4115,7 +4106,7 @@ private module FlowExploration { int pos, TRevSummaryCtx1Some sc1, TRevSummaryCtx2Some sc2, RevPartialAccessPath ap, Configuration config ) { - exists(PartialPathNodeRev mid, ParameterNode p | + exists(PartialPathNodeRev mid, ParamNode p | mid.getNode() = p and p.isParameterOf(_, pos) and sc1 = mid.getSummaryCtx1() and @@ -4138,7 +4129,7 @@ private module FlowExploration { pragma[nomagic] private predicate revPartialPathThroughCallable( - PartialPathNodeRev mid, ArgumentNode 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 9498e51e7e6..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 @@ -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(ArgNode arg | fwdFlow(arg, cc, config) and viableParamArg(call, _, arg) ) @@ -515,24 +512,22 @@ private module Stage1 { pragma[nomagic] predicate viableParamArgNodeCandFwd1( - DataFlowCall call, ParameterNode p, ArgumentNode 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, ArgumentNode arg, boolean toReturn, Configuration config - ) { - exists(ParameterNode 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, ArgumentNode arg, Configuration config) { + private predicate revFlowInToReturn(DataFlowCall call, ArgNode arg, Configuration config) { revFlowIn(call, arg, true, config) } @@ -597,7 +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(ParameterNode 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 @@ -663,7 +658,7 @@ private predicate flowOutOfCallNodeCand1( pragma[nomagic] private predicate viableParamArgNodeCand1( - DataFlowCall call, ParameterNode p, ArgumentNode arg, Configuration config + DataFlowCall call, ParamNode p, ArgNode arg, Configuration config ) { Stage1::viableParamArgNodeCandFwd1(call, p, arg, config) and Stage1::revFlow(arg, config) @@ -675,7 +670,7 @@ private predicate viableParamArgNodeCand1( */ pragma[nomagic] private predicate flowIntoCallNodeCand1( - DataFlowCall call, ArgumentNode arg, ParameterNode p, Configuration config + DataFlowCall call, ArgNode arg, ParamNode p, Configuration config ) { viableParamArgNodeCand1(call, p, arg, config) and Stage1::revFlow(p, config) and @@ -735,8 +730,7 @@ private predicate flowOutOfCallNodeCand1( */ pragma[nomagic] private predicate flowIntoCallNodeCand1( - DataFlowCall call, ArgumentNode arg, ParameterNode 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 | @@ -944,10 +938,10 @@ private module Stage2 { pragma[nomagic] private predicate fwdFlowIn( - DataFlowCall call, ParameterNode p, Cc outercc, Cc innercc, ApOption argAp, Ap ap, + DataFlowCall call, ParamNode p, Cc outercc, Cc innercc, ApOption argAp, Ap ap, Configuration config ) { - exists(ArgumentNode 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) @@ -992,7 +986,7 @@ private module Stage2 { private predicate fwdFlowIsEntered( DataFlowCall call, Cc cc, ApOption argAp, Ap ap, Configuration config ) { - exists(ParameterNode p | + exists(ParamNode p | fwdFlowIn(call, p, cc, _, argAp, ap, config) and PrevStage::parameterMayFlowThrough(p, _, getApprox(ap), config) ) @@ -1133,10 +1127,9 @@ private module Stage2 { pragma[nomagic] private predicate revFlowIn( - DataFlowCall call, ArgumentNode arg, boolean toReturn, ApOption returnAp, Ap ap, - Configuration config + DataFlowCall call, ArgNode arg, boolean toReturn, ApOption returnAp, Ap ap, Configuration config ) { - exists(ParameterNode p, boolean allowsFieldFlow | + exists(ParamNode p, boolean allowsFieldFlow | revFlow(p, toReturn, returnAp, ap, config) and flowIntoCall(call, arg, p, allowsFieldFlow, config) | @@ -1146,7 +1139,7 @@ private module Stage2 { pragma[nomagic] private predicate revFlowInToReturn( - DataFlowCall call, ArgumentNode 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) } @@ -1199,13 +1192,13 @@ private module Stage2 { pragma[noinline] private predicate parameterFlow( - ParameterNode 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(ParameterNode 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 @@ -1245,8 +1238,7 @@ private predicate flowOutOfCallNodeCand2( pragma[nomagic] private predicate flowIntoCallNodeCand2( - DataFlowCall call, ArgumentNode node1, ParameterNode 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 @@ -1260,8 +1252,8 @@ private module LocalFlowBigStep { */ private class FlowCheckNode extends Node { FlowCheckNode() { - this instanceof CastNode or - clearsContent(this, _) + castNode(this) or + clearsContentCached(this, _) } } @@ -1275,7 +1267,7 @@ private module LocalFlowBigStep { config.isSource(node) or jumpStep(_, node, config) or additionalJumpStep(_, node, config) or - node instanceof ParameterNode or + node instanceof ParamNode or node instanceof OutNodeExt or store(_, _, node, _) or read(_, _, node) or @@ -1321,21 +1313,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 +1342,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 +1376,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 +1435,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 +1577,10 @@ private module Stage3 { pragma[nomagic] private predicate fwdFlowIn( - DataFlowCall call, ParameterNode p, Cc outercc, Cc innercc, ApOption argAp, Ap ap, + DataFlowCall call, ParamNode p, Cc outercc, Cc innercc, ApOption argAp, Ap ap, Configuration config ) { - exists(ArgumentNode 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) @@ -1631,7 +1625,7 @@ private module Stage3 { private predicate fwdFlowIsEntered( DataFlowCall call, Cc cc, ApOption argAp, Ap ap, Configuration config ) { - exists(ParameterNode p | + exists(ParamNode p | fwdFlowIn(call, p, cc, _, argAp, ap, config) and PrevStage::parameterMayFlowThrough(p, _, unbindApa(getApprox(ap)), config) ) @@ -1772,10 +1766,9 @@ private module Stage3 { pragma[nomagic] private predicate revFlowIn( - DataFlowCall call, ArgumentNode arg, boolean toReturn, ApOption returnAp, Ap ap, - Configuration config + DataFlowCall call, ArgNode arg, boolean toReturn, ApOption returnAp, Ap ap, Configuration config ) { - exists(ParameterNode p, boolean allowsFieldFlow | + exists(ParamNode p, boolean allowsFieldFlow | revFlow(p, toReturn, returnAp, ap, config) and flowIntoCall(call, arg, p, allowsFieldFlow, config) | @@ -1785,7 +1778,7 @@ private module Stage3 { pragma[nomagic] private predicate revFlowInToReturn( - DataFlowCall call, ArgumentNode 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) } @@ -1838,13 +1831,13 @@ private module Stage3 { pragma[noinline] private predicate parameterFlow( - ParameterNode 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(ParameterNode 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 @@ -2088,7 +2081,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,8 +2148,7 @@ private module Stage4 { pragma[nomagic] private predicate flowIntoCall( - DataFlowCall call, ArgumentNode node1, ParameterNode 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 @@ -2300,10 +2292,10 @@ private module Stage4 { pragma[nomagic] private predicate fwdFlowIn( - DataFlowCall call, ParameterNode p, Cc outercc, Cc innercc, ApOption argAp, Ap ap, + DataFlowCall call, ParamNode p, Cc outercc, Cc innercc, ApOption argAp, Ap ap, Configuration config ) { - exists(ArgumentNode 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) @@ -2348,7 +2340,7 @@ private module Stage4 { private predicate fwdFlowIsEntered( DataFlowCall call, Cc cc, ApOption argAp, Ap ap, Configuration config ) { - exists(ParameterNode p | + exists(ParamNode p | fwdFlowIn(call, p, cc, _, argAp, ap, config) and PrevStage::parameterMayFlowThrough(p, _, unbindApa(getApprox(ap)), config) ) @@ -2489,10 +2481,9 @@ private module Stage4 { pragma[nomagic] private predicate revFlowIn( - DataFlowCall call, ArgumentNode arg, boolean toReturn, ApOption returnAp, Ap ap, - Configuration config + DataFlowCall call, ArgNode arg, boolean toReturn, ApOption returnAp, Ap ap, Configuration config ) { - exists(ParameterNode p, boolean allowsFieldFlow | + exists(ParamNode p, boolean allowsFieldFlow | revFlow(p, toReturn, returnAp, ap, config) and flowIntoCall(call, arg, p, allowsFieldFlow, config) | @@ -2502,7 +2493,7 @@ private module Stage4 { pragma[nomagic] private predicate revFlowInToReturn( - DataFlowCall call, ArgumentNode 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) } @@ -2555,13 +2546,13 @@ private module Stage4 { pragma[noinline] private predicate parameterFlow( - ParameterNode 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(ParameterNode 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 @@ -2606,7 +2597,7 @@ private predicate nodeMayUseSummary(Node n, AccessPathApprox apa, Configuration private newtype TSummaryCtx = TSummaryCtxNone() or - TSummaryCtxSome(ParameterNode p, AccessPath ap) { + TSummaryCtxSome(ParamNode p, AccessPath ap) { Stage4::parameterMayFlowThrough(p, _, ap.getApprox(), _) } @@ -2627,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 ParameterNode p; + private ParamNode p; private AccessPath ap; SummaryCtxSome() { this = TSummaryCtxSome(p, ap) } @@ -2758,7 +2749,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 +2970,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 +3139,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 +3226,7 @@ pragma[noinline] private predicate pathIntoArg( PathNodeMid mid, int i, CallContext cc, DataFlowCall call, AccessPath ap, AccessPathApprox apa ) { - exists(ArgumentNode arg | + exists(ArgNode arg | arg = mid.getNode() and cc = mid.getCallContext() and arg.argumentOf(call, i) and @@ -3248,7 +3239,7 @@ pragma[noinline] private predicate parameterCand( DataFlowCallable callable, int i, AccessPathApprox apa, Configuration config ) { - exists(ParameterNode p | + exists(ParamNode p | Stage4::revFlow(p, _, _, apa, config) and p.isParameterOf(callable, i) ) @@ -3272,7 +3263,7 @@ private predicate pathIntoCallable0( * respectively. */ private predicate pathIntoCallable( - PathNodeMid mid, ParameterNode 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 | @@ -3568,7 +3559,7 @@ private module FlowExploration { private newtype TSummaryCtx1 = TSummaryCtx1None() or - TSummaryCtx1Param(ParameterNode p) + TSummaryCtx1Param(ParamNode p) private newtype TSummaryCtx2 = TSummaryCtx2None() or @@ -3591,7 +3582,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 +3602,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 +3616,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 +3774,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 +3788,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 +3804,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 +3818,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 +3915,7 @@ private module FlowExploration { PartialPathNodeFwd mid, int i, CallContext cc, DataFlowCall call, PartialAccessPath ap, Configuration config ) { - exists(ArgumentNode arg | + exists(ArgNode arg | arg = mid.getNode() and cc = mid.getCallContext() and arg.argumentOf(call, i) and @@ -3943,7 +3934,7 @@ private module FlowExploration { } private predicate partialPathIntoCallable( - PartialPathNodeFwd mid, ParameterNode p, CallContext outercc, CallContextCall innercc, + PartialPathNodeFwd mid, ParamNode p, CallContext outercc, CallContextCall innercc, TSummaryCtx1 sc1, TSummaryCtx2 sc2, DataFlowCall call, PartialAccessPath ap, Configuration config ) { @@ -3980,7 +3971,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(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) ) @@ -4037,7 +4028,7 @@ private module FlowExploration { apConsRev(ap, c, ap0, config) ) or - exists(ParameterNode p | + exists(ParamNode p | mid.getNode() = p and viableParamArg(_, p, node) and sc1 = mid.getSummaryCtx1() and @@ -4115,7 +4106,7 @@ private module FlowExploration { int pos, TRevSummaryCtx1Some sc1, TRevSummaryCtx2Some sc2, RevPartialAccessPath ap, Configuration config ) { - exists(PartialPathNodeRev mid, ParameterNode p | + exists(PartialPathNodeRev mid, ParamNode p | mid.getNode() = p and p.isParameterOf(_, pos) and sc1 = mid.getSummaryCtx1() and @@ -4138,7 +4129,7 @@ private module FlowExploration { pragma[nomagic] private predicate revPartialPathThroughCallable( - PartialPathNodeRev mid, ArgumentNode 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 9498e51e7e6..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 @@ -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(ArgNode arg | fwdFlow(arg, cc, config) and viableParamArg(call, _, arg) ) @@ -515,24 +512,22 @@ private module Stage1 { pragma[nomagic] predicate viableParamArgNodeCandFwd1( - DataFlowCall call, ParameterNode p, ArgumentNode 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, ArgumentNode arg, boolean toReturn, Configuration config - ) { - exists(ParameterNode 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, ArgumentNode arg, Configuration config) { + private predicate revFlowInToReturn(DataFlowCall call, ArgNode arg, Configuration config) { revFlowIn(call, arg, true, config) } @@ -597,7 +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(ParameterNode 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 @@ -663,7 +658,7 @@ private predicate flowOutOfCallNodeCand1( pragma[nomagic] private predicate viableParamArgNodeCand1( - DataFlowCall call, ParameterNode p, ArgumentNode arg, Configuration config + DataFlowCall call, ParamNode p, ArgNode arg, Configuration config ) { Stage1::viableParamArgNodeCandFwd1(call, p, arg, config) and Stage1::revFlow(arg, config) @@ -675,7 +670,7 @@ private predicate viableParamArgNodeCand1( */ pragma[nomagic] private predicate flowIntoCallNodeCand1( - DataFlowCall call, ArgumentNode arg, ParameterNode p, Configuration config + DataFlowCall call, ArgNode arg, ParamNode p, Configuration config ) { viableParamArgNodeCand1(call, p, arg, config) and Stage1::revFlow(p, config) and @@ -735,8 +730,7 @@ private predicate flowOutOfCallNodeCand1( */ pragma[nomagic] private predicate flowIntoCallNodeCand1( - DataFlowCall call, ArgumentNode arg, ParameterNode 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 | @@ -944,10 +938,10 @@ private module Stage2 { pragma[nomagic] private predicate fwdFlowIn( - DataFlowCall call, ParameterNode p, Cc outercc, Cc innercc, ApOption argAp, Ap ap, + DataFlowCall call, ParamNode p, Cc outercc, Cc innercc, ApOption argAp, Ap ap, Configuration config ) { - exists(ArgumentNode 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) @@ -992,7 +986,7 @@ private module Stage2 { private predicate fwdFlowIsEntered( DataFlowCall call, Cc cc, ApOption argAp, Ap ap, Configuration config ) { - exists(ParameterNode p | + exists(ParamNode p | fwdFlowIn(call, p, cc, _, argAp, ap, config) and PrevStage::parameterMayFlowThrough(p, _, getApprox(ap), config) ) @@ -1133,10 +1127,9 @@ private module Stage2 { pragma[nomagic] private predicate revFlowIn( - DataFlowCall call, ArgumentNode arg, boolean toReturn, ApOption returnAp, Ap ap, - Configuration config + DataFlowCall call, ArgNode arg, boolean toReturn, ApOption returnAp, Ap ap, Configuration config ) { - exists(ParameterNode p, boolean allowsFieldFlow | + exists(ParamNode p, boolean allowsFieldFlow | revFlow(p, toReturn, returnAp, ap, config) and flowIntoCall(call, arg, p, allowsFieldFlow, config) | @@ -1146,7 +1139,7 @@ private module Stage2 { pragma[nomagic] private predicate revFlowInToReturn( - DataFlowCall call, ArgumentNode 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) } @@ -1199,13 +1192,13 @@ private module Stage2 { pragma[noinline] private predicate parameterFlow( - ParameterNode 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(ParameterNode 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 @@ -1245,8 +1238,7 @@ private predicate flowOutOfCallNodeCand2( pragma[nomagic] private predicate flowIntoCallNodeCand2( - DataFlowCall call, ArgumentNode node1, ParameterNode 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 @@ -1260,8 +1252,8 @@ private module LocalFlowBigStep { */ private class FlowCheckNode extends Node { FlowCheckNode() { - this instanceof CastNode or - clearsContent(this, _) + castNode(this) or + clearsContentCached(this, _) } } @@ -1275,7 +1267,7 @@ private module LocalFlowBigStep { config.isSource(node) or jumpStep(_, node, config) or additionalJumpStep(_, node, config) or - node instanceof ParameterNode or + node instanceof ParamNode or node instanceof OutNodeExt or store(_, _, node, _) or read(_, _, node) or @@ -1321,21 +1313,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 +1342,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 +1376,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 +1435,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 +1577,10 @@ private module Stage3 { pragma[nomagic] private predicate fwdFlowIn( - DataFlowCall call, ParameterNode p, Cc outercc, Cc innercc, ApOption argAp, Ap ap, + DataFlowCall call, ParamNode p, Cc outercc, Cc innercc, ApOption argAp, Ap ap, Configuration config ) { - exists(ArgumentNode 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) @@ -1631,7 +1625,7 @@ private module Stage3 { private predicate fwdFlowIsEntered( DataFlowCall call, Cc cc, ApOption argAp, Ap ap, Configuration config ) { - exists(ParameterNode p | + exists(ParamNode p | fwdFlowIn(call, p, cc, _, argAp, ap, config) and PrevStage::parameterMayFlowThrough(p, _, unbindApa(getApprox(ap)), config) ) @@ -1772,10 +1766,9 @@ private module Stage3 { pragma[nomagic] private predicate revFlowIn( - DataFlowCall call, ArgumentNode arg, boolean toReturn, ApOption returnAp, Ap ap, - Configuration config + DataFlowCall call, ArgNode arg, boolean toReturn, ApOption returnAp, Ap ap, Configuration config ) { - exists(ParameterNode p, boolean allowsFieldFlow | + exists(ParamNode p, boolean allowsFieldFlow | revFlow(p, toReturn, returnAp, ap, config) and flowIntoCall(call, arg, p, allowsFieldFlow, config) | @@ -1785,7 +1778,7 @@ private module Stage3 { pragma[nomagic] private predicate revFlowInToReturn( - DataFlowCall call, ArgumentNode 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) } @@ -1838,13 +1831,13 @@ private module Stage3 { pragma[noinline] private predicate parameterFlow( - ParameterNode 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(ParameterNode 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 @@ -2088,7 +2081,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,8 +2148,7 @@ private module Stage4 { pragma[nomagic] private predicate flowIntoCall( - DataFlowCall call, ArgumentNode node1, ParameterNode 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 @@ -2300,10 +2292,10 @@ private module Stage4 { pragma[nomagic] private predicate fwdFlowIn( - DataFlowCall call, ParameterNode p, Cc outercc, Cc innercc, ApOption argAp, Ap ap, + DataFlowCall call, ParamNode p, Cc outercc, Cc innercc, ApOption argAp, Ap ap, Configuration config ) { - exists(ArgumentNode 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) @@ -2348,7 +2340,7 @@ private module Stage4 { private predicate fwdFlowIsEntered( DataFlowCall call, Cc cc, ApOption argAp, Ap ap, Configuration config ) { - exists(ParameterNode p | + exists(ParamNode p | fwdFlowIn(call, p, cc, _, argAp, ap, config) and PrevStage::parameterMayFlowThrough(p, _, unbindApa(getApprox(ap)), config) ) @@ -2489,10 +2481,9 @@ private module Stage4 { pragma[nomagic] private predicate revFlowIn( - DataFlowCall call, ArgumentNode arg, boolean toReturn, ApOption returnAp, Ap ap, - Configuration config + DataFlowCall call, ArgNode arg, boolean toReturn, ApOption returnAp, Ap ap, Configuration config ) { - exists(ParameterNode p, boolean allowsFieldFlow | + exists(ParamNode p, boolean allowsFieldFlow | revFlow(p, toReturn, returnAp, ap, config) and flowIntoCall(call, arg, p, allowsFieldFlow, config) | @@ -2502,7 +2493,7 @@ private module Stage4 { pragma[nomagic] private predicate revFlowInToReturn( - DataFlowCall call, ArgumentNode 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) } @@ -2555,13 +2546,13 @@ private module Stage4 { pragma[noinline] private predicate parameterFlow( - ParameterNode 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(ParameterNode 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 @@ -2606,7 +2597,7 @@ private predicate nodeMayUseSummary(Node n, AccessPathApprox apa, Configuration private newtype TSummaryCtx = TSummaryCtxNone() or - TSummaryCtxSome(ParameterNode p, AccessPath ap) { + TSummaryCtxSome(ParamNode p, AccessPath ap) { Stage4::parameterMayFlowThrough(p, _, ap.getApprox(), _) } @@ -2627,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 ParameterNode p; + private ParamNode p; private AccessPath ap; SummaryCtxSome() { this = TSummaryCtxSome(p, ap) } @@ -2758,7 +2749,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 +2970,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 +3139,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 +3226,7 @@ pragma[noinline] private predicate pathIntoArg( PathNodeMid mid, int i, CallContext cc, DataFlowCall call, AccessPath ap, AccessPathApprox apa ) { - exists(ArgumentNode arg | + exists(ArgNode arg | arg = mid.getNode() and cc = mid.getCallContext() and arg.argumentOf(call, i) and @@ -3248,7 +3239,7 @@ pragma[noinline] private predicate parameterCand( DataFlowCallable callable, int i, AccessPathApprox apa, Configuration config ) { - exists(ParameterNode p | + exists(ParamNode p | Stage4::revFlow(p, _, _, apa, config) and p.isParameterOf(callable, i) ) @@ -3272,7 +3263,7 @@ private predicate pathIntoCallable0( * respectively. */ private predicate pathIntoCallable( - PathNodeMid mid, ParameterNode 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 | @@ -3568,7 +3559,7 @@ private module FlowExploration { private newtype TSummaryCtx1 = TSummaryCtx1None() or - TSummaryCtx1Param(ParameterNode p) + TSummaryCtx1Param(ParamNode p) private newtype TSummaryCtx2 = TSummaryCtx2None() or @@ -3591,7 +3582,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 +3602,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 +3616,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 +3774,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 +3788,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 +3804,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 +3818,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 +3915,7 @@ private module FlowExploration { PartialPathNodeFwd mid, int i, CallContext cc, DataFlowCall call, PartialAccessPath ap, Configuration config ) { - exists(ArgumentNode arg | + exists(ArgNode arg | arg = mid.getNode() and cc = mid.getCallContext() and arg.argumentOf(call, i) and @@ -3943,7 +3934,7 @@ private module FlowExploration { } private predicate partialPathIntoCallable( - PartialPathNodeFwd mid, ParameterNode p, CallContext outercc, CallContextCall innercc, + PartialPathNodeFwd mid, ParamNode p, CallContext outercc, CallContextCall innercc, TSummaryCtx1 sc1, TSummaryCtx2 sc2, DataFlowCall call, PartialAccessPath ap, Configuration config ) { @@ -3980,7 +3971,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(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) ) @@ -4037,7 +4028,7 @@ private module FlowExploration { apConsRev(ap, c, ap0, config) ) or - exists(ParameterNode p | + exists(ParamNode p | mid.getNode() = p and viableParamArg(_, p, node) and sc1 = mid.getSummaryCtx1() and @@ -4115,7 +4106,7 @@ private module FlowExploration { int pos, TRevSummaryCtx1Some sc1, TRevSummaryCtx2Some sc2, RevPartialAccessPath ap, Configuration config ) { - exists(PartialPathNodeRev mid, ParameterNode p | + exists(PartialPathNodeRev mid, ParamNode p | mid.getNode() = p and p.isParameterOf(_, pos) and sc1 = mid.getSummaryCtx1() and @@ -4138,7 +4129,7 @@ private module FlowExploration { pragma[nomagic] private predicate revPartialPathThroughCallable( - PartialPathNodeRev mid, ArgumentNode 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 966c30038cc..462e89ac9ed 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,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, ParameterNode p) { + private predicate viableParamNonLambda(DataFlowCall call, int i, ParamNode p) { p.isParameterOf(viableCallable(call), i) } - private predicate viableParamLambda(DataFlowCall call, int i, ParameterNode p) { + private predicate viableParamLambda(DataFlowCall call, int i, ParamNode p) { p.isParameterOf(viableCallableLambda(call, _), i) } - private predicate viableParamArgNonLambda(DataFlowCall call, ParameterNode p, ArgumentNode 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, ParameterNode p, ArgumentNode arg) { + private predicate viableParamArgLambda(DataFlowCall call, ParamNode p, ArgNode arg) { exists(int i | viableParamLambda(call, i, p) and arg.argumentOf(call, i) @@ -118,8 +118,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 ArgNode or node instanceof ReturnNode + 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 @@ -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 | @@ -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 @@ -176,7 +176,7 @@ private module LambdaFlow { ) or // flow into a callable - exists(ParameterNode 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 @@ -227,7 +227,7 @@ private module LambdaFlow { pragma[nomagic] predicate revLambdaFlowIn( - DataFlowCall lambdaCall, LambdaCallKind kind, ParameterNode p, DataFlowType t, boolean toJump, + DataFlowCall lambdaCall, LambdaCallKind kind, ParamNode p, DataFlowType t, boolean toJump, DataFlowCallOption lastCall ) { revLambdaFlow(lambdaCall, kind, p, t, false, toJump, lastCall) @@ -242,6 +242,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 ArgNode + } + + cached + predicate hiddenNode(Node n) { nodeIsHidden(n) } + + cached + OutNodeExt getAnOutNodeExt(DataFlowCall call, ReturnKindExt k) { + result = getAnOutNode(call, k.(ValueReturnKind).getKind()) + or + exists(ArgNode 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(ParamNode 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 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 + // 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 +344,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, ParamNode p) { p.isParameterOf(viableCallableExt(call), i) } @@ -270,11 +353,11 @@ private module Cached { * dispatch into account. */ cached - predicate viableParamArg(DataFlowCall call, ParameterNode p, ArgumentNode arg) { + predicate viableParamArg(DataFlowCall call, ParamNode p, ArgNode 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 +395,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(ParamNode p, Node node, boolean read) { p = node and read = false or @@ -325,30 +408,30 @@ 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(ArgNode arg | parameterValueFlowArgCand(p, arg, false) and argumentValueFlowsThroughCand(arg, node, read) ) or // flow through: no read inside method - exists(ArgumentNode arg | + exists(ArgNode arg | parameterValueFlowArgCand(p, arg, read) and argumentValueFlowsThroughCand(arg, node, false) ) } pragma[nomagic] - private predicate parameterValueFlowArgCand(ParameterNode p, ArgumentNode arg, boolean read) { + private predicate parameterValueFlowArgCand(ParamNode p, ArgNode arg, boolean read) { parameterValueFlowCand(p, arg, read) } pragma[nomagic] - predicate parameterValueFlowsToPreUpdateCand(ParameterNode p, PostUpdateNode n) { + predicate parameterValueFlowsToPreUpdateCand(ParamNode p, PostUpdateNode n) { parameterValueFlowCand(p, n.getPreUpdateNode(), false) } @@ -360,7 +443,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(ParamNode p, ReturnKind kind, boolean read) { exists(ReturnNode ret | parameterValueFlowCand(p, ret, read) and kind = ret.getKind() @@ -369,9 +452,9 @@ private module Cached { pragma[nomagic] private predicate argumentValueFlowsThroughCand0( - DataFlowCall call, ArgumentNode arg, ReturnKind kind, boolean read + DataFlowCall call, ArgNode arg, ReturnKind kind, boolean read ) { - exists(ParameterNode param | viableParamArg(call, param, arg) | + exists(ParamNode param | viableParamArg(call, param, arg) | parameterValueFlowReturnCand(param, kind, read) ) } @@ -382,14 +465,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(ArgNode 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(ParamNode p, Node n) { parameterValueFlowCand(p, n, _) and ( parameterValueFlowReturnCand(p, _, _) @@ -416,21 +499,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(ParamNode 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(ParamNode p, Node node, ReadStepTypesOption read) { p = node and Cand::cand(p, _) and read = TReadStepTypesNone() @@ -447,7 +530,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,34 +538,32 @@ private module Cached { pragma[nomagic] private predicate parameterValueFlow0_0( - ReadStepTypesOption mustBeNone, ParameterNode p, Node node, ReadStepTypesOption read + ReadStepTypesOption mustBeNone, ParamNode p, Node node, ReadStepTypesOption read ) { // flow through: no prior read - exists(ArgumentNode arg | + exists(ArgNode arg | parameterValueFlowArg(p, arg, mustBeNone) and argumentValueFlowsThrough(arg, read, node) ) or // flow through: no read inside method - exists(ArgumentNode arg | + exists(ArgNode arg | parameterValueFlowArg(p, arg, read) and argumentValueFlowsThrough(arg, mustBeNone, node) ) } pragma[nomagic] - private predicate parameterValueFlowArg( - ParameterNode p, ArgumentNode 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, ArgumentNode arg, ReturnKind kind, ReadStepTypesOption read + DataFlowCall call, ArgNode arg, ReturnKind kind, ReadStepTypesOption read ) { - exists(ParameterNode param | viableParamArg(call, param, arg) | + exists(ParamNode param | viableParamArg(call, param, arg) | parameterValueFlowReturn(param, kind, read) ) } @@ -496,18 +577,18 @@ private module Cached { * container type, and the content type. */ pragma[nomagic] - predicate argumentValueFlowsThrough(ArgumentNode 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) | // 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 +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(ArgumentNode arg, Content c, Node out) { + predicate getterStep(ArgNode arg, Content c, Node out) { argumentValueFlowsThrough(arg, TReadStepTypesSome(_, c, _), out) } @@ -529,7 +610,7 @@ private module Cached { * container type, and the content type. */ private predicate parameterValueFlowReturn( - ParameterNode p, ReturnKind kind, ReadStepTypesOption read + ParamNode p, ReturnKind kind, ReadStepTypesOption read ) { exists(ReturnNode ret | parameterValueFlow(p, ret, read) and @@ -553,7 +634,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 +692,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 +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. */ - cached - predicate parameterValueFlowsToPreUpdate(ParameterNode p, PostUpdateNode n) { + private predicate parameterValueFlowsToPreUpdate(ParamNode p, PostUpdateNode n) { parameterValueFlow(p, n.getPreUpdateNode(), TReadStepTypesNone()) } @@ -644,9 +724,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 +734,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 +761,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 +772,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.(ArgNode).argumentOf(c, _) and + simpleLocalFlowStep(toPre.(ArgNode), 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 +794,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 +816,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(ParamNode p | p.isParameterOf(_, pos)) } cached newtype TBooleanOption = @@ -761,23 +851,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 +936,7 @@ class CallContextSomeCall extends CallContextCall, TSomeCall { override string toString() { result = "CcSomeCall" } override predicate relevantFor(DataFlowCallable callable) { - exists(ParameterNode p | getNodeEnclosingCallable(p) = callable) + exists(ParamNode p | getNodeEnclosingCallable(p) = callable) } override predicate matchesCall(DataFlowCall call) { any() } @@ -866,7 +948,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 +981,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 +995,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 ParamNode extends Node { + ParamNode() { 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 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) } +} + /** * 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 +1033,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 +1046,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 +1057,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 +1067,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 +1093,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 +1123,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 +1144,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 +1195,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/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()) 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/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..244a28cf332 --- /dev/null +++ b/cpp/ql/test/experimental/query-tests/Security/CWE/CWE-1126/semmle/tests/DeclarationOfVariableWithUnnecessarilyWideScope.expected @@ -0,0 +1 @@ +| 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 | 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..47d89188e6b --- /dev/null +++ b/cpp/ql/test/experimental/query-tests/Security/CWE/CWE-1126/semmle/tests/test.c @@ -0,0 +1,59 @@ +void workFunction_0(char *s) { + int intIndex = 10; + int intGuard; + char buf[80]; + while(intIndex > 2) // GOOD + { + buf[intIndex] = 1; + intIndex--; + } + intIndex = 10; + while(intIndex > 2) + { + buf[intIndex] = 1; + int intIndex; // BAD + 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; + intIndex -= 2; + int intIndex; + intIndex--; + } + intIndex = 10; + while(intIndex > 2) // GOOD + { + buf[intIndex] = 1; + --intIndex; + int intIndex; + intIndex--; + } +} 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..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,228 +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: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 | ... * ... | -| 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: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 | -| 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: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 | ... * ... | +| 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: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 | ... * ... | -| 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: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 *)... | -| 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: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 | ... * ... | +| 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: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) | -| 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: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) | +| 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 943bc3b1214..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]; @@ -76,7 +77,7 @@ void processData2(char *start, char *end) { char *copy; - copy = new char[end - start]; // GOOD [FALSE POSITIVE] + copy = new char[end - start]; // GOOD // ... @@ -137,6 +138,15 @@ void more_bounded_tests() { { int size = atoi(getenv("USER")); + if (size > 0) + { + malloc(size * sizeof(int)); // GOOD (overflow not possible) + } + } + + { + long size = atol(getenv("USER")); + if (size > 0) { malloc(size * sizeof(int)); // BAD @@ -302,7 +312,7 @@ void equality_cases() { if ((size == 50) || (size == 100)) { - malloc(size * sizeof(int)); // GOOD [FALSE POSITIVE] + malloc(size * sizeof(int)); // GOOD } } { @@ -311,6 +321,15 @@ void equality_cases() { if (size != 50 && size != 100) return; - malloc(size * sizeof(int)); // GOOD [FALSE POSITIVE] + 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 +} \ No newline at end of file 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..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 @@ -18,3 +18,25 @@ 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 + } +} + +#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 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 } { 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..3d7a1168e30 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,34 +234,17 @@ 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. - /// - /// 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, 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). 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; } 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/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..0bda977a6d5 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) { 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, constructUnderlyingTupleType: false); 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, constructUnderlyingTupleType: false); trapFile.Write('*'); return; case TypeKind.TypeParameter: var tp = (ITypeParameterSymbol)type; - tp.ContainingSymbol.BuildOrWriteId(cx, trapFile, symbolBeingDefined, addBaseClass); + tp.ContainingSymbol.BuildOrWriteId(cx, trapFile, symbolBeingDefined, constructUnderlyingTupleType: false); 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) { 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 @@ -250,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. @@ -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, constructUnderlyingTupleType: false); } ); 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, constructUnderlyingTupleType: false); 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, constructUnderlyingTupleType: false) ); 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/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/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/DataFlowImpl.qll b/csharp/ql/src/semmle/code/csharp/dataflow/internal/DataFlowImpl.qll index 9498e51e7e6..058d66b1496 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 @@ -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(ArgNode arg | fwdFlow(arg, cc, config) and viableParamArg(call, _, arg) ) @@ -515,24 +512,22 @@ private module Stage1 { pragma[nomagic] predicate viableParamArgNodeCandFwd1( - DataFlowCall call, ParameterNode p, ArgumentNode 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, ArgumentNode arg, boolean toReturn, Configuration config - ) { - exists(ParameterNode 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, ArgumentNode arg, Configuration config) { + private predicate revFlowInToReturn(DataFlowCall call, ArgNode arg, Configuration config) { revFlowIn(call, arg, true, config) } @@ -597,7 +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(ParameterNode 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 @@ -663,7 +658,7 @@ private predicate flowOutOfCallNodeCand1( pragma[nomagic] private predicate viableParamArgNodeCand1( - DataFlowCall call, ParameterNode p, ArgumentNode arg, Configuration config + DataFlowCall call, ParamNode p, ArgNode arg, Configuration config ) { Stage1::viableParamArgNodeCandFwd1(call, p, arg, config) and Stage1::revFlow(arg, config) @@ -675,7 +670,7 @@ private predicate viableParamArgNodeCand1( */ pragma[nomagic] private predicate flowIntoCallNodeCand1( - DataFlowCall call, ArgumentNode arg, ParameterNode p, Configuration config + DataFlowCall call, ArgNode arg, ParamNode p, Configuration config ) { viableParamArgNodeCand1(call, p, arg, config) and Stage1::revFlow(p, config) and @@ -735,8 +730,7 @@ private predicate flowOutOfCallNodeCand1( */ pragma[nomagic] private predicate flowIntoCallNodeCand1( - DataFlowCall call, ArgumentNode arg, ParameterNode 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 | @@ -944,10 +938,10 @@ private module Stage2 { pragma[nomagic] private predicate fwdFlowIn( - DataFlowCall call, ParameterNode p, Cc outercc, Cc innercc, ApOption argAp, Ap ap, + DataFlowCall call, ParamNode p, Cc outercc, Cc innercc, ApOption argAp, Ap ap, Configuration config ) { - exists(ArgumentNode 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) @@ -992,7 +986,7 @@ private module Stage2 { private predicate fwdFlowIsEntered( DataFlowCall call, Cc cc, ApOption argAp, Ap ap, Configuration config ) { - exists(ParameterNode p | + exists(ParamNode p | fwdFlowIn(call, p, cc, _, argAp, ap, config) and PrevStage::parameterMayFlowThrough(p, _, getApprox(ap), config) ) @@ -1133,10 +1127,9 @@ private module Stage2 { pragma[nomagic] private predicate revFlowIn( - DataFlowCall call, ArgumentNode arg, boolean toReturn, ApOption returnAp, Ap ap, - Configuration config + DataFlowCall call, ArgNode arg, boolean toReturn, ApOption returnAp, Ap ap, Configuration config ) { - exists(ParameterNode p, boolean allowsFieldFlow | + exists(ParamNode p, boolean allowsFieldFlow | revFlow(p, toReturn, returnAp, ap, config) and flowIntoCall(call, arg, p, allowsFieldFlow, config) | @@ -1146,7 +1139,7 @@ private module Stage2 { pragma[nomagic] private predicate revFlowInToReturn( - DataFlowCall call, ArgumentNode 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) } @@ -1199,13 +1192,13 @@ private module Stage2 { pragma[noinline] private predicate parameterFlow( - ParameterNode 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(ParameterNode 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 @@ -1245,8 +1238,7 @@ private predicate flowOutOfCallNodeCand2( pragma[nomagic] private predicate flowIntoCallNodeCand2( - DataFlowCall call, ArgumentNode node1, ParameterNode 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 @@ -1260,8 +1252,8 @@ private module LocalFlowBigStep { */ private class FlowCheckNode extends Node { FlowCheckNode() { - this instanceof CastNode or - clearsContent(this, _) + castNode(this) or + clearsContentCached(this, _) } } @@ -1275,7 +1267,7 @@ private module LocalFlowBigStep { config.isSource(node) or jumpStep(_, node, config) or additionalJumpStep(_, node, config) or - node instanceof ParameterNode or + node instanceof ParamNode or node instanceof OutNodeExt or store(_, _, node, _) or read(_, _, node) or @@ -1321,21 +1313,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 +1342,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 +1376,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 +1435,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 +1577,10 @@ private module Stage3 { pragma[nomagic] private predicate fwdFlowIn( - DataFlowCall call, ParameterNode p, Cc outercc, Cc innercc, ApOption argAp, Ap ap, + DataFlowCall call, ParamNode p, Cc outercc, Cc innercc, ApOption argAp, Ap ap, Configuration config ) { - exists(ArgumentNode 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) @@ -1631,7 +1625,7 @@ private module Stage3 { private predicate fwdFlowIsEntered( DataFlowCall call, Cc cc, ApOption argAp, Ap ap, Configuration config ) { - exists(ParameterNode p | + exists(ParamNode p | fwdFlowIn(call, p, cc, _, argAp, ap, config) and PrevStage::parameterMayFlowThrough(p, _, unbindApa(getApprox(ap)), config) ) @@ -1772,10 +1766,9 @@ private module Stage3 { pragma[nomagic] private predicate revFlowIn( - DataFlowCall call, ArgumentNode arg, boolean toReturn, ApOption returnAp, Ap ap, - Configuration config + DataFlowCall call, ArgNode arg, boolean toReturn, ApOption returnAp, Ap ap, Configuration config ) { - exists(ParameterNode p, boolean allowsFieldFlow | + exists(ParamNode p, boolean allowsFieldFlow | revFlow(p, toReturn, returnAp, ap, config) and flowIntoCall(call, arg, p, allowsFieldFlow, config) | @@ -1785,7 +1778,7 @@ private module Stage3 { pragma[nomagic] private predicate revFlowInToReturn( - DataFlowCall call, ArgumentNode 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) } @@ -1838,13 +1831,13 @@ private module Stage3 { pragma[noinline] private predicate parameterFlow( - ParameterNode 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(ParameterNode 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 @@ -2088,7 +2081,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,8 +2148,7 @@ private module Stage4 { pragma[nomagic] private predicate flowIntoCall( - DataFlowCall call, ArgumentNode node1, ParameterNode 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 @@ -2300,10 +2292,10 @@ private module Stage4 { pragma[nomagic] private predicate fwdFlowIn( - DataFlowCall call, ParameterNode p, Cc outercc, Cc innercc, ApOption argAp, Ap ap, + DataFlowCall call, ParamNode p, Cc outercc, Cc innercc, ApOption argAp, Ap ap, Configuration config ) { - exists(ArgumentNode 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) @@ -2348,7 +2340,7 @@ private module Stage4 { private predicate fwdFlowIsEntered( DataFlowCall call, Cc cc, ApOption argAp, Ap ap, Configuration config ) { - exists(ParameterNode p | + exists(ParamNode p | fwdFlowIn(call, p, cc, _, argAp, ap, config) and PrevStage::parameterMayFlowThrough(p, _, unbindApa(getApprox(ap)), config) ) @@ -2489,10 +2481,9 @@ private module Stage4 { pragma[nomagic] private predicate revFlowIn( - DataFlowCall call, ArgumentNode arg, boolean toReturn, ApOption returnAp, Ap ap, - Configuration config + DataFlowCall call, ArgNode arg, boolean toReturn, ApOption returnAp, Ap ap, Configuration config ) { - exists(ParameterNode p, boolean allowsFieldFlow | + exists(ParamNode p, boolean allowsFieldFlow | revFlow(p, toReturn, returnAp, ap, config) and flowIntoCall(call, arg, p, allowsFieldFlow, config) | @@ -2502,7 +2493,7 @@ private module Stage4 { pragma[nomagic] private predicate revFlowInToReturn( - DataFlowCall call, ArgumentNode 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) } @@ -2555,13 +2546,13 @@ private module Stage4 { pragma[noinline] private predicate parameterFlow( - ParameterNode 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(ParameterNode 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 @@ -2606,7 +2597,7 @@ private predicate nodeMayUseSummary(Node n, AccessPathApprox apa, Configuration private newtype TSummaryCtx = TSummaryCtxNone() or - TSummaryCtxSome(ParameterNode p, AccessPath ap) { + TSummaryCtxSome(ParamNode p, AccessPath ap) { Stage4::parameterMayFlowThrough(p, _, ap.getApprox(), _) } @@ -2627,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 ParameterNode p; + private ParamNode p; private AccessPath ap; SummaryCtxSome() { this = TSummaryCtxSome(p, ap) } @@ -2758,7 +2749,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 +2970,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 +3139,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 +3226,7 @@ pragma[noinline] private predicate pathIntoArg( PathNodeMid mid, int i, CallContext cc, DataFlowCall call, AccessPath ap, AccessPathApprox apa ) { - exists(ArgumentNode arg | + exists(ArgNode arg | arg = mid.getNode() and cc = mid.getCallContext() and arg.argumentOf(call, i) and @@ -3248,7 +3239,7 @@ pragma[noinline] private predicate parameterCand( DataFlowCallable callable, int i, AccessPathApprox apa, Configuration config ) { - exists(ParameterNode p | + exists(ParamNode p | Stage4::revFlow(p, _, _, apa, config) and p.isParameterOf(callable, i) ) @@ -3272,7 +3263,7 @@ private predicate pathIntoCallable0( * respectively. */ private predicate pathIntoCallable( - PathNodeMid mid, ParameterNode 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 | @@ -3568,7 +3559,7 @@ private module FlowExploration { private newtype TSummaryCtx1 = TSummaryCtx1None() or - TSummaryCtx1Param(ParameterNode p) + TSummaryCtx1Param(ParamNode p) private newtype TSummaryCtx2 = TSummaryCtx2None() or @@ -3591,7 +3582,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 +3602,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 +3616,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 +3774,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 +3788,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 +3804,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 +3818,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 +3915,7 @@ private module FlowExploration { PartialPathNodeFwd mid, int i, CallContext cc, DataFlowCall call, PartialAccessPath ap, Configuration config ) { - exists(ArgumentNode arg | + exists(ArgNode arg | arg = mid.getNode() and cc = mid.getCallContext() and arg.argumentOf(call, i) and @@ -3943,7 +3934,7 @@ private module FlowExploration { } private predicate partialPathIntoCallable( - PartialPathNodeFwd mid, ParameterNode p, CallContext outercc, CallContextCall innercc, + PartialPathNodeFwd mid, ParamNode p, CallContext outercc, CallContextCall innercc, TSummaryCtx1 sc1, TSummaryCtx2 sc2, DataFlowCall call, PartialAccessPath ap, Configuration config ) { @@ -3980,7 +3971,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(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) ) @@ -4037,7 +4028,7 @@ private module FlowExploration { apConsRev(ap, c, ap0, config) ) or - exists(ParameterNode p | + exists(ParamNode p | mid.getNode() = p and viableParamArg(_, p, node) and sc1 = mid.getSummaryCtx1() and @@ -4115,7 +4106,7 @@ private module FlowExploration { int pos, TRevSummaryCtx1Some sc1, TRevSummaryCtx2Some sc2, RevPartialAccessPath ap, Configuration config ) { - exists(PartialPathNodeRev mid, ParameterNode p | + exists(PartialPathNodeRev mid, ParamNode p | mid.getNode() = p and p.isParameterOf(_, pos) and sc1 = mid.getSummaryCtx1() and @@ -4138,7 +4129,7 @@ private module FlowExploration { pragma[nomagic] private predicate revPartialPathThroughCallable( - PartialPathNodeRev mid, ArgumentNode 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 9498e51e7e6..058d66b1496 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(ArgNode arg | fwdFlow(arg, cc, config) and viableParamArg(call, _, arg) ) @@ -515,24 +512,22 @@ private module Stage1 { pragma[nomagic] predicate viableParamArgNodeCandFwd1( - DataFlowCall call, ParameterNode p, ArgumentNode 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, ArgumentNode arg, boolean toReturn, Configuration config - ) { - exists(ParameterNode 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, ArgumentNode arg, Configuration config) { + private predicate revFlowInToReturn(DataFlowCall call, ArgNode arg, Configuration config) { revFlowIn(call, arg, true, config) } @@ -597,7 +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(ParameterNode 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 @@ -663,7 +658,7 @@ private predicate flowOutOfCallNodeCand1( pragma[nomagic] private predicate viableParamArgNodeCand1( - DataFlowCall call, ParameterNode p, ArgumentNode arg, Configuration config + DataFlowCall call, ParamNode p, ArgNode arg, Configuration config ) { Stage1::viableParamArgNodeCandFwd1(call, p, arg, config) and Stage1::revFlow(arg, config) @@ -675,7 +670,7 @@ private predicate viableParamArgNodeCand1( */ pragma[nomagic] private predicate flowIntoCallNodeCand1( - DataFlowCall call, ArgumentNode arg, ParameterNode p, Configuration config + DataFlowCall call, ArgNode arg, ParamNode p, Configuration config ) { viableParamArgNodeCand1(call, p, arg, config) and Stage1::revFlow(p, config) and @@ -735,8 +730,7 @@ private predicate flowOutOfCallNodeCand1( */ pragma[nomagic] private predicate flowIntoCallNodeCand1( - DataFlowCall call, ArgumentNode arg, ParameterNode 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 | @@ -944,10 +938,10 @@ private module Stage2 { pragma[nomagic] private predicate fwdFlowIn( - DataFlowCall call, ParameterNode p, Cc outercc, Cc innercc, ApOption argAp, Ap ap, + DataFlowCall call, ParamNode p, Cc outercc, Cc innercc, ApOption argAp, Ap ap, Configuration config ) { - exists(ArgumentNode 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) @@ -992,7 +986,7 @@ private module Stage2 { private predicate fwdFlowIsEntered( DataFlowCall call, Cc cc, ApOption argAp, Ap ap, Configuration config ) { - exists(ParameterNode p | + exists(ParamNode p | fwdFlowIn(call, p, cc, _, argAp, ap, config) and PrevStage::parameterMayFlowThrough(p, _, getApprox(ap), config) ) @@ -1133,10 +1127,9 @@ private module Stage2 { pragma[nomagic] private predicate revFlowIn( - DataFlowCall call, ArgumentNode arg, boolean toReturn, ApOption returnAp, Ap ap, - Configuration config + DataFlowCall call, ArgNode arg, boolean toReturn, ApOption returnAp, Ap ap, Configuration config ) { - exists(ParameterNode p, boolean allowsFieldFlow | + exists(ParamNode p, boolean allowsFieldFlow | revFlow(p, toReturn, returnAp, ap, config) and flowIntoCall(call, arg, p, allowsFieldFlow, config) | @@ -1146,7 +1139,7 @@ private module Stage2 { pragma[nomagic] private predicate revFlowInToReturn( - DataFlowCall call, ArgumentNode 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) } @@ -1199,13 +1192,13 @@ private module Stage2 { pragma[noinline] private predicate parameterFlow( - ParameterNode 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(ParameterNode 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 @@ -1245,8 +1238,7 @@ private predicate flowOutOfCallNodeCand2( pragma[nomagic] private predicate flowIntoCallNodeCand2( - DataFlowCall call, ArgumentNode node1, ParameterNode 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 @@ -1260,8 +1252,8 @@ private module LocalFlowBigStep { */ private class FlowCheckNode extends Node { FlowCheckNode() { - this instanceof CastNode or - clearsContent(this, _) + castNode(this) or + clearsContentCached(this, _) } } @@ -1275,7 +1267,7 @@ private module LocalFlowBigStep { config.isSource(node) or jumpStep(_, node, config) or additionalJumpStep(_, node, config) or - node instanceof ParameterNode or + node instanceof ParamNode or node instanceof OutNodeExt or store(_, _, node, _) or read(_, _, node) or @@ -1321,21 +1313,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 +1342,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 +1376,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 +1435,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 +1577,10 @@ private module Stage3 { pragma[nomagic] private predicate fwdFlowIn( - DataFlowCall call, ParameterNode p, Cc outercc, Cc innercc, ApOption argAp, Ap ap, + DataFlowCall call, ParamNode p, Cc outercc, Cc innercc, ApOption argAp, Ap ap, Configuration config ) { - exists(ArgumentNode 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) @@ -1631,7 +1625,7 @@ private module Stage3 { private predicate fwdFlowIsEntered( DataFlowCall call, Cc cc, ApOption argAp, Ap ap, Configuration config ) { - exists(ParameterNode p | + exists(ParamNode p | fwdFlowIn(call, p, cc, _, argAp, ap, config) and PrevStage::parameterMayFlowThrough(p, _, unbindApa(getApprox(ap)), config) ) @@ -1772,10 +1766,9 @@ private module Stage3 { pragma[nomagic] private predicate revFlowIn( - DataFlowCall call, ArgumentNode arg, boolean toReturn, ApOption returnAp, Ap ap, - Configuration config + DataFlowCall call, ArgNode arg, boolean toReturn, ApOption returnAp, Ap ap, Configuration config ) { - exists(ParameterNode p, boolean allowsFieldFlow | + exists(ParamNode p, boolean allowsFieldFlow | revFlow(p, toReturn, returnAp, ap, config) and flowIntoCall(call, arg, p, allowsFieldFlow, config) | @@ -1785,7 +1778,7 @@ private module Stage3 { pragma[nomagic] private predicate revFlowInToReturn( - DataFlowCall call, ArgumentNode 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) } @@ -1838,13 +1831,13 @@ private module Stage3 { pragma[noinline] private predicate parameterFlow( - ParameterNode 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(ParameterNode 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 @@ -2088,7 +2081,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,8 +2148,7 @@ private module Stage4 { pragma[nomagic] private predicate flowIntoCall( - DataFlowCall call, ArgumentNode node1, ParameterNode 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 @@ -2300,10 +2292,10 @@ private module Stage4 { pragma[nomagic] private predicate fwdFlowIn( - DataFlowCall call, ParameterNode p, Cc outercc, Cc innercc, ApOption argAp, Ap ap, + DataFlowCall call, ParamNode p, Cc outercc, Cc innercc, ApOption argAp, Ap ap, Configuration config ) { - exists(ArgumentNode 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) @@ -2348,7 +2340,7 @@ private module Stage4 { private predicate fwdFlowIsEntered( DataFlowCall call, Cc cc, ApOption argAp, Ap ap, Configuration config ) { - exists(ParameterNode p | + exists(ParamNode p | fwdFlowIn(call, p, cc, _, argAp, ap, config) and PrevStage::parameterMayFlowThrough(p, _, unbindApa(getApprox(ap)), config) ) @@ -2489,10 +2481,9 @@ private module Stage4 { pragma[nomagic] private predicate revFlowIn( - DataFlowCall call, ArgumentNode arg, boolean toReturn, ApOption returnAp, Ap ap, - Configuration config + DataFlowCall call, ArgNode arg, boolean toReturn, ApOption returnAp, Ap ap, Configuration config ) { - exists(ParameterNode p, boolean allowsFieldFlow | + exists(ParamNode p, boolean allowsFieldFlow | revFlow(p, toReturn, returnAp, ap, config) and flowIntoCall(call, arg, p, allowsFieldFlow, config) | @@ -2502,7 +2493,7 @@ private module Stage4 { pragma[nomagic] private predicate revFlowInToReturn( - DataFlowCall call, ArgumentNode 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) } @@ -2555,13 +2546,13 @@ private module Stage4 { pragma[noinline] private predicate parameterFlow( - ParameterNode 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(ParameterNode 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 @@ -2606,7 +2597,7 @@ private predicate nodeMayUseSummary(Node n, AccessPathApprox apa, Configuration private newtype TSummaryCtx = TSummaryCtxNone() or - TSummaryCtxSome(ParameterNode p, AccessPath ap) { + TSummaryCtxSome(ParamNode p, AccessPath ap) { Stage4::parameterMayFlowThrough(p, _, ap.getApprox(), _) } @@ -2627,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 ParameterNode p; + private ParamNode p; private AccessPath ap; SummaryCtxSome() { this = TSummaryCtxSome(p, ap) } @@ -2758,7 +2749,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 +2970,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 +3139,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 +3226,7 @@ pragma[noinline] private predicate pathIntoArg( PathNodeMid mid, int i, CallContext cc, DataFlowCall call, AccessPath ap, AccessPathApprox apa ) { - exists(ArgumentNode arg | + exists(ArgNode arg | arg = mid.getNode() and cc = mid.getCallContext() and arg.argumentOf(call, i) and @@ -3248,7 +3239,7 @@ pragma[noinline] private predicate parameterCand( DataFlowCallable callable, int i, AccessPathApprox apa, Configuration config ) { - exists(ParameterNode p | + exists(ParamNode p | Stage4::revFlow(p, _, _, apa, config) and p.isParameterOf(callable, i) ) @@ -3272,7 +3263,7 @@ private predicate pathIntoCallable0( * respectively. */ private predicate pathIntoCallable( - PathNodeMid mid, ParameterNode 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 | @@ -3568,7 +3559,7 @@ private module FlowExploration { private newtype TSummaryCtx1 = TSummaryCtx1None() or - TSummaryCtx1Param(ParameterNode p) + TSummaryCtx1Param(ParamNode p) private newtype TSummaryCtx2 = TSummaryCtx2None() or @@ -3591,7 +3582,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 +3602,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 +3616,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 +3774,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 +3788,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 +3804,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 +3818,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 +3915,7 @@ private module FlowExploration { PartialPathNodeFwd mid, int i, CallContext cc, DataFlowCall call, PartialAccessPath ap, Configuration config ) { - exists(ArgumentNode arg | + exists(ArgNode arg | arg = mid.getNode() and cc = mid.getCallContext() and arg.argumentOf(call, i) and @@ -3943,7 +3934,7 @@ private module FlowExploration { } private predicate partialPathIntoCallable( - PartialPathNodeFwd mid, ParameterNode p, CallContext outercc, CallContextCall innercc, + PartialPathNodeFwd mid, ParamNode p, CallContext outercc, CallContextCall innercc, TSummaryCtx1 sc1, TSummaryCtx2 sc2, DataFlowCall call, PartialAccessPath ap, Configuration config ) { @@ -3980,7 +3971,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(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) ) @@ -4037,7 +4028,7 @@ private module FlowExploration { apConsRev(ap, c, ap0, config) ) or - exists(ParameterNode p | + exists(ParamNode p | mid.getNode() = p and viableParamArg(_, p, node) and sc1 = mid.getSummaryCtx1() and @@ -4115,7 +4106,7 @@ private module FlowExploration { int pos, TRevSummaryCtx1Some sc1, TRevSummaryCtx2Some sc2, RevPartialAccessPath ap, Configuration config ) { - exists(PartialPathNodeRev mid, ParameterNode p | + exists(PartialPathNodeRev mid, ParamNode p | mid.getNode() = p and p.isParameterOf(_, pos) and sc1 = mid.getSummaryCtx1() and @@ -4138,7 +4129,7 @@ private module FlowExploration { pragma[nomagic] private predicate revPartialPathThroughCallable( - PartialPathNodeRev mid, ArgumentNode 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 9498e51e7e6..058d66b1496 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(ArgNode arg | fwdFlow(arg, cc, config) and viableParamArg(call, _, arg) ) @@ -515,24 +512,22 @@ private module Stage1 { pragma[nomagic] predicate viableParamArgNodeCandFwd1( - DataFlowCall call, ParameterNode p, ArgumentNode 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, ArgumentNode arg, boolean toReturn, Configuration config - ) { - exists(ParameterNode 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, ArgumentNode arg, Configuration config) { + private predicate revFlowInToReturn(DataFlowCall call, ArgNode arg, Configuration config) { revFlowIn(call, arg, true, config) } @@ -597,7 +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(ParameterNode 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 @@ -663,7 +658,7 @@ private predicate flowOutOfCallNodeCand1( pragma[nomagic] private predicate viableParamArgNodeCand1( - DataFlowCall call, ParameterNode p, ArgumentNode arg, Configuration config + DataFlowCall call, ParamNode p, ArgNode arg, Configuration config ) { Stage1::viableParamArgNodeCandFwd1(call, p, arg, config) and Stage1::revFlow(arg, config) @@ -675,7 +670,7 @@ private predicate viableParamArgNodeCand1( */ pragma[nomagic] private predicate flowIntoCallNodeCand1( - DataFlowCall call, ArgumentNode arg, ParameterNode p, Configuration config + DataFlowCall call, ArgNode arg, ParamNode p, Configuration config ) { viableParamArgNodeCand1(call, p, arg, config) and Stage1::revFlow(p, config) and @@ -735,8 +730,7 @@ private predicate flowOutOfCallNodeCand1( */ pragma[nomagic] private predicate flowIntoCallNodeCand1( - DataFlowCall call, ArgumentNode arg, ParameterNode 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 | @@ -944,10 +938,10 @@ private module Stage2 { pragma[nomagic] private predicate fwdFlowIn( - DataFlowCall call, ParameterNode p, Cc outercc, Cc innercc, ApOption argAp, Ap ap, + DataFlowCall call, ParamNode p, Cc outercc, Cc innercc, ApOption argAp, Ap ap, Configuration config ) { - exists(ArgumentNode 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) @@ -992,7 +986,7 @@ private module Stage2 { private predicate fwdFlowIsEntered( DataFlowCall call, Cc cc, ApOption argAp, Ap ap, Configuration config ) { - exists(ParameterNode p | + exists(ParamNode p | fwdFlowIn(call, p, cc, _, argAp, ap, config) and PrevStage::parameterMayFlowThrough(p, _, getApprox(ap), config) ) @@ -1133,10 +1127,9 @@ private module Stage2 { pragma[nomagic] private predicate revFlowIn( - DataFlowCall call, ArgumentNode arg, boolean toReturn, ApOption returnAp, Ap ap, - Configuration config + DataFlowCall call, ArgNode arg, boolean toReturn, ApOption returnAp, Ap ap, Configuration config ) { - exists(ParameterNode p, boolean allowsFieldFlow | + exists(ParamNode p, boolean allowsFieldFlow | revFlow(p, toReturn, returnAp, ap, config) and flowIntoCall(call, arg, p, allowsFieldFlow, config) | @@ -1146,7 +1139,7 @@ private module Stage2 { pragma[nomagic] private predicate revFlowInToReturn( - DataFlowCall call, ArgumentNode 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) } @@ -1199,13 +1192,13 @@ private module Stage2 { pragma[noinline] private predicate parameterFlow( - ParameterNode 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(ParameterNode 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 @@ -1245,8 +1238,7 @@ private predicate flowOutOfCallNodeCand2( pragma[nomagic] private predicate flowIntoCallNodeCand2( - DataFlowCall call, ArgumentNode node1, ParameterNode 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 @@ -1260,8 +1252,8 @@ private module LocalFlowBigStep { */ private class FlowCheckNode extends Node { FlowCheckNode() { - this instanceof CastNode or - clearsContent(this, _) + castNode(this) or + clearsContentCached(this, _) } } @@ -1275,7 +1267,7 @@ private module LocalFlowBigStep { config.isSource(node) or jumpStep(_, node, config) or additionalJumpStep(_, node, config) or - node instanceof ParameterNode or + node instanceof ParamNode or node instanceof OutNodeExt or store(_, _, node, _) or read(_, _, node) or @@ -1321,21 +1313,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 +1342,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 +1376,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 +1435,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 +1577,10 @@ private module Stage3 { pragma[nomagic] private predicate fwdFlowIn( - DataFlowCall call, ParameterNode p, Cc outercc, Cc innercc, ApOption argAp, Ap ap, + DataFlowCall call, ParamNode p, Cc outercc, Cc innercc, ApOption argAp, Ap ap, Configuration config ) { - exists(ArgumentNode 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) @@ -1631,7 +1625,7 @@ private module Stage3 { private predicate fwdFlowIsEntered( DataFlowCall call, Cc cc, ApOption argAp, Ap ap, Configuration config ) { - exists(ParameterNode p | + exists(ParamNode p | fwdFlowIn(call, p, cc, _, argAp, ap, config) and PrevStage::parameterMayFlowThrough(p, _, unbindApa(getApprox(ap)), config) ) @@ -1772,10 +1766,9 @@ private module Stage3 { pragma[nomagic] private predicate revFlowIn( - DataFlowCall call, ArgumentNode arg, boolean toReturn, ApOption returnAp, Ap ap, - Configuration config + DataFlowCall call, ArgNode arg, boolean toReturn, ApOption returnAp, Ap ap, Configuration config ) { - exists(ParameterNode p, boolean allowsFieldFlow | + exists(ParamNode p, boolean allowsFieldFlow | revFlow(p, toReturn, returnAp, ap, config) and flowIntoCall(call, arg, p, allowsFieldFlow, config) | @@ -1785,7 +1778,7 @@ private module Stage3 { pragma[nomagic] private predicate revFlowInToReturn( - DataFlowCall call, ArgumentNode 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) } @@ -1838,13 +1831,13 @@ private module Stage3 { pragma[noinline] private predicate parameterFlow( - ParameterNode 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(ParameterNode 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 @@ -2088,7 +2081,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,8 +2148,7 @@ private module Stage4 { pragma[nomagic] private predicate flowIntoCall( - DataFlowCall call, ArgumentNode node1, ParameterNode 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 @@ -2300,10 +2292,10 @@ private module Stage4 { pragma[nomagic] private predicate fwdFlowIn( - DataFlowCall call, ParameterNode p, Cc outercc, Cc innercc, ApOption argAp, Ap ap, + DataFlowCall call, ParamNode p, Cc outercc, Cc innercc, ApOption argAp, Ap ap, Configuration config ) { - exists(ArgumentNode 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) @@ -2348,7 +2340,7 @@ private module Stage4 { private predicate fwdFlowIsEntered( DataFlowCall call, Cc cc, ApOption argAp, Ap ap, Configuration config ) { - exists(ParameterNode p | + exists(ParamNode p | fwdFlowIn(call, p, cc, _, argAp, ap, config) and PrevStage::parameterMayFlowThrough(p, _, unbindApa(getApprox(ap)), config) ) @@ -2489,10 +2481,9 @@ private module Stage4 { pragma[nomagic] private predicate revFlowIn( - DataFlowCall call, ArgumentNode arg, boolean toReturn, ApOption returnAp, Ap ap, - Configuration config + DataFlowCall call, ArgNode arg, boolean toReturn, ApOption returnAp, Ap ap, Configuration config ) { - exists(ParameterNode p, boolean allowsFieldFlow | + exists(ParamNode p, boolean allowsFieldFlow | revFlow(p, toReturn, returnAp, ap, config) and flowIntoCall(call, arg, p, allowsFieldFlow, config) | @@ -2502,7 +2493,7 @@ private module Stage4 { pragma[nomagic] private predicate revFlowInToReturn( - DataFlowCall call, ArgumentNode 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) } @@ -2555,13 +2546,13 @@ private module Stage4 { pragma[noinline] private predicate parameterFlow( - ParameterNode 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(ParameterNode 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 @@ -2606,7 +2597,7 @@ private predicate nodeMayUseSummary(Node n, AccessPathApprox apa, Configuration private newtype TSummaryCtx = TSummaryCtxNone() or - TSummaryCtxSome(ParameterNode p, AccessPath ap) { + TSummaryCtxSome(ParamNode p, AccessPath ap) { Stage4::parameterMayFlowThrough(p, _, ap.getApprox(), _) } @@ -2627,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 ParameterNode p; + private ParamNode p; private AccessPath ap; SummaryCtxSome() { this = TSummaryCtxSome(p, ap) } @@ -2758,7 +2749,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 +2970,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 +3139,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 +3226,7 @@ pragma[noinline] private predicate pathIntoArg( PathNodeMid mid, int i, CallContext cc, DataFlowCall call, AccessPath ap, AccessPathApprox apa ) { - exists(ArgumentNode arg | + exists(ArgNode arg | arg = mid.getNode() and cc = mid.getCallContext() and arg.argumentOf(call, i) and @@ -3248,7 +3239,7 @@ pragma[noinline] private predicate parameterCand( DataFlowCallable callable, int i, AccessPathApprox apa, Configuration config ) { - exists(ParameterNode p | + exists(ParamNode p | Stage4::revFlow(p, _, _, apa, config) and p.isParameterOf(callable, i) ) @@ -3272,7 +3263,7 @@ private predicate pathIntoCallable0( * respectively. */ private predicate pathIntoCallable( - PathNodeMid mid, ParameterNode 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 | @@ -3568,7 +3559,7 @@ private module FlowExploration { private newtype TSummaryCtx1 = TSummaryCtx1None() or - TSummaryCtx1Param(ParameterNode p) + TSummaryCtx1Param(ParamNode p) private newtype TSummaryCtx2 = TSummaryCtx2None() or @@ -3591,7 +3582,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 +3602,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 +3616,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 +3774,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 +3788,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 +3804,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 +3818,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 +3915,7 @@ private module FlowExploration { PartialPathNodeFwd mid, int i, CallContext cc, DataFlowCall call, PartialAccessPath ap, Configuration config ) { - exists(ArgumentNode arg | + exists(ArgNode arg | arg = mid.getNode() and cc = mid.getCallContext() and arg.argumentOf(call, i) and @@ -3943,7 +3934,7 @@ private module FlowExploration { } private predicate partialPathIntoCallable( - PartialPathNodeFwd mid, ParameterNode p, CallContext outercc, CallContextCall innercc, + PartialPathNodeFwd mid, ParamNode p, CallContext outercc, CallContextCall innercc, TSummaryCtx1 sc1, TSummaryCtx2 sc2, DataFlowCall call, PartialAccessPath ap, Configuration config ) { @@ -3980,7 +3971,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(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) ) @@ -4037,7 +4028,7 @@ private module FlowExploration { apConsRev(ap, c, ap0, config) ) or - exists(ParameterNode p | + exists(ParamNode p | mid.getNode() = p and viableParamArg(_, p, node) and sc1 = mid.getSummaryCtx1() and @@ -4115,7 +4106,7 @@ private module FlowExploration { int pos, TRevSummaryCtx1Some sc1, TRevSummaryCtx2Some sc2, RevPartialAccessPath ap, Configuration config ) { - exists(PartialPathNodeRev mid, ParameterNode p | + exists(PartialPathNodeRev mid, ParamNode p | mid.getNode() = p and p.isParameterOf(_, pos) and sc1 = mid.getSummaryCtx1() and @@ -4138,7 +4129,7 @@ private module FlowExploration { pragma[nomagic] private predicate revPartialPathThroughCallable( - PartialPathNodeRev mid, ArgumentNode 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 9498e51e7e6..058d66b1496 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(ArgNode arg | fwdFlow(arg, cc, config) and viableParamArg(call, _, arg) ) @@ -515,24 +512,22 @@ private module Stage1 { pragma[nomagic] predicate viableParamArgNodeCandFwd1( - DataFlowCall call, ParameterNode p, ArgumentNode 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, ArgumentNode arg, boolean toReturn, Configuration config - ) { - exists(ParameterNode 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, ArgumentNode arg, Configuration config) { + private predicate revFlowInToReturn(DataFlowCall call, ArgNode arg, Configuration config) { revFlowIn(call, arg, true, config) } @@ -597,7 +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(ParameterNode 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 @@ -663,7 +658,7 @@ private predicate flowOutOfCallNodeCand1( pragma[nomagic] private predicate viableParamArgNodeCand1( - DataFlowCall call, ParameterNode p, ArgumentNode arg, Configuration config + DataFlowCall call, ParamNode p, ArgNode arg, Configuration config ) { Stage1::viableParamArgNodeCandFwd1(call, p, arg, config) and Stage1::revFlow(arg, config) @@ -675,7 +670,7 @@ private predicate viableParamArgNodeCand1( */ pragma[nomagic] private predicate flowIntoCallNodeCand1( - DataFlowCall call, ArgumentNode arg, ParameterNode p, Configuration config + DataFlowCall call, ArgNode arg, ParamNode p, Configuration config ) { viableParamArgNodeCand1(call, p, arg, config) and Stage1::revFlow(p, config) and @@ -735,8 +730,7 @@ private predicate flowOutOfCallNodeCand1( */ pragma[nomagic] private predicate flowIntoCallNodeCand1( - DataFlowCall call, ArgumentNode arg, ParameterNode 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 | @@ -944,10 +938,10 @@ private module Stage2 { pragma[nomagic] private predicate fwdFlowIn( - DataFlowCall call, ParameterNode p, Cc outercc, Cc innercc, ApOption argAp, Ap ap, + DataFlowCall call, ParamNode p, Cc outercc, Cc innercc, ApOption argAp, Ap ap, Configuration config ) { - exists(ArgumentNode 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) @@ -992,7 +986,7 @@ private module Stage2 { private predicate fwdFlowIsEntered( DataFlowCall call, Cc cc, ApOption argAp, Ap ap, Configuration config ) { - exists(ParameterNode p | + exists(ParamNode p | fwdFlowIn(call, p, cc, _, argAp, ap, config) and PrevStage::parameterMayFlowThrough(p, _, getApprox(ap), config) ) @@ -1133,10 +1127,9 @@ private module Stage2 { pragma[nomagic] private predicate revFlowIn( - DataFlowCall call, ArgumentNode arg, boolean toReturn, ApOption returnAp, Ap ap, - Configuration config + DataFlowCall call, ArgNode arg, boolean toReturn, ApOption returnAp, Ap ap, Configuration config ) { - exists(ParameterNode p, boolean allowsFieldFlow | + exists(ParamNode p, boolean allowsFieldFlow | revFlow(p, toReturn, returnAp, ap, config) and flowIntoCall(call, arg, p, allowsFieldFlow, config) | @@ -1146,7 +1139,7 @@ private module Stage2 { pragma[nomagic] private predicate revFlowInToReturn( - DataFlowCall call, ArgumentNode 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) } @@ -1199,13 +1192,13 @@ private module Stage2 { pragma[noinline] private predicate parameterFlow( - ParameterNode 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(ParameterNode 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 @@ -1245,8 +1238,7 @@ private predicate flowOutOfCallNodeCand2( pragma[nomagic] private predicate flowIntoCallNodeCand2( - DataFlowCall call, ArgumentNode node1, ParameterNode 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 @@ -1260,8 +1252,8 @@ private module LocalFlowBigStep { */ private class FlowCheckNode extends Node { FlowCheckNode() { - this instanceof CastNode or - clearsContent(this, _) + castNode(this) or + clearsContentCached(this, _) } } @@ -1275,7 +1267,7 @@ private module LocalFlowBigStep { config.isSource(node) or jumpStep(_, node, config) or additionalJumpStep(_, node, config) or - node instanceof ParameterNode or + node instanceof ParamNode or node instanceof OutNodeExt or store(_, _, node, _) or read(_, _, node) or @@ -1321,21 +1313,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 +1342,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 +1376,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 +1435,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 +1577,10 @@ private module Stage3 { pragma[nomagic] private predicate fwdFlowIn( - DataFlowCall call, ParameterNode p, Cc outercc, Cc innercc, ApOption argAp, Ap ap, + DataFlowCall call, ParamNode p, Cc outercc, Cc innercc, ApOption argAp, Ap ap, Configuration config ) { - exists(ArgumentNode 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) @@ -1631,7 +1625,7 @@ private module Stage3 { private predicate fwdFlowIsEntered( DataFlowCall call, Cc cc, ApOption argAp, Ap ap, Configuration config ) { - exists(ParameterNode p | + exists(ParamNode p | fwdFlowIn(call, p, cc, _, argAp, ap, config) and PrevStage::parameterMayFlowThrough(p, _, unbindApa(getApprox(ap)), config) ) @@ -1772,10 +1766,9 @@ private module Stage3 { pragma[nomagic] private predicate revFlowIn( - DataFlowCall call, ArgumentNode arg, boolean toReturn, ApOption returnAp, Ap ap, - Configuration config + DataFlowCall call, ArgNode arg, boolean toReturn, ApOption returnAp, Ap ap, Configuration config ) { - exists(ParameterNode p, boolean allowsFieldFlow | + exists(ParamNode p, boolean allowsFieldFlow | revFlow(p, toReturn, returnAp, ap, config) and flowIntoCall(call, arg, p, allowsFieldFlow, config) | @@ -1785,7 +1778,7 @@ private module Stage3 { pragma[nomagic] private predicate revFlowInToReturn( - DataFlowCall call, ArgumentNode 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) } @@ -1838,13 +1831,13 @@ private module Stage3 { pragma[noinline] private predicate parameterFlow( - ParameterNode 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(ParameterNode 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 @@ -2088,7 +2081,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,8 +2148,7 @@ private module Stage4 { pragma[nomagic] private predicate flowIntoCall( - DataFlowCall call, ArgumentNode node1, ParameterNode 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 @@ -2300,10 +2292,10 @@ private module Stage4 { pragma[nomagic] private predicate fwdFlowIn( - DataFlowCall call, ParameterNode p, Cc outercc, Cc innercc, ApOption argAp, Ap ap, + DataFlowCall call, ParamNode p, Cc outercc, Cc innercc, ApOption argAp, Ap ap, Configuration config ) { - exists(ArgumentNode 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) @@ -2348,7 +2340,7 @@ private module Stage4 { private predicate fwdFlowIsEntered( DataFlowCall call, Cc cc, ApOption argAp, Ap ap, Configuration config ) { - exists(ParameterNode p | + exists(ParamNode p | fwdFlowIn(call, p, cc, _, argAp, ap, config) and PrevStage::parameterMayFlowThrough(p, _, unbindApa(getApprox(ap)), config) ) @@ -2489,10 +2481,9 @@ private module Stage4 { pragma[nomagic] private predicate revFlowIn( - DataFlowCall call, ArgumentNode arg, boolean toReturn, ApOption returnAp, Ap ap, - Configuration config + DataFlowCall call, ArgNode arg, boolean toReturn, ApOption returnAp, Ap ap, Configuration config ) { - exists(ParameterNode p, boolean allowsFieldFlow | + exists(ParamNode p, boolean allowsFieldFlow | revFlow(p, toReturn, returnAp, ap, config) and flowIntoCall(call, arg, p, allowsFieldFlow, config) | @@ -2502,7 +2493,7 @@ private module Stage4 { pragma[nomagic] private predicate revFlowInToReturn( - DataFlowCall call, ArgumentNode 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) } @@ -2555,13 +2546,13 @@ private module Stage4 { pragma[noinline] private predicate parameterFlow( - ParameterNode 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(ParameterNode 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 @@ -2606,7 +2597,7 @@ private predicate nodeMayUseSummary(Node n, AccessPathApprox apa, Configuration private newtype TSummaryCtx = TSummaryCtxNone() or - TSummaryCtxSome(ParameterNode p, AccessPath ap) { + TSummaryCtxSome(ParamNode p, AccessPath ap) { Stage4::parameterMayFlowThrough(p, _, ap.getApprox(), _) } @@ -2627,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 ParameterNode p; + private ParamNode p; private AccessPath ap; SummaryCtxSome() { this = TSummaryCtxSome(p, ap) } @@ -2758,7 +2749,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 +2970,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 +3139,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 +3226,7 @@ pragma[noinline] private predicate pathIntoArg( PathNodeMid mid, int i, CallContext cc, DataFlowCall call, AccessPath ap, AccessPathApprox apa ) { - exists(ArgumentNode arg | + exists(ArgNode arg | arg = mid.getNode() and cc = mid.getCallContext() and arg.argumentOf(call, i) and @@ -3248,7 +3239,7 @@ pragma[noinline] private predicate parameterCand( DataFlowCallable callable, int i, AccessPathApprox apa, Configuration config ) { - exists(ParameterNode p | + exists(ParamNode p | Stage4::revFlow(p, _, _, apa, config) and p.isParameterOf(callable, i) ) @@ -3272,7 +3263,7 @@ private predicate pathIntoCallable0( * respectively. */ private predicate pathIntoCallable( - PathNodeMid mid, ParameterNode 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 | @@ -3568,7 +3559,7 @@ private module FlowExploration { private newtype TSummaryCtx1 = TSummaryCtx1None() or - TSummaryCtx1Param(ParameterNode p) + TSummaryCtx1Param(ParamNode p) private newtype TSummaryCtx2 = TSummaryCtx2None() or @@ -3591,7 +3582,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 +3602,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 +3616,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 +3774,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 +3788,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 +3804,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 +3818,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 +3915,7 @@ private module FlowExploration { PartialPathNodeFwd mid, int i, CallContext cc, DataFlowCall call, PartialAccessPath ap, Configuration config ) { - exists(ArgumentNode arg | + exists(ArgNode arg | arg = mid.getNode() and cc = mid.getCallContext() and arg.argumentOf(call, i) and @@ -3943,7 +3934,7 @@ private module FlowExploration { } private predicate partialPathIntoCallable( - PartialPathNodeFwd mid, ParameterNode p, CallContext outercc, CallContextCall innercc, + PartialPathNodeFwd mid, ParamNode p, CallContext outercc, CallContextCall innercc, TSummaryCtx1 sc1, TSummaryCtx2 sc2, DataFlowCall call, PartialAccessPath ap, Configuration config ) { @@ -3980,7 +3971,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(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) ) @@ -4037,7 +4028,7 @@ private module FlowExploration { apConsRev(ap, c, ap0, config) ) or - exists(ParameterNode p | + exists(ParamNode p | mid.getNode() = p and viableParamArg(_, p, node) and sc1 = mid.getSummaryCtx1() and @@ -4115,7 +4106,7 @@ private module FlowExploration { int pos, TRevSummaryCtx1Some sc1, TRevSummaryCtx2Some sc2, RevPartialAccessPath ap, Configuration config ) { - exists(PartialPathNodeRev mid, ParameterNode p | + exists(PartialPathNodeRev mid, ParamNode p | mid.getNode() = p and p.isParameterOf(_, pos) and sc1 = mid.getSummaryCtx1() and @@ -4138,7 +4129,7 @@ private module FlowExploration { pragma[nomagic] private predicate revPartialPathThroughCallable( - PartialPathNodeRev mid, ArgumentNode 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 9498e51e7e6..058d66b1496 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(ArgNode arg | fwdFlow(arg, cc, config) and viableParamArg(call, _, arg) ) @@ -515,24 +512,22 @@ private module Stage1 { pragma[nomagic] predicate viableParamArgNodeCandFwd1( - DataFlowCall call, ParameterNode p, ArgumentNode 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, ArgumentNode arg, boolean toReturn, Configuration config - ) { - exists(ParameterNode 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, ArgumentNode arg, Configuration config) { + private predicate revFlowInToReturn(DataFlowCall call, ArgNode arg, Configuration config) { revFlowIn(call, arg, true, config) } @@ -597,7 +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(ParameterNode 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 @@ -663,7 +658,7 @@ private predicate flowOutOfCallNodeCand1( pragma[nomagic] private predicate viableParamArgNodeCand1( - DataFlowCall call, ParameterNode p, ArgumentNode arg, Configuration config + DataFlowCall call, ParamNode p, ArgNode arg, Configuration config ) { Stage1::viableParamArgNodeCandFwd1(call, p, arg, config) and Stage1::revFlow(arg, config) @@ -675,7 +670,7 @@ private predicate viableParamArgNodeCand1( */ pragma[nomagic] private predicate flowIntoCallNodeCand1( - DataFlowCall call, ArgumentNode arg, ParameterNode p, Configuration config + DataFlowCall call, ArgNode arg, ParamNode p, Configuration config ) { viableParamArgNodeCand1(call, p, arg, config) and Stage1::revFlow(p, config) and @@ -735,8 +730,7 @@ private predicate flowOutOfCallNodeCand1( */ pragma[nomagic] private predicate flowIntoCallNodeCand1( - DataFlowCall call, ArgumentNode arg, ParameterNode 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 | @@ -944,10 +938,10 @@ private module Stage2 { pragma[nomagic] private predicate fwdFlowIn( - DataFlowCall call, ParameterNode p, Cc outercc, Cc innercc, ApOption argAp, Ap ap, + DataFlowCall call, ParamNode p, Cc outercc, Cc innercc, ApOption argAp, Ap ap, Configuration config ) { - exists(ArgumentNode 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) @@ -992,7 +986,7 @@ private module Stage2 { private predicate fwdFlowIsEntered( DataFlowCall call, Cc cc, ApOption argAp, Ap ap, Configuration config ) { - exists(ParameterNode p | + exists(ParamNode p | fwdFlowIn(call, p, cc, _, argAp, ap, config) and PrevStage::parameterMayFlowThrough(p, _, getApprox(ap), config) ) @@ -1133,10 +1127,9 @@ private module Stage2 { pragma[nomagic] private predicate revFlowIn( - DataFlowCall call, ArgumentNode arg, boolean toReturn, ApOption returnAp, Ap ap, - Configuration config + DataFlowCall call, ArgNode arg, boolean toReturn, ApOption returnAp, Ap ap, Configuration config ) { - exists(ParameterNode p, boolean allowsFieldFlow | + exists(ParamNode p, boolean allowsFieldFlow | revFlow(p, toReturn, returnAp, ap, config) and flowIntoCall(call, arg, p, allowsFieldFlow, config) | @@ -1146,7 +1139,7 @@ private module Stage2 { pragma[nomagic] private predicate revFlowInToReturn( - DataFlowCall call, ArgumentNode 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) } @@ -1199,13 +1192,13 @@ private module Stage2 { pragma[noinline] private predicate parameterFlow( - ParameterNode 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(ParameterNode 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 @@ -1245,8 +1238,7 @@ private predicate flowOutOfCallNodeCand2( pragma[nomagic] private predicate flowIntoCallNodeCand2( - DataFlowCall call, ArgumentNode node1, ParameterNode 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 @@ -1260,8 +1252,8 @@ private module LocalFlowBigStep { */ private class FlowCheckNode extends Node { FlowCheckNode() { - this instanceof CastNode or - clearsContent(this, _) + castNode(this) or + clearsContentCached(this, _) } } @@ -1275,7 +1267,7 @@ private module LocalFlowBigStep { config.isSource(node) or jumpStep(_, node, config) or additionalJumpStep(_, node, config) or - node instanceof ParameterNode or + node instanceof ParamNode or node instanceof OutNodeExt or store(_, _, node, _) or read(_, _, node) or @@ -1321,21 +1313,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 +1342,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 +1376,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 +1435,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 +1577,10 @@ private module Stage3 { pragma[nomagic] private predicate fwdFlowIn( - DataFlowCall call, ParameterNode p, Cc outercc, Cc innercc, ApOption argAp, Ap ap, + DataFlowCall call, ParamNode p, Cc outercc, Cc innercc, ApOption argAp, Ap ap, Configuration config ) { - exists(ArgumentNode 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) @@ -1631,7 +1625,7 @@ private module Stage3 { private predicate fwdFlowIsEntered( DataFlowCall call, Cc cc, ApOption argAp, Ap ap, Configuration config ) { - exists(ParameterNode p | + exists(ParamNode p | fwdFlowIn(call, p, cc, _, argAp, ap, config) and PrevStage::parameterMayFlowThrough(p, _, unbindApa(getApprox(ap)), config) ) @@ -1772,10 +1766,9 @@ private module Stage3 { pragma[nomagic] private predicate revFlowIn( - DataFlowCall call, ArgumentNode arg, boolean toReturn, ApOption returnAp, Ap ap, - Configuration config + DataFlowCall call, ArgNode arg, boolean toReturn, ApOption returnAp, Ap ap, Configuration config ) { - exists(ParameterNode p, boolean allowsFieldFlow | + exists(ParamNode p, boolean allowsFieldFlow | revFlow(p, toReturn, returnAp, ap, config) and flowIntoCall(call, arg, p, allowsFieldFlow, config) | @@ -1785,7 +1778,7 @@ private module Stage3 { pragma[nomagic] private predicate revFlowInToReturn( - DataFlowCall call, ArgumentNode 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) } @@ -1838,13 +1831,13 @@ private module Stage3 { pragma[noinline] private predicate parameterFlow( - ParameterNode 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(ParameterNode 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 @@ -2088,7 +2081,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,8 +2148,7 @@ private module Stage4 { pragma[nomagic] private predicate flowIntoCall( - DataFlowCall call, ArgumentNode node1, ParameterNode 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 @@ -2300,10 +2292,10 @@ private module Stage4 { pragma[nomagic] private predicate fwdFlowIn( - DataFlowCall call, ParameterNode p, Cc outercc, Cc innercc, ApOption argAp, Ap ap, + DataFlowCall call, ParamNode p, Cc outercc, Cc innercc, ApOption argAp, Ap ap, Configuration config ) { - exists(ArgumentNode 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) @@ -2348,7 +2340,7 @@ private module Stage4 { private predicate fwdFlowIsEntered( DataFlowCall call, Cc cc, ApOption argAp, Ap ap, Configuration config ) { - exists(ParameterNode p | + exists(ParamNode p | fwdFlowIn(call, p, cc, _, argAp, ap, config) and PrevStage::parameterMayFlowThrough(p, _, unbindApa(getApprox(ap)), config) ) @@ -2489,10 +2481,9 @@ private module Stage4 { pragma[nomagic] private predicate revFlowIn( - DataFlowCall call, ArgumentNode arg, boolean toReturn, ApOption returnAp, Ap ap, - Configuration config + DataFlowCall call, ArgNode arg, boolean toReturn, ApOption returnAp, Ap ap, Configuration config ) { - exists(ParameterNode p, boolean allowsFieldFlow | + exists(ParamNode p, boolean allowsFieldFlow | revFlow(p, toReturn, returnAp, ap, config) and flowIntoCall(call, arg, p, allowsFieldFlow, config) | @@ -2502,7 +2493,7 @@ private module Stage4 { pragma[nomagic] private predicate revFlowInToReturn( - DataFlowCall call, ArgumentNode 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) } @@ -2555,13 +2546,13 @@ private module Stage4 { pragma[noinline] private predicate parameterFlow( - ParameterNode 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(ParameterNode 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 @@ -2606,7 +2597,7 @@ private predicate nodeMayUseSummary(Node n, AccessPathApprox apa, Configuration private newtype TSummaryCtx = TSummaryCtxNone() or - TSummaryCtxSome(ParameterNode p, AccessPath ap) { + TSummaryCtxSome(ParamNode p, AccessPath ap) { Stage4::parameterMayFlowThrough(p, _, ap.getApprox(), _) } @@ -2627,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 ParameterNode p; + private ParamNode p; private AccessPath ap; SummaryCtxSome() { this = TSummaryCtxSome(p, ap) } @@ -2758,7 +2749,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 +2970,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 +3139,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 +3226,7 @@ pragma[noinline] private predicate pathIntoArg( PathNodeMid mid, int i, CallContext cc, DataFlowCall call, AccessPath ap, AccessPathApprox apa ) { - exists(ArgumentNode arg | + exists(ArgNode arg | arg = mid.getNode() and cc = mid.getCallContext() and arg.argumentOf(call, i) and @@ -3248,7 +3239,7 @@ pragma[noinline] private predicate parameterCand( DataFlowCallable callable, int i, AccessPathApprox apa, Configuration config ) { - exists(ParameterNode p | + exists(ParamNode p | Stage4::revFlow(p, _, _, apa, config) and p.isParameterOf(callable, i) ) @@ -3272,7 +3263,7 @@ private predicate pathIntoCallable0( * respectively. */ private predicate pathIntoCallable( - PathNodeMid mid, ParameterNode 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 | @@ -3568,7 +3559,7 @@ private module FlowExploration { private newtype TSummaryCtx1 = TSummaryCtx1None() or - TSummaryCtx1Param(ParameterNode p) + TSummaryCtx1Param(ParamNode p) private newtype TSummaryCtx2 = TSummaryCtx2None() or @@ -3591,7 +3582,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 +3602,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 +3616,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 +3774,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 +3788,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 +3804,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 +3818,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 +3915,7 @@ private module FlowExploration { PartialPathNodeFwd mid, int i, CallContext cc, DataFlowCall call, PartialAccessPath ap, Configuration config ) { - exists(ArgumentNode arg | + exists(ArgNode arg | arg = mid.getNode() and cc = mid.getCallContext() and arg.argumentOf(call, i) and @@ -3943,7 +3934,7 @@ private module FlowExploration { } private predicate partialPathIntoCallable( - PartialPathNodeFwd mid, ParameterNode p, CallContext outercc, CallContextCall innercc, + PartialPathNodeFwd mid, ParamNode p, CallContext outercc, CallContextCall innercc, TSummaryCtx1 sc1, TSummaryCtx2 sc2, DataFlowCall call, PartialAccessPath ap, Configuration config ) { @@ -3980,7 +3971,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(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) ) @@ -4037,7 +4028,7 @@ private module FlowExploration { apConsRev(ap, c, ap0, config) ) or - exists(ParameterNode p | + exists(ParamNode p | mid.getNode() = p and viableParamArg(_, p, node) and sc1 = mid.getSummaryCtx1() and @@ -4115,7 +4106,7 @@ private module FlowExploration { int pos, TRevSummaryCtx1Some sc1, TRevSummaryCtx2Some sc2, RevPartialAccessPath ap, Configuration config ) { - exists(PartialPathNodeRev mid, ParameterNode p | + exists(PartialPathNodeRev mid, ParamNode p | mid.getNode() = p and p.isParameterOf(_, pos) and sc1 = mid.getSummaryCtx1() and @@ -4138,7 +4129,7 @@ private module FlowExploration { pragma[nomagic] private predicate revPartialPathThroughCallable( - PartialPathNodeRev mid, ArgumentNode 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 966c30038cc..462e89ac9ed 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,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, ParameterNode p) { + private predicate viableParamNonLambda(DataFlowCall call, int i, ParamNode p) { p.isParameterOf(viableCallable(call), i) } - private predicate viableParamLambda(DataFlowCall call, int i, ParameterNode p) { + private predicate viableParamLambda(DataFlowCall call, int i, ParamNode p) { p.isParameterOf(viableCallableLambda(call, _), i) } - private predicate viableParamArgNonLambda(DataFlowCall call, ParameterNode p, ArgumentNode 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, ParameterNode p, ArgumentNode arg) { + private predicate viableParamArgLambda(DataFlowCall call, ParamNode p, ArgNode arg) { exists(int i | viableParamLambda(call, i, p) and arg.argumentOf(call, i) @@ -118,8 +118,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 ArgNode or node instanceof ReturnNode + 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 @@ -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 | @@ -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 @@ -176,7 +176,7 @@ private module LambdaFlow { ) or // flow into a callable - exists(ParameterNode 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 @@ -227,7 +227,7 @@ private module LambdaFlow { pragma[nomagic] predicate revLambdaFlowIn( - DataFlowCall lambdaCall, LambdaCallKind kind, ParameterNode p, DataFlowType t, boolean toJump, + DataFlowCall lambdaCall, LambdaCallKind kind, ParamNode p, DataFlowType t, boolean toJump, DataFlowCallOption lastCall ) { revLambdaFlow(lambdaCall, kind, p, t, false, toJump, lastCall) @@ -242,6 +242,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 ArgNode + } + + cached + predicate hiddenNode(Node n) { nodeIsHidden(n) } + + cached + OutNodeExt getAnOutNodeExt(DataFlowCall call, ReturnKindExt k) { + result = getAnOutNode(call, k.(ValueReturnKind).getKind()) + or + exists(ArgNode 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(ParamNode 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 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 + // 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 +344,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, ParamNode p) { p.isParameterOf(viableCallableExt(call), i) } @@ -270,11 +353,11 @@ private module Cached { * dispatch into account. */ cached - predicate viableParamArg(DataFlowCall call, ParameterNode p, ArgumentNode arg) { + predicate viableParamArg(DataFlowCall call, ParamNode p, ArgNode 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 +395,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(ParamNode p, Node node, boolean read) { p = node and read = false or @@ -325,30 +408,30 @@ 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(ArgNode arg | parameterValueFlowArgCand(p, arg, false) and argumentValueFlowsThroughCand(arg, node, read) ) or // flow through: no read inside method - exists(ArgumentNode arg | + exists(ArgNode arg | parameterValueFlowArgCand(p, arg, read) and argumentValueFlowsThroughCand(arg, node, false) ) } pragma[nomagic] - private predicate parameterValueFlowArgCand(ParameterNode p, ArgumentNode arg, boolean read) { + private predicate parameterValueFlowArgCand(ParamNode p, ArgNode arg, boolean read) { parameterValueFlowCand(p, arg, read) } pragma[nomagic] - predicate parameterValueFlowsToPreUpdateCand(ParameterNode p, PostUpdateNode n) { + predicate parameterValueFlowsToPreUpdateCand(ParamNode p, PostUpdateNode n) { parameterValueFlowCand(p, n.getPreUpdateNode(), false) } @@ -360,7 +443,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(ParamNode p, ReturnKind kind, boolean read) { exists(ReturnNode ret | parameterValueFlowCand(p, ret, read) and kind = ret.getKind() @@ -369,9 +452,9 @@ private module Cached { pragma[nomagic] private predicate argumentValueFlowsThroughCand0( - DataFlowCall call, ArgumentNode arg, ReturnKind kind, boolean read + DataFlowCall call, ArgNode arg, ReturnKind kind, boolean read ) { - exists(ParameterNode param | viableParamArg(call, param, arg) | + exists(ParamNode param | viableParamArg(call, param, arg) | parameterValueFlowReturnCand(param, kind, read) ) } @@ -382,14 +465,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(ArgNode 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(ParamNode p, Node n) { parameterValueFlowCand(p, n, _) and ( parameterValueFlowReturnCand(p, _, _) @@ -416,21 +499,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(ParamNode 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(ParamNode p, Node node, ReadStepTypesOption read) { p = node and Cand::cand(p, _) and read = TReadStepTypesNone() @@ -447,7 +530,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,34 +538,32 @@ private module Cached { pragma[nomagic] private predicate parameterValueFlow0_0( - ReadStepTypesOption mustBeNone, ParameterNode p, Node node, ReadStepTypesOption read + ReadStepTypesOption mustBeNone, ParamNode p, Node node, ReadStepTypesOption read ) { // flow through: no prior read - exists(ArgumentNode arg | + exists(ArgNode arg | parameterValueFlowArg(p, arg, mustBeNone) and argumentValueFlowsThrough(arg, read, node) ) or // flow through: no read inside method - exists(ArgumentNode arg | + exists(ArgNode arg | parameterValueFlowArg(p, arg, read) and argumentValueFlowsThrough(arg, mustBeNone, node) ) } pragma[nomagic] - private predicate parameterValueFlowArg( - ParameterNode p, ArgumentNode 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, ArgumentNode arg, ReturnKind kind, ReadStepTypesOption read + DataFlowCall call, ArgNode arg, ReturnKind kind, ReadStepTypesOption read ) { - exists(ParameterNode param | viableParamArg(call, param, arg) | + exists(ParamNode param | viableParamArg(call, param, arg) | parameterValueFlowReturn(param, kind, read) ) } @@ -496,18 +577,18 @@ private module Cached { * container type, and the content type. */ pragma[nomagic] - predicate argumentValueFlowsThrough(ArgumentNode 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) | // 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 +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(ArgumentNode arg, Content c, Node out) { + predicate getterStep(ArgNode arg, Content c, Node out) { argumentValueFlowsThrough(arg, TReadStepTypesSome(_, c, _), out) } @@ -529,7 +610,7 @@ private module Cached { * container type, and the content type. */ private predicate parameterValueFlowReturn( - ParameterNode p, ReturnKind kind, ReadStepTypesOption read + ParamNode p, ReturnKind kind, ReadStepTypesOption read ) { exists(ReturnNode ret | parameterValueFlow(p, ret, read) and @@ -553,7 +634,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 +692,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 +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. */ - cached - predicate parameterValueFlowsToPreUpdate(ParameterNode p, PostUpdateNode n) { + private predicate parameterValueFlowsToPreUpdate(ParamNode p, PostUpdateNode n) { parameterValueFlow(p, n.getPreUpdateNode(), TReadStepTypesNone()) } @@ -644,9 +724,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 +734,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 +761,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 +772,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.(ArgNode).argumentOf(c, _) and + simpleLocalFlowStep(toPre.(ArgNode), 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 +794,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 +816,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(ParamNode p | p.isParameterOf(_, pos)) } cached newtype TBooleanOption = @@ -761,23 +851,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 +936,7 @@ class CallContextSomeCall extends CallContextCall, TSomeCall { override string toString() { result = "CcSomeCall" } override predicate relevantFor(DataFlowCallable callable) { - exists(ParameterNode p | getNodeEnclosingCallable(p) = callable) + exists(ParamNode p | getNodeEnclosingCallable(p) = callable) } override predicate matchesCall(DataFlowCall call) { any() } @@ -866,7 +948,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 +981,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 +995,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 ParamNode extends Node { + ParamNode() { 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 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) } +} + /** * 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 +1033,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 +1046,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 +1057,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 +1067,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 +1093,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 +1123,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 +1144,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 +1195,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/DataFlowPrivate.qll b/csharp/ql/src/semmle/code/csharp/dataflow/internal/DataFlowPrivate.qll index 97a8406a21e..6373382a204 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. 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/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 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 diff --git a/docs/codeql/codeql-cli/creating-codeql-databases.rst b/docs/codeql/codeql-cli/creating-codeql-databases.rst index 4f7212050df..637df58555b 100644 --- a/docs/codeql/codeql-cli/creating-codeql-databases.rst +++ b/docs/codeql/codeql-cli/creating-codeql-databases.rst @@ -165,13 +165,15 @@ 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 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 + build command. Specifying build commands ~~~~~~~~~~~~~~~~~~~~~~~~~ @@ -200,6 +202,14 @@ 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 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' 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. 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..dbacb10099b --- /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 serialization/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/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. 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/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..5c1796e1f60 --- /dev/null +++ b/java/ql/src/experimental/Security/CWE/CWE-094/JythonInjection.java @@ -0,0 +1,49 @@ +import org.python.util.PythonInterpreter; + +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 execution of arbitrary Python code + 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 execution of arbitrary Python code + 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..c44eee23602 --- /dev/null +++ b/java/ql/src/experimental/Security/CWE/CWE-094/JythonInjection.ql @@ -0,0 +1,112 @@ +/** + * @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 semmle.code.java.frameworks.spring.SpringController +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%", "run%", "eval", "compile"]) + } +} + +/** 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 runsCode(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", "makeCode"]) + } +} + +/** + * Holds if `ma` is a call to a class-loading method, and `sink` is the byte array + * representing the class to be loaded. + */ +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, ...) + sink = ma.getArgument(i) + ) +} + +/** The class `org.python.core.Py`. */ +class Py extends RefType { + Py() { this.hasQualifiedName("org.python.core", "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 + 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) + ) +} + +/** An expression loaded by Jython. */ +class CodeInjectionSink extends DataFlow::ExprNode { + MethodAccess methodAccess; + + CodeInjectionSink() { + runsCode(methodAccess, this.getExpr()) or + loadsClass(methodAccess, this.getExpr()) or + compile(methodAccess, this.getExpr()) + } + + MethodAccess getMethodAccess() { result = methodAccess } +} + +/** + * 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" } + + override predicate isSource(DataFlow::Node source) { source instanceof RemoteFlowSource } + + override predicate isSink(DataFlow::Node sink) { sink instanceof CodeInjectionSink } +} + +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/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..15adfbe4524 --- /dev/null +++ b/java/ql/src/experimental/Security/CWE/CWE-094/RhinoInjection.java @@ -0,0 +1,40 @@ +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 { + 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) { + return className.startsWith("com.example."); + } + }); + } + + 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/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..2683cf9ad29 --- /dev/null +++ b/java/ql/src/experimental/Security/CWE/CWE-094/ScriptInjection.qhelp @@ -0,0 +1,52 @@ + + + + +

    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 + 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 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, which will be deprecated in a future release, or use + GraalVM instead.

    +
    + + +

    The following code could execute user-supplied 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 +
  • +
  • +GraalVM: Secure by Default +
  • +
  • + 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..fb8bf867501 --- /dev/null +++ b/java/ql/src/experimental/Security/CWE/CWE-094/ScriptInjection.ql @@ -0,0 +1,146 @@ +/** + * @name Injection in Java Script Engine + * @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 + * @id java/unsafe-eval + * @tags security + * external/cwe/cwe-094 + */ + +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 Java Script Engine. */ +class RhinoContext extends RefType { + RhinoContext() { this.hasQualifiedName("org.mozilla.javascript", "Context") } +} + +/** A method that evaluates a Rhino expression with `org.mozilla.javascript.Context`. */ +class RhinoEvaluateExpressionMethod extends Method { + RhinoEvaluateExpressionMethod() { + this.getDeclaringType().getAnAncestor*() instanceof RhinoContext and + 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 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") + then sink = ma.getArgument(_) // all arguments allow script injection + else sink = ma.getArgument(0) + ) +} + +/** + * Holds if a Rhino expression evaluation method is vulnerable to code injection. + */ +predicate evaluatesRhinoExpression(MethodAccess ma, Expr sink) { + exists(RhinoEvaluateExpressionMethod m | m = ma.getMethod() | + ( + 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", "setClassShutter"]) and // safe mode or `ClassShutter` constraint is enforced + ma.getQualifier() = ca.getQualifier().(VarAccess).getVariable().getAnAccess() + ) + ) +} + +/** + * Holds if a Rhino expression compilation method is vulnerable to code injection. + */ +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 definesRhinoClass(MethodAccess ma, Expr sink) { + exists(RhinoDefineClassMethod m | m = ma.getMethod() | sink = ma.getArgument(1)) +} + +/** A script injection sink. */ +class ScriptInjectionSink extends DataFlow::ExprNode { + MethodAccess methodAccess; + + ScriptInjectionSink() { + 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() { 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" } + + override predicate isSource(DataFlow::Node source) { source instanceof RemoteFlowSource } + + 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, + "Java Script Engine evaluate $@.", source.getNode(), "user input" 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..b02bd3e4c30 --- /dev/null +++ b/java/ql/src/experimental/Security/CWE/CWE-601/SpringUrlRedirect.ql @@ -0,0 +1,66 @@ +/** + * @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 + +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" } + + 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");` + // 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 + ) + 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.*") + ) + ) + } +} + +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..4a86640d4d4 --- /dev/null +++ b/java/ql/src/experimental/Security/CWE/CWE-601/SpringUrlRedirect.qll @@ -0,0 +1,73 @@ +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 + +/** + * 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() 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() and + 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 + any(SpringRequestMappingMethod sqmm).polyCalls*(this.getEnclosingCallable()) + ) + 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() + ) + 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 + exists(RedirectBuilderExpr rbe | + rbe = cie.getArgument(0) and rbe.getRightOperand() = this.asExpr() + ) + ) + } +} diff --git a/java/ql/src/semmle/code/java/dataflow/ExternalFlow.qll b/java/ql/src/semmle/code/java/dataflow/ExternalFlow.qll index fee40a328a1..5e4a307002a 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 @@ -293,6 +294,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/dataflow/internal/DataFlowImpl.qll b/java/ql/src/semmle/code/java/dataflow/internal/DataFlowImpl.qll index 9498e51e7e6..058d66b1496 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(ArgNode arg | fwdFlow(arg, cc, config) and viableParamArg(call, _, arg) ) @@ -515,24 +512,22 @@ private module Stage1 { pragma[nomagic] predicate viableParamArgNodeCandFwd1( - DataFlowCall call, ParameterNode p, ArgumentNode 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, ArgumentNode arg, boolean toReturn, Configuration config - ) { - exists(ParameterNode 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, ArgumentNode arg, Configuration config) { + private predicate revFlowInToReturn(DataFlowCall call, ArgNode arg, Configuration config) { revFlowIn(call, arg, true, config) } @@ -597,7 +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(ParameterNode 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 @@ -663,7 +658,7 @@ private predicate flowOutOfCallNodeCand1( pragma[nomagic] private predicate viableParamArgNodeCand1( - DataFlowCall call, ParameterNode p, ArgumentNode arg, Configuration config + DataFlowCall call, ParamNode p, ArgNode arg, Configuration config ) { Stage1::viableParamArgNodeCandFwd1(call, p, arg, config) and Stage1::revFlow(arg, config) @@ -675,7 +670,7 @@ private predicate viableParamArgNodeCand1( */ pragma[nomagic] private predicate flowIntoCallNodeCand1( - DataFlowCall call, ArgumentNode arg, ParameterNode p, Configuration config + DataFlowCall call, ArgNode arg, ParamNode p, Configuration config ) { viableParamArgNodeCand1(call, p, arg, config) and Stage1::revFlow(p, config) and @@ -735,8 +730,7 @@ private predicate flowOutOfCallNodeCand1( */ pragma[nomagic] private predicate flowIntoCallNodeCand1( - DataFlowCall call, ArgumentNode arg, ParameterNode 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 | @@ -944,10 +938,10 @@ private module Stage2 { pragma[nomagic] private predicate fwdFlowIn( - DataFlowCall call, ParameterNode p, Cc outercc, Cc innercc, ApOption argAp, Ap ap, + DataFlowCall call, ParamNode p, Cc outercc, Cc innercc, ApOption argAp, Ap ap, Configuration config ) { - exists(ArgumentNode 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) @@ -992,7 +986,7 @@ private module Stage2 { private predicate fwdFlowIsEntered( DataFlowCall call, Cc cc, ApOption argAp, Ap ap, Configuration config ) { - exists(ParameterNode p | + exists(ParamNode p | fwdFlowIn(call, p, cc, _, argAp, ap, config) and PrevStage::parameterMayFlowThrough(p, _, getApprox(ap), config) ) @@ -1133,10 +1127,9 @@ private module Stage2 { pragma[nomagic] private predicate revFlowIn( - DataFlowCall call, ArgumentNode arg, boolean toReturn, ApOption returnAp, Ap ap, - Configuration config + DataFlowCall call, ArgNode arg, boolean toReturn, ApOption returnAp, Ap ap, Configuration config ) { - exists(ParameterNode p, boolean allowsFieldFlow | + exists(ParamNode p, boolean allowsFieldFlow | revFlow(p, toReturn, returnAp, ap, config) and flowIntoCall(call, arg, p, allowsFieldFlow, config) | @@ -1146,7 +1139,7 @@ private module Stage2 { pragma[nomagic] private predicate revFlowInToReturn( - DataFlowCall call, ArgumentNode 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) } @@ -1199,13 +1192,13 @@ private module Stage2 { pragma[noinline] private predicate parameterFlow( - ParameterNode 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(ParameterNode 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 @@ -1245,8 +1238,7 @@ private predicate flowOutOfCallNodeCand2( pragma[nomagic] private predicate flowIntoCallNodeCand2( - DataFlowCall call, ArgumentNode node1, ParameterNode 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 @@ -1260,8 +1252,8 @@ private module LocalFlowBigStep { */ private class FlowCheckNode extends Node { FlowCheckNode() { - this instanceof CastNode or - clearsContent(this, _) + castNode(this) or + clearsContentCached(this, _) } } @@ -1275,7 +1267,7 @@ private module LocalFlowBigStep { config.isSource(node) or jumpStep(_, node, config) or additionalJumpStep(_, node, config) or - node instanceof ParameterNode or + node instanceof ParamNode or node instanceof OutNodeExt or store(_, _, node, _) or read(_, _, node) or @@ -1321,21 +1313,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 +1342,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 +1376,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 +1435,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 +1577,10 @@ private module Stage3 { pragma[nomagic] private predicate fwdFlowIn( - DataFlowCall call, ParameterNode p, Cc outercc, Cc innercc, ApOption argAp, Ap ap, + DataFlowCall call, ParamNode p, Cc outercc, Cc innercc, ApOption argAp, Ap ap, Configuration config ) { - exists(ArgumentNode 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) @@ -1631,7 +1625,7 @@ private module Stage3 { private predicate fwdFlowIsEntered( DataFlowCall call, Cc cc, ApOption argAp, Ap ap, Configuration config ) { - exists(ParameterNode p | + exists(ParamNode p | fwdFlowIn(call, p, cc, _, argAp, ap, config) and PrevStage::parameterMayFlowThrough(p, _, unbindApa(getApprox(ap)), config) ) @@ -1772,10 +1766,9 @@ private module Stage3 { pragma[nomagic] private predicate revFlowIn( - DataFlowCall call, ArgumentNode arg, boolean toReturn, ApOption returnAp, Ap ap, - Configuration config + DataFlowCall call, ArgNode arg, boolean toReturn, ApOption returnAp, Ap ap, Configuration config ) { - exists(ParameterNode p, boolean allowsFieldFlow | + exists(ParamNode p, boolean allowsFieldFlow | revFlow(p, toReturn, returnAp, ap, config) and flowIntoCall(call, arg, p, allowsFieldFlow, config) | @@ -1785,7 +1778,7 @@ private module Stage3 { pragma[nomagic] private predicate revFlowInToReturn( - DataFlowCall call, ArgumentNode 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) } @@ -1838,13 +1831,13 @@ private module Stage3 { pragma[noinline] private predicate parameterFlow( - ParameterNode 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(ParameterNode 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 @@ -2088,7 +2081,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,8 +2148,7 @@ private module Stage4 { pragma[nomagic] private predicate flowIntoCall( - DataFlowCall call, ArgumentNode node1, ParameterNode 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 @@ -2300,10 +2292,10 @@ private module Stage4 { pragma[nomagic] private predicate fwdFlowIn( - DataFlowCall call, ParameterNode p, Cc outercc, Cc innercc, ApOption argAp, Ap ap, + DataFlowCall call, ParamNode p, Cc outercc, Cc innercc, ApOption argAp, Ap ap, Configuration config ) { - exists(ArgumentNode 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) @@ -2348,7 +2340,7 @@ private module Stage4 { private predicate fwdFlowIsEntered( DataFlowCall call, Cc cc, ApOption argAp, Ap ap, Configuration config ) { - exists(ParameterNode p | + exists(ParamNode p | fwdFlowIn(call, p, cc, _, argAp, ap, config) and PrevStage::parameterMayFlowThrough(p, _, unbindApa(getApprox(ap)), config) ) @@ -2489,10 +2481,9 @@ private module Stage4 { pragma[nomagic] private predicate revFlowIn( - DataFlowCall call, ArgumentNode arg, boolean toReturn, ApOption returnAp, Ap ap, - Configuration config + DataFlowCall call, ArgNode arg, boolean toReturn, ApOption returnAp, Ap ap, Configuration config ) { - exists(ParameterNode p, boolean allowsFieldFlow | + exists(ParamNode p, boolean allowsFieldFlow | revFlow(p, toReturn, returnAp, ap, config) and flowIntoCall(call, arg, p, allowsFieldFlow, config) | @@ -2502,7 +2493,7 @@ private module Stage4 { pragma[nomagic] private predicate revFlowInToReturn( - DataFlowCall call, ArgumentNode 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) } @@ -2555,13 +2546,13 @@ private module Stage4 { pragma[noinline] private predicate parameterFlow( - ParameterNode 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(ParameterNode 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 @@ -2606,7 +2597,7 @@ private predicate nodeMayUseSummary(Node n, AccessPathApprox apa, Configuration private newtype TSummaryCtx = TSummaryCtxNone() or - TSummaryCtxSome(ParameterNode p, AccessPath ap) { + TSummaryCtxSome(ParamNode p, AccessPath ap) { Stage4::parameterMayFlowThrough(p, _, ap.getApprox(), _) } @@ -2627,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 ParameterNode p; + private ParamNode p; private AccessPath ap; SummaryCtxSome() { this = TSummaryCtxSome(p, ap) } @@ -2758,7 +2749,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 +2970,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 +3139,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 +3226,7 @@ pragma[noinline] private predicate pathIntoArg( PathNodeMid mid, int i, CallContext cc, DataFlowCall call, AccessPath ap, AccessPathApprox apa ) { - exists(ArgumentNode arg | + exists(ArgNode arg | arg = mid.getNode() and cc = mid.getCallContext() and arg.argumentOf(call, i) and @@ -3248,7 +3239,7 @@ pragma[noinline] private predicate parameterCand( DataFlowCallable callable, int i, AccessPathApprox apa, Configuration config ) { - exists(ParameterNode p | + exists(ParamNode p | Stage4::revFlow(p, _, _, apa, config) and p.isParameterOf(callable, i) ) @@ -3272,7 +3263,7 @@ private predicate pathIntoCallable0( * respectively. */ private predicate pathIntoCallable( - PathNodeMid mid, ParameterNode 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 | @@ -3568,7 +3559,7 @@ private module FlowExploration { private newtype TSummaryCtx1 = TSummaryCtx1None() or - TSummaryCtx1Param(ParameterNode p) + TSummaryCtx1Param(ParamNode p) private newtype TSummaryCtx2 = TSummaryCtx2None() or @@ -3591,7 +3582,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 +3602,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 +3616,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 +3774,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 +3788,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 +3804,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 +3818,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 +3915,7 @@ private module FlowExploration { PartialPathNodeFwd mid, int i, CallContext cc, DataFlowCall call, PartialAccessPath ap, Configuration config ) { - exists(ArgumentNode arg | + exists(ArgNode arg | arg = mid.getNode() and cc = mid.getCallContext() and arg.argumentOf(call, i) and @@ -3943,7 +3934,7 @@ private module FlowExploration { } private predicate partialPathIntoCallable( - PartialPathNodeFwd mid, ParameterNode p, CallContext outercc, CallContextCall innercc, + PartialPathNodeFwd mid, ParamNode p, CallContext outercc, CallContextCall innercc, TSummaryCtx1 sc1, TSummaryCtx2 sc2, DataFlowCall call, PartialAccessPath ap, Configuration config ) { @@ -3980,7 +3971,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(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) ) @@ -4037,7 +4028,7 @@ private module FlowExploration { apConsRev(ap, c, ap0, config) ) or - exists(ParameterNode p | + exists(ParamNode p | mid.getNode() = p and viableParamArg(_, p, node) and sc1 = mid.getSummaryCtx1() and @@ -4115,7 +4106,7 @@ private module FlowExploration { int pos, TRevSummaryCtx1Some sc1, TRevSummaryCtx2Some sc2, RevPartialAccessPath ap, Configuration config ) { - exists(PartialPathNodeRev mid, ParameterNode p | + exists(PartialPathNodeRev mid, ParamNode p | mid.getNode() = p and p.isParameterOf(_, pos) and sc1 = mid.getSummaryCtx1() and @@ -4138,7 +4129,7 @@ private module FlowExploration { pragma[nomagic] private predicate revPartialPathThroughCallable( - PartialPathNodeRev mid, ArgumentNode 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 9498e51e7e6..058d66b1496 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(ArgNode arg | fwdFlow(arg, cc, config) and viableParamArg(call, _, arg) ) @@ -515,24 +512,22 @@ private module Stage1 { pragma[nomagic] predicate viableParamArgNodeCandFwd1( - DataFlowCall call, ParameterNode p, ArgumentNode 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, ArgumentNode arg, boolean toReturn, Configuration config - ) { - exists(ParameterNode 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, ArgumentNode arg, Configuration config) { + private predicate revFlowInToReturn(DataFlowCall call, ArgNode arg, Configuration config) { revFlowIn(call, arg, true, config) } @@ -597,7 +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(ParameterNode 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 @@ -663,7 +658,7 @@ private predicate flowOutOfCallNodeCand1( pragma[nomagic] private predicate viableParamArgNodeCand1( - DataFlowCall call, ParameterNode p, ArgumentNode arg, Configuration config + DataFlowCall call, ParamNode p, ArgNode arg, Configuration config ) { Stage1::viableParamArgNodeCandFwd1(call, p, arg, config) and Stage1::revFlow(arg, config) @@ -675,7 +670,7 @@ private predicate viableParamArgNodeCand1( */ pragma[nomagic] private predicate flowIntoCallNodeCand1( - DataFlowCall call, ArgumentNode arg, ParameterNode p, Configuration config + DataFlowCall call, ArgNode arg, ParamNode p, Configuration config ) { viableParamArgNodeCand1(call, p, arg, config) and Stage1::revFlow(p, config) and @@ -735,8 +730,7 @@ private predicate flowOutOfCallNodeCand1( */ pragma[nomagic] private predicate flowIntoCallNodeCand1( - DataFlowCall call, ArgumentNode arg, ParameterNode 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 | @@ -944,10 +938,10 @@ private module Stage2 { pragma[nomagic] private predicate fwdFlowIn( - DataFlowCall call, ParameterNode p, Cc outercc, Cc innercc, ApOption argAp, Ap ap, + DataFlowCall call, ParamNode p, Cc outercc, Cc innercc, ApOption argAp, Ap ap, Configuration config ) { - exists(ArgumentNode 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) @@ -992,7 +986,7 @@ private module Stage2 { private predicate fwdFlowIsEntered( DataFlowCall call, Cc cc, ApOption argAp, Ap ap, Configuration config ) { - exists(ParameterNode p | + exists(ParamNode p | fwdFlowIn(call, p, cc, _, argAp, ap, config) and PrevStage::parameterMayFlowThrough(p, _, getApprox(ap), config) ) @@ -1133,10 +1127,9 @@ private module Stage2 { pragma[nomagic] private predicate revFlowIn( - DataFlowCall call, ArgumentNode arg, boolean toReturn, ApOption returnAp, Ap ap, - Configuration config + DataFlowCall call, ArgNode arg, boolean toReturn, ApOption returnAp, Ap ap, Configuration config ) { - exists(ParameterNode p, boolean allowsFieldFlow | + exists(ParamNode p, boolean allowsFieldFlow | revFlow(p, toReturn, returnAp, ap, config) and flowIntoCall(call, arg, p, allowsFieldFlow, config) | @@ -1146,7 +1139,7 @@ private module Stage2 { pragma[nomagic] private predicate revFlowInToReturn( - DataFlowCall call, ArgumentNode 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) } @@ -1199,13 +1192,13 @@ private module Stage2 { pragma[noinline] private predicate parameterFlow( - ParameterNode 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(ParameterNode 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 @@ -1245,8 +1238,7 @@ private predicate flowOutOfCallNodeCand2( pragma[nomagic] private predicate flowIntoCallNodeCand2( - DataFlowCall call, ArgumentNode node1, ParameterNode 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 @@ -1260,8 +1252,8 @@ private module LocalFlowBigStep { */ private class FlowCheckNode extends Node { FlowCheckNode() { - this instanceof CastNode or - clearsContent(this, _) + castNode(this) or + clearsContentCached(this, _) } } @@ -1275,7 +1267,7 @@ private module LocalFlowBigStep { config.isSource(node) or jumpStep(_, node, config) or additionalJumpStep(_, node, config) or - node instanceof ParameterNode or + node instanceof ParamNode or node instanceof OutNodeExt or store(_, _, node, _) or read(_, _, node) or @@ -1321,21 +1313,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 +1342,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 +1376,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 +1435,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 +1577,10 @@ private module Stage3 { pragma[nomagic] private predicate fwdFlowIn( - DataFlowCall call, ParameterNode p, Cc outercc, Cc innercc, ApOption argAp, Ap ap, + DataFlowCall call, ParamNode p, Cc outercc, Cc innercc, ApOption argAp, Ap ap, Configuration config ) { - exists(ArgumentNode 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) @@ -1631,7 +1625,7 @@ private module Stage3 { private predicate fwdFlowIsEntered( DataFlowCall call, Cc cc, ApOption argAp, Ap ap, Configuration config ) { - exists(ParameterNode p | + exists(ParamNode p | fwdFlowIn(call, p, cc, _, argAp, ap, config) and PrevStage::parameterMayFlowThrough(p, _, unbindApa(getApprox(ap)), config) ) @@ -1772,10 +1766,9 @@ private module Stage3 { pragma[nomagic] private predicate revFlowIn( - DataFlowCall call, ArgumentNode arg, boolean toReturn, ApOption returnAp, Ap ap, - Configuration config + DataFlowCall call, ArgNode arg, boolean toReturn, ApOption returnAp, Ap ap, Configuration config ) { - exists(ParameterNode p, boolean allowsFieldFlow | + exists(ParamNode p, boolean allowsFieldFlow | revFlow(p, toReturn, returnAp, ap, config) and flowIntoCall(call, arg, p, allowsFieldFlow, config) | @@ -1785,7 +1778,7 @@ private module Stage3 { pragma[nomagic] private predicate revFlowInToReturn( - DataFlowCall call, ArgumentNode 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) } @@ -1838,13 +1831,13 @@ private module Stage3 { pragma[noinline] private predicate parameterFlow( - ParameterNode 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(ParameterNode 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 @@ -2088,7 +2081,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,8 +2148,7 @@ private module Stage4 { pragma[nomagic] private predicate flowIntoCall( - DataFlowCall call, ArgumentNode node1, ParameterNode 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 @@ -2300,10 +2292,10 @@ private module Stage4 { pragma[nomagic] private predicate fwdFlowIn( - DataFlowCall call, ParameterNode p, Cc outercc, Cc innercc, ApOption argAp, Ap ap, + DataFlowCall call, ParamNode p, Cc outercc, Cc innercc, ApOption argAp, Ap ap, Configuration config ) { - exists(ArgumentNode 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) @@ -2348,7 +2340,7 @@ private module Stage4 { private predicate fwdFlowIsEntered( DataFlowCall call, Cc cc, ApOption argAp, Ap ap, Configuration config ) { - exists(ParameterNode p | + exists(ParamNode p | fwdFlowIn(call, p, cc, _, argAp, ap, config) and PrevStage::parameterMayFlowThrough(p, _, unbindApa(getApprox(ap)), config) ) @@ -2489,10 +2481,9 @@ private module Stage4 { pragma[nomagic] private predicate revFlowIn( - DataFlowCall call, ArgumentNode arg, boolean toReturn, ApOption returnAp, Ap ap, - Configuration config + DataFlowCall call, ArgNode arg, boolean toReturn, ApOption returnAp, Ap ap, Configuration config ) { - exists(ParameterNode p, boolean allowsFieldFlow | + exists(ParamNode p, boolean allowsFieldFlow | revFlow(p, toReturn, returnAp, ap, config) and flowIntoCall(call, arg, p, allowsFieldFlow, config) | @@ -2502,7 +2493,7 @@ private module Stage4 { pragma[nomagic] private predicate revFlowInToReturn( - DataFlowCall call, ArgumentNode 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) } @@ -2555,13 +2546,13 @@ private module Stage4 { pragma[noinline] private predicate parameterFlow( - ParameterNode 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(ParameterNode 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 @@ -2606,7 +2597,7 @@ private predicate nodeMayUseSummary(Node n, AccessPathApprox apa, Configuration private newtype TSummaryCtx = TSummaryCtxNone() or - TSummaryCtxSome(ParameterNode p, AccessPath ap) { + TSummaryCtxSome(ParamNode p, AccessPath ap) { Stage4::parameterMayFlowThrough(p, _, ap.getApprox(), _) } @@ -2627,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 ParameterNode p; + private ParamNode p; private AccessPath ap; SummaryCtxSome() { this = TSummaryCtxSome(p, ap) } @@ -2758,7 +2749,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 +2970,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 +3139,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 +3226,7 @@ pragma[noinline] private predicate pathIntoArg( PathNodeMid mid, int i, CallContext cc, DataFlowCall call, AccessPath ap, AccessPathApprox apa ) { - exists(ArgumentNode arg | + exists(ArgNode arg | arg = mid.getNode() and cc = mid.getCallContext() and arg.argumentOf(call, i) and @@ -3248,7 +3239,7 @@ pragma[noinline] private predicate parameterCand( DataFlowCallable callable, int i, AccessPathApprox apa, Configuration config ) { - exists(ParameterNode p | + exists(ParamNode p | Stage4::revFlow(p, _, _, apa, config) and p.isParameterOf(callable, i) ) @@ -3272,7 +3263,7 @@ private predicate pathIntoCallable0( * respectively. */ private predicate pathIntoCallable( - PathNodeMid mid, ParameterNode 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 | @@ -3568,7 +3559,7 @@ private module FlowExploration { private newtype TSummaryCtx1 = TSummaryCtx1None() or - TSummaryCtx1Param(ParameterNode p) + TSummaryCtx1Param(ParamNode p) private newtype TSummaryCtx2 = TSummaryCtx2None() or @@ -3591,7 +3582,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 +3602,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 +3616,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 +3774,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 +3788,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 +3804,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 +3818,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 +3915,7 @@ private module FlowExploration { PartialPathNodeFwd mid, int i, CallContext cc, DataFlowCall call, PartialAccessPath ap, Configuration config ) { - exists(ArgumentNode arg | + exists(ArgNode arg | arg = mid.getNode() and cc = mid.getCallContext() and arg.argumentOf(call, i) and @@ -3943,7 +3934,7 @@ private module FlowExploration { } private predicate partialPathIntoCallable( - PartialPathNodeFwd mid, ParameterNode p, CallContext outercc, CallContextCall innercc, + PartialPathNodeFwd mid, ParamNode p, CallContext outercc, CallContextCall innercc, TSummaryCtx1 sc1, TSummaryCtx2 sc2, DataFlowCall call, PartialAccessPath ap, Configuration config ) { @@ -3980,7 +3971,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(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) ) @@ -4037,7 +4028,7 @@ private module FlowExploration { apConsRev(ap, c, ap0, config) ) or - exists(ParameterNode p | + exists(ParamNode p | mid.getNode() = p and viableParamArg(_, p, node) and sc1 = mid.getSummaryCtx1() and @@ -4115,7 +4106,7 @@ private module FlowExploration { int pos, TRevSummaryCtx1Some sc1, TRevSummaryCtx2Some sc2, RevPartialAccessPath ap, Configuration config ) { - exists(PartialPathNodeRev mid, ParameterNode p | + exists(PartialPathNodeRev mid, ParamNode p | mid.getNode() = p and p.isParameterOf(_, pos) and sc1 = mid.getSummaryCtx1() and @@ -4138,7 +4129,7 @@ private module FlowExploration { pragma[nomagic] private predicate revPartialPathThroughCallable( - PartialPathNodeRev mid, ArgumentNode 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 9498e51e7e6..058d66b1496 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(ArgNode arg | fwdFlow(arg, cc, config) and viableParamArg(call, _, arg) ) @@ -515,24 +512,22 @@ private module Stage1 { pragma[nomagic] predicate viableParamArgNodeCandFwd1( - DataFlowCall call, ParameterNode p, ArgumentNode 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, ArgumentNode arg, boolean toReturn, Configuration config - ) { - exists(ParameterNode 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, ArgumentNode arg, Configuration config) { + private predicate revFlowInToReturn(DataFlowCall call, ArgNode arg, Configuration config) { revFlowIn(call, arg, true, config) } @@ -597,7 +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(ParameterNode 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 @@ -663,7 +658,7 @@ private predicate flowOutOfCallNodeCand1( pragma[nomagic] private predicate viableParamArgNodeCand1( - DataFlowCall call, ParameterNode p, ArgumentNode arg, Configuration config + DataFlowCall call, ParamNode p, ArgNode arg, Configuration config ) { Stage1::viableParamArgNodeCandFwd1(call, p, arg, config) and Stage1::revFlow(arg, config) @@ -675,7 +670,7 @@ private predicate viableParamArgNodeCand1( */ pragma[nomagic] private predicate flowIntoCallNodeCand1( - DataFlowCall call, ArgumentNode arg, ParameterNode p, Configuration config + DataFlowCall call, ArgNode arg, ParamNode p, Configuration config ) { viableParamArgNodeCand1(call, p, arg, config) and Stage1::revFlow(p, config) and @@ -735,8 +730,7 @@ private predicate flowOutOfCallNodeCand1( */ pragma[nomagic] private predicate flowIntoCallNodeCand1( - DataFlowCall call, ArgumentNode arg, ParameterNode 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 | @@ -944,10 +938,10 @@ private module Stage2 { pragma[nomagic] private predicate fwdFlowIn( - DataFlowCall call, ParameterNode p, Cc outercc, Cc innercc, ApOption argAp, Ap ap, + DataFlowCall call, ParamNode p, Cc outercc, Cc innercc, ApOption argAp, Ap ap, Configuration config ) { - exists(ArgumentNode 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) @@ -992,7 +986,7 @@ private module Stage2 { private predicate fwdFlowIsEntered( DataFlowCall call, Cc cc, ApOption argAp, Ap ap, Configuration config ) { - exists(ParameterNode p | + exists(ParamNode p | fwdFlowIn(call, p, cc, _, argAp, ap, config) and PrevStage::parameterMayFlowThrough(p, _, getApprox(ap), config) ) @@ -1133,10 +1127,9 @@ private module Stage2 { pragma[nomagic] private predicate revFlowIn( - DataFlowCall call, ArgumentNode arg, boolean toReturn, ApOption returnAp, Ap ap, - Configuration config + DataFlowCall call, ArgNode arg, boolean toReturn, ApOption returnAp, Ap ap, Configuration config ) { - exists(ParameterNode p, boolean allowsFieldFlow | + exists(ParamNode p, boolean allowsFieldFlow | revFlow(p, toReturn, returnAp, ap, config) and flowIntoCall(call, arg, p, allowsFieldFlow, config) | @@ -1146,7 +1139,7 @@ private module Stage2 { pragma[nomagic] private predicate revFlowInToReturn( - DataFlowCall call, ArgumentNode 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) } @@ -1199,13 +1192,13 @@ private module Stage2 { pragma[noinline] private predicate parameterFlow( - ParameterNode 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(ParameterNode 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 @@ -1245,8 +1238,7 @@ private predicate flowOutOfCallNodeCand2( pragma[nomagic] private predicate flowIntoCallNodeCand2( - DataFlowCall call, ArgumentNode node1, ParameterNode 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 @@ -1260,8 +1252,8 @@ private module LocalFlowBigStep { */ private class FlowCheckNode extends Node { FlowCheckNode() { - this instanceof CastNode or - clearsContent(this, _) + castNode(this) or + clearsContentCached(this, _) } } @@ -1275,7 +1267,7 @@ private module LocalFlowBigStep { config.isSource(node) or jumpStep(_, node, config) or additionalJumpStep(_, node, config) or - node instanceof ParameterNode or + node instanceof ParamNode or node instanceof OutNodeExt or store(_, _, node, _) or read(_, _, node) or @@ -1321,21 +1313,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 +1342,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 +1376,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 +1435,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 +1577,10 @@ private module Stage3 { pragma[nomagic] private predicate fwdFlowIn( - DataFlowCall call, ParameterNode p, Cc outercc, Cc innercc, ApOption argAp, Ap ap, + DataFlowCall call, ParamNode p, Cc outercc, Cc innercc, ApOption argAp, Ap ap, Configuration config ) { - exists(ArgumentNode 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) @@ -1631,7 +1625,7 @@ private module Stage3 { private predicate fwdFlowIsEntered( DataFlowCall call, Cc cc, ApOption argAp, Ap ap, Configuration config ) { - exists(ParameterNode p | + exists(ParamNode p | fwdFlowIn(call, p, cc, _, argAp, ap, config) and PrevStage::parameterMayFlowThrough(p, _, unbindApa(getApprox(ap)), config) ) @@ -1772,10 +1766,9 @@ private module Stage3 { pragma[nomagic] private predicate revFlowIn( - DataFlowCall call, ArgumentNode arg, boolean toReturn, ApOption returnAp, Ap ap, - Configuration config + DataFlowCall call, ArgNode arg, boolean toReturn, ApOption returnAp, Ap ap, Configuration config ) { - exists(ParameterNode p, boolean allowsFieldFlow | + exists(ParamNode p, boolean allowsFieldFlow | revFlow(p, toReturn, returnAp, ap, config) and flowIntoCall(call, arg, p, allowsFieldFlow, config) | @@ -1785,7 +1778,7 @@ private module Stage3 { pragma[nomagic] private predicate revFlowInToReturn( - DataFlowCall call, ArgumentNode 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) } @@ -1838,13 +1831,13 @@ private module Stage3 { pragma[noinline] private predicate parameterFlow( - ParameterNode 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(ParameterNode 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 @@ -2088,7 +2081,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,8 +2148,7 @@ private module Stage4 { pragma[nomagic] private predicate flowIntoCall( - DataFlowCall call, ArgumentNode node1, ParameterNode 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 @@ -2300,10 +2292,10 @@ private module Stage4 { pragma[nomagic] private predicate fwdFlowIn( - DataFlowCall call, ParameterNode p, Cc outercc, Cc innercc, ApOption argAp, Ap ap, + DataFlowCall call, ParamNode p, Cc outercc, Cc innercc, ApOption argAp, Ap ap, Configuration config ) { - exists(ArgumentNode 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) @@ -2348,7 +2340,7 @@ private module Stage4 { private predicate fwdFlowIsEntered( DataFlowCall call, Cc cc, ApOption argAp, Ap ap, Configuration config ) { - exists(ParameterNode p | + exists(ParamNode p | fwdFlowIn(call, p, cc, _, argAp, ap, config) and PrevStage::parameterMayFlowThrough(p, _, unbindApa(getApprox(ap)), config) ) @@ -2489,10 +2481,9 @@ private module Stage4 { pragma[nomagic] private predicate revFlowIn( - DataFlowCall call, ArgumentNode arg, boolean toReturn, ApOption returnAp, Ap ap, - Configuration config + DataFlowCall call, ArgNode arg, boolean toReturn, ApOption returnAp, Ap ap, Configuration config ) { - exists(ParameterNode p, boolean allowsFieldFlow | + exists(ParamNode p, boolean allowsFieldFlow | revFlow(p, toReturn, returnAp, ap, config) and flowIntoCall(call, arg, p, allowsFieldFlow, config) | @@ -2502,7 +2493,7 @@ private module Stage4 { pragma[nomagic] private predicate revFlowInToReturn( - DataFlowCall call, ArgumentNode 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) } @@ -2555,13 +2546,13 @@ private module Stage4 { pragma[noinline] private predicate parameterFlow( - ParameterNode 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(ParameterNode 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 @@ -2606,7 +2597,7 @@ private predicate nodeMayUseSummary(Node n, AccessPathApprox apa, Configuration private newtype TSummaryCtx = TSummaryCtxNone() or - TSummaryCtxSome(ParameterNode p, AccessPath ap) { + TSummaryCtxSome(ParamNode p, AccessPath ap) { Stage4::parameterMayFlowThrough(p, _, ap.getApprox(), _) } @@ -2627,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 ParameterNode p; + private ParamNode p; private AccessPath ap; SummaryCtxSome() { this = TSummaryCtxSome(p, ap) } @@ -2758,7 +2749,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 +2970,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 +3139,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 +3226,7 @@ pragma[noinline] private predicate pathIntoArg( PathNodeMid mid, int i, CallContext cc, DataFlowCall call, AccessPath ap, AccessPathApprox apa ) { - exists(ArgumentNode arg | + exists(ArgNode arg | arg = mid.getNode() and cc = mid.getCallContext() and arg.argumentOf(call, i) and @@ -3248,7 +3239,7 @@ pragma[noinline] private predicate parameterCand( DataFlowCallable callable, int i, AccessPathApprox apa, Configuration config ) { - exists(ParameterNode p | + exists(ParamNode p | Stage4::revFlow(p, _, _, apa, config) and p.isParameterOf(callable, i) ) @@ -3272,7 +3263,7 @@ private predicate pathIntoCallable0( * respectively. */ private predicate pathIntoCallable( - PathNodeMid mid, ParameterNode 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 | @@ -3568,7 +3559,7 @@ private module FlowExploration { private newtype TSummaryCtx1 = TSummaryCtx1None() or - TSummaryCtx1Param(ParameterNode p) + TSummaryCtx1Param(ParamNode p) private newtype TSummaryCtx2 = TSummaryCtx2None() or @@ -3591,7 +3582,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 +3602,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 +3616,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 +3774,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 +3788,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 +3804,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 +3818,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 +3915,7 @@ private module FlowExploration { PartialPathNodeFwd mid, int i, CallContext cc, DataFlowCall call, PartialAccessPath ap, Configuration config ) { - exists(ArgumentNode arg | + exists(ArgNode arg | arg = mid.getNode() and cc = mid.getCallContext() and arg.argumentOf(call, i) and @@ -3943,7 +3934,7 @@ private module FlowExploration { } private predicate partialPathIntoCallable( - PartialPathNodeFwd mid, ParameterNode p, CallContext outercc, CallContextCall innercc, + PartialPathNodeFwd mid, ParamNode p, CallContext outercc, CallContextCall innercc, TSummaryCtx1 sc1, TSummaryCtx2 sc2, DataFlowCall call, PartialAccessPath ap, Configuration config ) { @@ -3980,7 +3971,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(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) ) @@ -4037,7 +4028,7 @@ private module FlowExploration { apConsRev(ap, c, ap0, config) ) or - exists(ParameterNode p | + exists(ParamNode p | mid.getNode() = p and viableParamArg(_, p, node) and sc1 = mid.getSummaryCtx1() and @@ -4115,7 +4106,7 @@ private module FlowExploration { int pos, TRevSummaryCtx1Some sc1, TRevSummaryCtx2Some sc2, RevPartialAccessPath ap, Configuration config ) { - exists(PartialPathNodeRev mid, ParameterNode p | + exists(PartialPathNodeRev mid, ParamNode p | mid.getNode() = p and p.isParameterOf(_, pos) and sc1 = mid.getSummaryCtx1() and @@ -4138,7 +4129,7 @@ private module FlowExploration { pragma[nomagic] private predicate revPartialPathThroughCallable( - PartialPathNodeRev mid, ArgumentNode 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 9498e51e7e6..058d66b1496 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(ArgNode arg | fwdFlow(arg, cc, config) and viableParamArg(call, _, arg) ) @@ -515,24 +512,22 @@ private module Stage1 { pragma[nomagic] predicate viableParamArgNodeCandFwd1( - DataFlowCall call, ParameterNode p, ArgumentNode 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, ArgumentNode arg, boolean toReturn, Configuration config - ) { - exists(ParameterNode 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, ArgumentNode arg, Configuration config) { + private predicate revFlowInToReturn(DataFlowCall call, ArgNode arg, Configuration config) { revFlowIn(call, arg, true, config) } @@ -597,7 +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(ParameterNode 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 @@ -663,7 +658,7 @@ private predicate flowOutOfCallNodeCand1( pragma[nomagic] private predicate viableParamArgNodeCand1( - DataFlowCall call, ParameterNode p, ArgumentNode arg, Configuration config + DataFlowCall call, ParamNode p, ArgNode arg, Configuration config ) { Stage1::viableParamArgNodeCandFwd1(call, p, arg, config) and Stage1::revFlow(arg, config) @@ -675,7 +670,7 @@ private predicate viableParamArgNodeCand1( */ pragma[nomagic] private predicate flowIntoCallNodeCand1( - DataFlowCall call, ArgumentNode arg, ParameterNode p, Configuration config + DataFlowCall call, ArgNode arg, ParamNode p, Configuration config ) { viableParamArgNodeCand1(call, p, arg, config) and Stage1::revFlow(p, config) and @@ -735,8 +730,7 @@ private predicate flowOutOfCallNodeCand1( */ pragma[nomagic] private predicate flowIntoCallNodeCand1( - DataFlowCall call, ArgumentNode arg, ParameterNode 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 | @@ -944,10 +938,10 @@ private module Stage2 { pragma[nomagic] private predicate fwdFlowIn( - DataFlowCall call, ParameterNode p, Cc outercc, Cc innercc, ApOption argAp, Ap ap, + DataFlowCall call, ParamNode p, Cc outercc, Cc innercc, ApOption argAp, Ap ap, Configuration config ) { - exists(ArgumentNode 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) @@ -992,7 +986,7 @@ private module Stage2 { private predicate fwdFlowIsEntered( DataFlowCall call, Cc cc, ApOption argAp, Ap ap, Configuration config ) { - exists(ParameterNode p | + exists(ParamNode p | fwdFlowIn(call, p, cc, _, argAp, ap, config) and PrevStage::parameterMayFlowThrough(p, _, getApprox(ap), config) ) @@ -1133,10 +1127,9 @@ private module Stage2 { pragma[nomagic] private predicate revFlowIn( - DataFlowCall call, ArgumentNode arg, boolean toReturn, ApOption returnAp, Ap ap, - Configuration config + DataFlowCall call, ArgNode arg, boolean toReturn, ApOption returnAp, Ap ap, Configuration config ) { - exists(ParameterNode p, boolean allowsFieldFlow | + exists(ParamNode p, boolean allowsFieldFlow | revFlow(p, toReturn, returnAp, ap, config) and flowIntoCall(call, arg, p, allowsFieldFlow, config) | @@ -1146,7 +1139,7 @@ private module Stage2 { pragma[nomagic] private predicate revFlowInToReturn( - DataFlowCall call, ArgumentNode 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) } @@ -1199,13 +1192,13 @@ private module Stage2 { pragma[noinline] private predicate parameterFlow( - ParameterNode 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(ParameterNode 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 @@ -1245,8 +1238,7 @@ private predicate flowOutOfCallNodeCand2( pragma[nomagic] private predicate flowIntoCallNodeCand2( - DataFlowCall call, ArgumentNode node1, ParameterNode 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 @@ -1260,8 +1252,8 @@ private module LocalFlowBigStep { */ private class FlowCheckNode extends Node { FlowCheckNode() { - this instanceof CastNode or - clearsContent(this, _) + castNode(this) or + clearsContentCached(this, _) } } @@ -1275,7 +1267,7 @@ private module LocalFlowBigStep { config.isSource(node) or jumpStep(_, node, config) or additionalJumpStep(_, node, config) or - node instanceof ParameterNode or + node instanceof ParamNode or node instanceof OutNodeExt or store(_, _, node, _) or read(_, _, node) or @@ -1321,21 +1313,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 +1342,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 +1376,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 +1435,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 +1577,10 @@ private module Stage3 { pragma[nomagic] private predicate fwdFlowIn( - DataFlowCall call, ParameterNode p, Cc outercc, Cc innercc, ApOption argAp, Ap ap, + DataFlowCall call, ParamNode p, Cc outercc, Cc innercc, ApOption argAp, Ap ap, Configuration config ) { - exists(ArgumentNode 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) @@ -1631,7 +1625,7 @@ private module Stage3 { private predicate fwdFlowIsEntered( DataFlowCall call, Cc cc, ApOption argAp, Ap ap, Configuration config ) { - exists(ParameterNode p | + exists(ParamNode p | fwdFlowIn(call, p, cc, _, argAp, ap, config) and PrevStage::parameterMayFlowThrough(p, _, unbindApa(getApprox(ap)), config) ) @@ -1772,10 +1766,9 @@ private module Stage3 { pragma[nomagic] private predicate revFlowIn( - DataFlowCall call, ArgumentNode arg, boolean toReturn, ApOption returnAp, Ap ap, - Configuration config + DataFlowCall call, ArgNode arg, boolean toReturn, ApOption returnAp, Ap ap, Configuration config ) { - exists(ParameterNode p, boolean allowsFieldFlow | + exists(ParamNode p, boolean allowsFieldFlow | revFlow(p, toReturn, returnAp, ap, config) and flowIntoCall(call, arg, p, allowsFieldFlow, config) | @@ -1785,7 +1778,7 @@ private module Stage3 { pragma[nomagic] private predicate revFlowInToReturn( - DataFlowCall call, ArgumentNode 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) } @@ -1838,13 +1831,13 @@ private module Stage3 { pragma[noinline] private predicate parameterFlow( - ParameterNode 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(ParameterNode 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 @@ -2088,7 +2081,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,8 +2148,7 @@ private module Stage4 { pragma[nomagic] private predicate flowIntoCall( - DataFlowCall call, ArgumentNode node1, ParameterNode 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 @@ -2300,10 +2292,10 @@ private module Stage4 { pragma[nomagic] private predicate fwdFlowIn( - DataFlowCall call, ParameterNode p, Cc outercc, Cc innercc, ApOption argAp, Ap ap, + DataFlowCall call, ParamNode p, Cc outercc, Cc innercc, ApOption argAp, Ap ap, Configuration config ) { - exists(ArgumentNode 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) @@ -2348,7 +2340,7 @@ private module Stage4 { private predicate fwdFlowIsEntered( DataFlowCall call, Cc cc, ApOption argAp, Ap ap, Configuration config ) { - exists(ParameterNode p | + exists(ParamNode p | fwdFlowIn(call, p, cc, _, argAp, ap, config) and PrevStage::parameterMayFlowThrough(p, _, unbindApa(getApprox(ap)), config) ) @@ -2489,10 +2481,9 @@ private module Stage4 { pragma[nomagic] private predicate revFlowIn( - DataFlowCall call, ArgumentNode arg, boolean toReturn, ApOption returnAp, Ap ap, - Configuration config + DataFlowCall call, ArgNode arg, boolean toReturn, ApOption returnAp, Ap ap, Configuration config ) { - exists(ParameterNode p, boolean allowsFieldFlow | + exists(ParamNode p, boolean allowsFieldFlow | revFlow(p, toReturn, returnAp, ap, config) and flowIntoCall(call, arg, p, allowsFieldFlow, config) | @@ -2502,7 +2493,7 @@ private module Stage4 { pragma[nomagic] private predicate revFlowInToReturn( - DataFlowCall call, ArgumentNode 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) } @@ -2555,13 +2546,13 @@ private module Stage4 { pragma[noinline] private predicate parameterFlow( - ParameterNode 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(ParameterNode 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 @@ -2606,7 +2597,7 @@ private predicate nodeMayUseSummary(Node n, AccessPathApprox apa, Configuration private newtype TSummaryCtx = TSummaryCtxNone() or - TSummaryCtxSome(ParameterNode p, AccessPath ap) { + TSummaryCtxSome(ParamNode p, AccessPath ap) { Stage4::parameterMayFlowThrough(p, _, ap.getApprox(), _) } @@ -2627,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 ParameterNode p; + private ParamNode p; private AccessPath ap; SummaryCtxSome() { this = TSummaryCtxSome(p, ap) } @@ -2758,7 +2749,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 +2970,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 +3139,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 +3226,7 @@ pragma[noinline] private predicate pathIntoArg( PathNodeMid mid, int i, CallContext cc, DataFlowCall call, AccessPath ap, AccessPathApprox apa ) { - exists(ArgumentNode arg | + exists(ArgNode arg | arg = mid.getNode() and cc = mid.getCallContext() and arg.argumentOf(call, i) and @@ -3248,7 +3239,7 @@ pragma[noinline] private predicate parameterCand( DataFlowCallable callable, int i, AccessPathApprox apa, Configuration config ) { - exists(ParameterNode p | + exists(ParamNode p | Stage4::revFlow(p, _, _, apa, config) and p.isParameterOf(callable, i) ) @@ -3272,7 +3263,7 @@ private predicate pathIntoCallable0( * respectively. */ private predicate pathIntoCallable( - PathNodeMid mid, ParameterNode 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 | @@ -3568,7 +3559,7 @@ private module FlowExploration { private newtype TSummaryCtx1 = TSummaryCtx1None() or - TSummaryCtx1Param(ParameterNode p) + TSummaryCtx1Param(ParamNode p) private newtype TSummaryCtx2 = TSummaryCtx2None() or @@ -3591,7 +3582,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 +3602,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 +3616,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 +3774,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 +3788,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 +3804,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 +3818,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 +3915,7 @@ private module FlowExploration { PartialPathNodeFwd mid, int i, CallContext cc, DataFlowCall call, PartialAccessPath ap, Configuration config ) { - exists(ArgumentNode arg | + exists(ArgNode arg | arg = mid.getNode() and cc = mid.getCallContext() and arg.argumentOf(call, i) and @@ -3943,7 +3934,7 @@ private module FlowExploration { } private predicate partialPathIntoCallable( - PartialPathNodeFwd mid, ParameterNode p, CallContext outercc, CallContextCall innercc, + PartialPathNodeFwd mid, ParamNode p, CallContext outercc, CallContextCall innercc, TSummaryCtx1 sc1, TSummaryCtx2 sc2, DataFlowCall call, PartialAccessPath ap, Configuration config ) { @@ -3980,7 +3971,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(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) ) @@ -4037,7 +4028,7 @@ private module FlowExploration { apConsRev(ap, c, ap0, config) ) or - exists(ParameterNode p | + exists(ParamNode p | mid.getNode() = p and viableParamArg(_, p, node) and sc1 = mid.getSummaryCtx1() and @@ -4115,7 +4106,7 @@ private module FlowExploration { int pos, TRevSummaryCtx1Some sc1, TRevSummaryCtx2Some sc2, RevPartialAccessPath ap, Configuration config ) { - exists(PartialPathNodeRev mid, ParameterNode p | + exists(PartialPathNodeRev mid, ParamNode p | mid.getNode() = p and p.isParameterOf(_, pos) and sc1 = mid.getSummaryCtx1() and @@ -4138,7 +4129,7 @@ private module FlowExploration { pragma[nomagic] private predicate revPartialPathThroughCallable( - PartialPathNodeRev mid, ArgumentNode 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 9498e51e7e6..058d66b1496 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(ArgNode arg | fwdFlow(arg, cc, config) and viableParamArg(call, _, arg) ) @@ -515,24 +512,22 @@ private module Stage1 { pragma[nomagic] predicate viableParamArgNodeCandFwd1( - DataFlowCall call, ParameterNode p, ArgumentNode 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, ArgumentNode arg, boolean toReturn, Configuration config - ) { - exists(ParameterNode 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, ArgumentNode arg, Configuration config) { + private predicate revFlowInToReturn(DataFlowCall call, ArgNode arg, Configuration config) { revFlowIn(call, arg, true, config) } @@ -597,7 +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(ParameterNode 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 @@ -663,7 +658,7 @@ private predicate flowOutOfCallNodeCand1( pragma[nomagic] private predicate viableParamArgNodeCand1( - DataFlowCall call, ParameterNode p, ArgumentNode arg, Configuration config + DataFlowCall call, ParamNode p, ArgNode arg, Configuration config ) { Stage1::viableParamArgNodeCandFwd1(call, p, arg, config) and Stage1::revFlow(arg, config) @@ -675,7 +670,7 @@ private predicate viableParamArgNodeCand1( */ pragma[nomagic] private predicate flowIntoCallNodeCand1( - DataFlowCall call, ArgumentNode arg, ParameterNode p, Configuration config + DataFlowCall call, ArgNode arg, ParamNode p, Configuration config ) { viableParamArgNodeCand1(call, p, arg, config) and Stage1::revFlow(p, config) and @@ -735,8 +730,7 @@ private predicate flowOutOfCallNodeCand1( */ pragma[nomagic] private predicate flowIntoCallNodeCand1( - DataFlowCall call, ArgumentNode arg, ParameterNode 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 | @@ -944,10 +938,10 @@ private module Stage2 { pragma[nomagic] private predicate fwdFlowIn( - DataFlowCall call, ParameterNode p, Cc outercc, Cc innercc, ApOption argAp, Ap ap, + DataFlowCall call, ParamNode p, Cc outercc, Cc innercc, ApOption argAp, Ap ap, Configuration config ) { - exists(ArgumentNode 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) @@ -992,7 +986,7 @@ private module Stage2 { private predicate fwdFlowIsEntered( DataFlowCall call, Cc cc, ApOption argAp, Ap ap, Configuration config ) { - exists(ParameterNode p | + exists(ParamNode p | fwdFlowIn(call, p, cc, _, argAp, ap, config) and PrevStage::parameterMayFlowThrough(p, _, getApprox(ap), config) ) @@ -1133,10 +1127,9 @@ private module Stage2 { pragma[nomagic] private predicate revFlowIn( - DataFlowCall call, ArgumentNode arg, boolean toReturn, ApOption returnAp, Ap ap, - Configuration config + DataFlowCall call, ArgNode arg, boolean toReturn, ApOption returnAp, Ap ap, Configuration config ) { - exists(ParameterNode p, boolean allowsFieldFlow | + exists(ParamNode p, boolean allowsFieldFlow | revFlow(p, toReturn, returnAp, ap, config) and flowIntoCall(call, arg, p, allowsFieldFlow, config) | @@ -1146,7 +1139,7 @@ private module Stage2 { pragma[nomagic] private predicate revFlowInToReturn( - DataFlowCall call, ArgumentNode 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) } @@ -1199,13 +1192,13 @@ private module Stage2 { pragma[noinline] private predicate parameterFlow( - ParameterNode 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(ParameterNode 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 @@ -1245,8 +1238,7 @@ private predicate flowOutOfCallNodeCand2( pragma[nomagic] private predicate flowIntoCallNodeCand2( - DataFlowCall call, ArgumentNode node1, ParameterNode 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 @@ -1260,8 +1252,8 @@ private module LocalFlowBigStep { */ private class FlowCheckNode extends Node { FlowCheckNode() { - this instanceof CastNode or - clearsContent(this, _) + castNode(this) or + clearsContentCached(this, _) } } @@ -1275,7 +1267,7 @@ private module LocalFlowBigStep { config.isSource(node) or jumpStep(_, node, config) or additionalJumpStep(_, node, config) or - node instanceof ParameterNode or + node instanceof ParamNode or node instanceof OutNodeExt or store(_, _, node, _) or read(_, _, node) or @@ -1321,21 +1313,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 +1342,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 +1376,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 +1435,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 +1577,10 @@ private module Stage3 { pragma[nomagic] private predicate fwdFlowIn( - DataFlowCall call, ParameterNode p, Cc outercc, Cc innercc, ApOption argAp, Ap ap, + DataFlowCall call, ParamNode p, Cc outercc, Cc innercc, ApOption argAp, Ap ap, Configuration config ) { - exists(ArgumentNode 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) @@ -1631,7 +1625,7 @@ private module Stage3 { private predicate fwdFlowIsEntered( DataFlowCall call, Cc cc, ApOption argAp, Ap ap, Configuration config ) { - exists(ParameterNode p | + exists(ParamNode p | fwdFlowIn(call, p, cc, _, argAp, ap, config) and PrevStage::parameterMayFlowThrough(p, _, unbindApa(getApprox(ap)), config) ) @@ -1772,10 +1766,9 @@ private module Stage3 { pragma[nomagic] private predicate revFlowIn( - DataFlowCall call, ArgumentNode arg, boolean toReturn, ApOption returnAp, Ap ap, - Configuration config + DataFlowCall call, ArgNode arg, boolean toReturn, ApOption returnAp, Ap ap, Configuration config ) { - exists(ParameterNode p, boolean allowsFieldFlow | + exists(ParamNode p, boolean allowsFieldFlow | revFlow(p, toReturn, returnAp, ap, config) and flowIntoCall(call, arg, p, allowsFieldFlow, config) | @@ -1785,7 +1778,7 @@ private module Stage3 { pragma[nomagic] private predicate revFlowInToReturn( - DataFlowCall call, ArgumentNode 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) } @@ -1838,13 +1831,13 @@ private module Stage3 { pragma[noinline] private predicate parameterFlow( - ParameterNode 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(ParameterNode 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 @@ -2088,7 +2081,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,8 +2148,7 @@ private module Stage4 { pragma[nomagic] private predicate flowIntoCall( - DataFlowCall call, ArgumentNode node1, ParameterNode 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 @@ -2300,10 +2292,10 @@ private module Stage4 { pragma[nomagic] private predicate fwdFlowIn( - DataFlowCall call, ParameterNode p, Cc outercc, Cc innercc, ApOption argAp, Ap ap, + DataFlowCall call, ParamNode p, Cc outercc, Cc innercc, ApOption argAp, Ap ap, Configuration config ) { - exists(ArgumentNode 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) @@ -2348,7 +2340,7 @@ private module Stage4 { private predicate fwdFlowIsEntered( DataFlowCall call, Cc cc, ApOption argAp, Ap ap, Configuration config ) { - exists(ParameterNode p | + exists(ParamNode p | fwdFlowIn(call, p, cc, _, argAp, ap, config) and PrevStage::parameterMayFlowThrough(p, _, unbindApa(getApprox(ap)), config) ) @@ -2489,10 +2481,9 @@ private module Stage4 { pragma[nomagic] private predicate revFlowIn( - DataFlowCall call, ArgumentNode arg, boolean toReturn, ApOption returnAp, Ap ap, - Configuration config + DataFlowCall call, ArgNode arg, boolean toReturn, ApOption returnAp, Ap ap, Configuration config ) { - exists(ParameterNode p, boolean allowsFieldFlow | + exists(ParamNode p, boolean allowsFieldFlow | revFlow(p, toReturn, returnAp, ap, config) and flowIntoCall(call, arg, p, allowsFieldFlow, config) | @@ -2502,7 +2493,7 @@ private module Stage4 { pragma[nomagic] private predicate revFlowInToReturn( - DataFlowCall call, ArgumentNode 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) } @@ -2555,13 +2546,13 @@ private module Stage4 { pragma[noinline] private predicate parameterFlow( - ParameterNode 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(ParameterNode 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 @@ -2606,7 +2597,7 @@ private predicate nodeMayUseSummary(Node n, AccessPathApprox apa, Configuration private newtype TSummaryCtx = TSummaryCtxNone() or - TSummaryCtxSome(ParameterNode p, AccessPath ap) { + TSummaryCtxSome(ParamNode p, AccessPath ap) { Stage4::parameterMayFlowThrough(p, _, ap.getApprox(), _) } @@ -2627,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 ParameterNode p; + private ParamNode p; private AccessPath ap; SummaryCtxSome() { this = TSummaryCtxSome(p, ap) } @@ -2758,7 +2749,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 +2970,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 +3139,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 +3226,7 @@ pragma[noinline] private predicate pathIntoArg( PathNodeMid mid, int i, CallContext cc, DataFlowCall call, AccessPath ap, AccessPathApprox apa ) { - exists(ArgumentNode arg | + exists(ArgNode arg | arg = mid.getNode() and cc = mid.getCallContext() and arg.argumentOf(call, i) and @@ -3248,7 +3239,7 @@ pragma[noinline] private predicate parameterCand( DataFlowCallable callable, int i, AccessPathApprox apa, Configuration config ) { - exists(ParameterNode p | + exists(ParamNode p | Stage4::revFlow(p, _, _, apa, config) and p.isParameterOf(callable, i) ) @@ -3272,7 +3263,7 @@ private predicate pathIntoCallable0( * respectively. */ private predicate pathIntoCallable( - PathNodeMid mid, ParameterNode 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 | @@ -3568,7 +3559,7 @@ private module FlowExploration { private newtype TSummaryCtx1 = TSummaryCtx1None() or - TSummaryCtx1Param(ParameterNode p) + TSummaryCtx1Param(ParamNode p) private newtype TSummaryCtx2 = TSummaryCtx2None() or @@ -3591,7 +3582,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 +3602,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 +3616,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 +3774,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 +3788,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 +3804,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 +3818,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 +3915,7 @@ private module FlowExploration { PartialPathNodeFwd mid, int i, CallContext cc, DataFlowCall call, PartialAccessPath ap, Configuration config ) { - exists(ArgumentNode arg | + exists(ArgNode arg | arg = mid.getNode() and cc = mid.getCallContext() and arg.argumentOf(call, i) and @@ -3943,7 +3934,7 @@ private module FlowExploration { } private predicate partialPathIntoCallable( - PartialPathNodeFwd mid, ParameterNode p, CallContext outercc, CallContextCall innercc, + PartialPathNodeFwd mid, ParamNode p, CallContext outercc, CallContextCall innercc, TSummaryCtx1 sc1, TSummaryCtx2 sc2, DataFlowCall call, PartialAccessPath ap, Configuration config ) { @@ -3980,7 +3971,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(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) ) @@ -4037,7 +4028,7 @@ private module FlowExploration { apConsRev(ap, c, ap0, config) ) or - exists(ParameterNode p | + exists(ParamNode p | mid.getNode() = p and viableParamArg(_, p, node) and sc1 = mid.getSummaryCtx1() and @@ -4115,7 +4106,7 @@ private module FlowExploration { int pos, TRevSummaryCtx1Some sc1, TRevSummaryCtx2Some sc2, RevPartialAccessPath ap, Configuration config ) { - exists(PartialPathNodeRev mid, ParameterNode p | + exists(PartialPathNodeRev mid, ParamNode p | mid.getNode() = p and p.isParameterOf(_, pos) and sc1 = mid.getSummaryCtx1() and @@ -4138,7 +4129,7 @@ private module FlowExploration { pragma[nomagic] private predicate revPartialPathThroughCallable( - PartialPathNodeRev mid, ArgumentNode 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 9498e51e7e6..058d66b1496 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(ArgNode arg | fwdFlow(arg, cc, config) and viableParamArg(call, _, arg) ) @@ -515,24 +512,22 @@ private module Stage1 { pragma[nomagic] predicate viableParamArgNodeCandFwd1( - DataFlowCall call, ParameterNode p, ArgumentNode 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, ArgumentNode arg, boolean toReturn, Configuration config - ) { - exists(ParameterNode 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, ArgumentNode arg, Configuration config) { + private predicate revFlowInToReturn(DataFlowCall call, ArgNode arg, Configuration config) { revFlowIn(call, arg, true, config) } @@ -597,7 +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(ParameterNode 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 @@ -663,7 +658,7 @@ private predicate flowOutOfCallNodeCand1( pragma[nomagic] private predicate viableParamArgNodeCand1( - DataFlowCall call, ParameterNode p, ArgumentNode arg, Configuration config + DataFlowCall call, ParamNode p, ArgNode arg, Configuration config ) { Stage1::viableParamArgNodeCandFwd1(call, p, arg, config) and Stage1::revFlow(arg, config) @@ -675,7 +670,7 @@ private predicate viableParamArgNodeCand1( */ pragma[nomagic] private predicate flowIntoCallNodeCand1( - DataFlowCall call, ArgumentNode arg, ParameterNode p, Configuration config + DataFlowCall call, ArgNode arg, ParamNode p, Configuration config ) { viableParamArgNodeCand1(call, p, arg, config) and Stage1::revFlow(p, config) and @@ -735,8 +730,7 @@ private predicate flowOutOfCallNodeCand1( */ pragma[nomagic] private predicate flowIntoCallNodeCand1( - DataFlowCall call, ArgumentNode arg, ParameterNode 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 | @@ -944,10 +938,10 @@ private module Stage2 { pragma[nomagic] private predicate fwdFlowIn( - DataFlowCall call, ParameterNode p, Cc outercc, Cc innercc, ApOption argAp, Ap ap, + DataFlowCall call, ParamNode p, Cc outercc, Cc innercc, ApOption argAp, Ap ap, Configuration config ) { - exists(ArgumentNode 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) @@ -992,7 +986,7 @@ private module Stage2 { private predicate fwdFlowIsEntered( DataFlowCall call, Cc cc, ApOption argAp, Ap ap, Configuration config ) { - exists(ParameterNode p | + exists(ParamNode p | fwdFlowIn(call, p, cc, _, argAp, ap, config) and PrevStage::parameterMayFlowThrough(p, _, getApprox(ap), config) ) @@ -1133,10 +1127,9 @@ private module Stage2 { pragma[nomagic] private predicate revFlowIn( - DataFlowCall call, ArgumentNode arg, boolean toReturn, ApOption returnAp, Ap ap, - Configuration config + DataFlowCall call, ArgNode arg, boolean toReturn, ApOption returnAp, Ap ap, Configuration config ) { - exists(ParameterNode p, boolean allowsFieldFlow | + exists(ParamNode p, boolean allowsFieldFlow | revFlow(p, toReturn, returnAp, ap, config) and flowIntoCall(call, arg, p, allowsFieldFlow, config) | @@ -1146,7 +1139,7 @@ private module Stage2 { pragma[nomagic] private predicate revFlowInToReturn( - DataFlowCall call, ArgumentNode 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) } @@ -1199,13 +1192,13 @@ private module Stage2 { pragma[noinline] private predicate parameterFlow( - ParameterNode 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(ParameterNode 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 @@ -1245,8 +1238,7 @@ private predicate flowOutOfCallNodeCand2( pragma[nomagic] private predicate flowIntoCallNodeCand2( - DataFlowCall call, ArgumentNode node1, ParameterNode 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 @@ -1260,8 +1252,8 @@ private module LocalFlowBigStep { */ private class FlowCheckNode extends Node { FlowCheckNode() { - this instanceof CastNode or - clearsContent(this, _) + castNode(this) or + clearsContentCached(this, _) } } @@ -1275,7 +1267,7 @@ private module LocalFlowBigStep { config.isSource(node) or jumpStep(_, node, config) or additionalJumpStep(_, node, config) or - node instanceof ParameterNode or + node instanceof ParamNode or node instanceof OutNodeExt or store(_, _, node, _) or read(_, _, node) or @@ -1321,21 +1313,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 +1342,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 +1376,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 +1435,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 +1577,10 @@ private module Stage3 { pragma[nomagic] private predicate fwdFlowIn( - DataFlowCall call, ParameterNode p, Cc outercc, Cc innercc, ApOption argAp, Ap ap, + DataFlowCall call, ParamNode p, Cc outercc, Cc innercc, ApOption argAp, Ap ap, Configuration config ) { - exists(ArgumentNode 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) @@ -1631,7 +1625,7 @@ private module Stage3 { private predicate fwdFlowIsEntered( DataFlowCall call, Cc cc, ApOption argAp, Ap ap, Configuration config ) { - exists(ParameterNode p | + exists(ParamNode p | fwdFlowIn(call, p, cc, _, argAp, ap, config) and PrevStage::parameterMayFlowThrough(p, _, unbindApa(getApprox(ap)), config) ) @@ -1772,10 +1766,9 @@ private module Stage3 { pragma[nomagic] private predicate revFlowIn( - DataFlowCall call, ArgumentNode arg, boolean toReturn, ApOption returnAp, Ap ap, - Configuration config + DataFlowCall call, ArgNode arg, boolean toReturn, ApOption returnAp, Ap ap, Configuration config ) { - exists(ParameterNode p, boolean allowsFieldFlow | + exists(ParamNode p, boolean allowsFieldFlow | revFlow(p, toReturn, returnAp, ap, config) and flowIntoCall(call, arg, p, allowsFieldFlow, config) | @@ -1785,7 +1778,7 @@ private module Stage3 { pragma[nomagic] private predicate revFlowInToReturn( - DataFlowCall call, ArgumentNode 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) } @@ -1838,13 +1831,13 @@ private module Stage3 { pragma[noinline] private predicate parameterFlow( - ParameterNode 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(ParameterNode 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 @@ -2088,7 +2081,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,8 +2148,7 @@ private module Stage4 { pragma[nomagic] private predicate flowIntoCall( - DataFlowCall call, ArgumentNode node1, ParameterNode 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 @@ -2300,10 +2292,10 @@ private module Stage4 { pragma[nomagic] private predicate fwdFlowIn( - DataFlowCall call, ParameterNode p, Cc outercc, Cc innercc, ApOption argAp, Ap ap, + DataFlowCall call, ParamNode p, Cc outercc, Cc innercc, ApOption argAp, Ap ap, Configuration config ) { - exists(ArgumentNode 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) @@ -2348,7 +2340,7 @@ private module Stage4 { private predicate fwdFlowIsEntered( DataFlowCall call, Cc cc, ApOption argAp, Ap ap, Configuration config ) { - exists(ParameterNode p | + exists(ParamNode p | fwdFlowIn(call, p, cc, _, argAp, ap, config) and PrevStage::parameterMayFlowThrough(p, _, unbindApa(getApprox(ap)), config) ) @@ -2489,10 +2481,9 @@ private module Stage4 { pragma[nomagic] private predicate revFlowIn( - DataFlowCall call, ArgumentNode arg, boolean toReturn, ApOption returnAp, Ap ap, - Configuration config + DataFlowCall call, ArgNode arg, boolean toReturn, ApOption returnAp, Ap ap, Configuration config ) { - exists(ParameterNode p, boolean allowsFieldFlow | + exists(ParamNode p, boolean allowsFieldFlow | revFlow(p, toReturn, returnAp, ap, config) and flowIntoCall(call, arg, p, allowsFieldFlow, config) | @@ -2502,7 +2493,7 @@ private module Stage4 { pragma[nomagic] private predicate revFlowInToReturn( - DataFlowCall call, ArgumentNode 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) } @@ -2555,13 +2546,13 @@ private module Stage4 { pragma[noinline] private predicate parameterFlow( - ParameterNode 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(ParameterNode 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 @@ -2606,7 +2597,7 @@ private predicate nodeMayUseSummary(Node n, AccessPathApprox apa, Configuration private newtype TSummaryCtx = TSummaryCtxNone() or - TSummaryCtxSome(ParameterNode p, AccessPath ap) { + TSummaryCtxSome(ParamNode p, AccessPath ap) { Stage4::parameterMayFlowThrough(p, _, ap.getApprox(), _) } @@ -2627,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 ParameterNode p; + private ParamNode p; private AccessPath ap; SummaryCtxSome() { this = TSummaryCtxSome(p, ap) } @@ -2758,7 +2749,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 +2970,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 +3139,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 +3226,7 @@ pragma[noinline] private predicate pathIntoArg( PathNodeMid mid, int i, CallContext cc, DataFlowCall call, AccessPath ap, AccessPathApprox apa ) { - exists(ArgumentNode arg | + exists(ArgNode arg | arg = mid.getNode() and cc = mid.getCallContext() and arg.argumentOf(call, i) and @@ -3248,7 +3239,7 @@ pragma[noinline] private predicate parameterCand( DataFlowCallable callable, int i, AccessPathApprox apa, Configuration config ) { - exists(ParameterNode p | + exists(ParamNode p | Stage4::revFlow(p, _, _, apa, config) and p.isParameterOf(callable, i) ) @@ -3272,7 +3263,7 @@ private predicate pathIntoCallable0( * respectively. */ private predicate pathIntoCallable( - PathNodeMid mid, ParameterNode 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 | @@ -3568,7 +3559,7 @@ private module FlowExploration { private newtype TSummaryCtx1 = TSummaryCtx1None() or - TSummaryCtx1Param(ParameterNode p) + TSummaryCtx1Param(ParamNode p) private newtype TSummaryCtx2 = TSummaryCtx2None() or @@ -3591,7 +3582,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 +3602,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 +3616,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 +3774,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 +3788,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 +3804,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 +3818,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 +3915,7 @@ private module FlowExploration { PartialPathNodeFwd mid, int i, CallContext cc, DataFlowCall call, PartialAccessPath ap, Configuration config ) { - exists(ArgumentNode arg | + exists(ArgNode arg | arg = mid.getNode() and cc = mid.getCallContext() and arg.argumentOf(call, i) and @@ -3943,7 +3934,7 @@ private module FlowExploration { } private predicate partialPathIntoCallable( - PartialPathNodeFwd mid, ParameterNode p, CallContext outercc, CallContextCall innercc, + PartialPathNodeFwd mid, ParamNode p, CallContext outercc, CallContextCall innercc, TSummaryCtx1 sc1, TSummaryCtx2 sc2, DataFlowCall call, PartialAccessPath ap, Configuration config ) { @@ -3980,7 +3971,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(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) ) @@ -4037,7 +4028,7 @@ private module FlowExploration { apConsRev(ap, c, ap0, config) ) or - exists(ParameterNode p | + exists(ParamNode p | mid.getNode() = p and viableParamArg(_, p, node) and sc1 = mid.getSummaryCtx1() and @@ -4115,7 +4106,7 @@ private module FlowExploration { int pos, TRevSummaryCtx1Some sc1, TRevSummaryCtx2Some sc2, RevPartialAccessPath ap, Configuration config ) { - exists(PartialPathNodeRev mid, ParameterNode p | + exists(PartialPathNodeRev mid, ParamNode p | mid.getNode() = p and p.isParameterOf(_, pos) and sc1 = mid.getSummaryCtx1() and @@ -4138,7 +4129,7 @@ private module FlowExploration { pragma[nomagic] private predicate revPartialPathThroughCallable( - PartialPathNodeRev mid, ArgumentNode 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 966c30038cc..462e89ac9ed 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,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, ParameterNode p) { + private predicate viableParamNonLambda(DataFlowCall call, int i, ParamNode p) { p.isParameterOf(viableCallable(call), i) } - private predicate viableParamLambda(DataFlowCall call, int i, ParameterNode p) { + private predicate viableParamLambda(DataFlowCall call, int i, ParamNode p) { p.isParameterOf(viableCallableLambda(call, _), i) } - private predicate viableParamArgNonLambda(DataFlowCall call, ParameterNode p, ArgumentNode 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, ParameterNode p, ArgumentNode arg) { + private predicate viableParamArgLambda(DataFlowCall call, ParamNode p, ArgNode arg) { exists(int i | viableParamLambda(call, i, p) and arg.argumentOf(call, i) @@ -118,8 +118,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 ArgNode or node instanceof ReturnNode + 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 @@ -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 | @@ -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 @@ -176,7 +176,7 @@ private module LambdaFlow { ) or // flow into a callable - exists(ParameterNode 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 @@ -227,7 +227,7 @@ private module LambdaFlow { pragma[nomagic] predicate revLambdaFlowIn( - DataFlowCall lambdaCall, LambdaCallKind kind, ParameterNode p, DataFlowType t, boolean toJump, + DataFlowCall lambdaCall, LambdaCallKind kind, ParamNode p, DataFlowType t, boolean toJump, DataFlowCallOption lastCall ) { revLambdaFlow(lambdaCall, kind, p, t, false, toJump, lastCall) @@ -242,6 +242,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 ArgNode + } + + cached + predicate hiddenNode(Node n) { nodeIsHidden(n) } + + cached + OutNodeExt getAnOutNodeExt(DataFlowCall call, ReturnKindExt k) { + result = getAnOutNode(call, k.(ValueReturnKind).getKind()) + or + exists(ArgNode 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(ParamNode 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 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 + // 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 +344,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, ParamNode p) { p.isParameterOf(viableCallableExt(call), i) } @@ -270,11 +353,11 @@ private module Cached { * dispatch into account. */ cached - predicate viableParamArg(DataFlowCall call, ParameterNode p, ArgumentNode arg) { + predicate viableParamArg(DataFlowCall call, ParamNode p, ArgNode 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 +395,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(ParamNode p, Node node, boolean read) { p = node and read = false or @@ -325,30 +408,30 @@ 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(ArgNode arg | parameterValueFlowArgCand(p, arg, false) and argumentValueFlowsThroughCand(arg, node, read) ) or // flow through: no read inside method - exists(ArgumentNode arg | + exists(ArgNode arg | parameterValueFlowArgCand(p, arg, read) and argumentValueFlowsThroughCand(arg, node, false) ) } pragma[nomagic] - private predicate parameterValueFlowArgCand(ParameterNode p, ArgumentNode arg, boolean read) { + private predicate parameterValueFlowArgCand(ParamNode p, ArgNode arg, boolean read) { parameterValueFlowCand(p, arg, read) } pragma[nomagic] - predicate parameterValueFlowsToPreUpdateCand(ParameterNode p, PostUpdateNode n) { + predicate parameterValueFlowsToPreUpdateCand(ParamNode p, PostUpdateNode n) { parameterValueFlowCand(p, n.getPreUpdateNode(), false) } @@ -360,7 +443,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(ParamNode p, ReturnKind kind, boolean read) { exists(ReturnNode ret | parameterValueFlowCand(p, ret, read) and kind = ret.getKind() @@ -369,9 +452,9 @@ private module Cached { pragma[nomagic] private predicate argumentValueFlowsThroughCand0( - DataFlowCall call, ArgumentNode arg, ReturnKind kind, boolean read + DataFlowCall call, ArgNode arg, ReturnKind kind, boolean read ) { - exists(ParameterNode param | viableParamArg(call, param, arg) | + exists(ParamNode param | viableParamArg(call, param, arg) | parameterValueFlowReturnCand(param, kind, read) ) } @@ -382,14 +465,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(ArgNode 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(ParamNode p, Node n) { parameterValueFlowCand(p, n, _) and ( parameterValueFlowReturnCand(p, _, _) @@ -416,21 +499,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(ParamNode 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(ParamNode p, Node node, ReadStepTypesOption read) { p = node and Cand::cand(p, _) and read = TReadStepTypesNone() @@ -447,7 +530,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,34 +538,32 @@ private module Cached { pragma[nomagic] private predicate parameterValueFlow0_0( - ReadStepTypesOption mustBeNone, ParameterNode p, Node node, ReadStepTypesOption read + ReadStepTypesOption mustBeNone, ParamNode p, Node node, ReadStepTypesOption read ) { // flow through: no prior read - exists(ArgumentNode arg | + exists(ArgNode arg | parameterValueFlowArg(p, arg, mustBeNone) and argumentValueFlowsThrough(arg, read, node) ) or // flow through: no read inside method - exists(ArgumentNode arg | + exists(ArgNode arg | parameterValueFlowArg(p, arg, read) and argumentValueFlowsThrough(arg, mustBeNone, node) ) } pragma[nomagic] - private predicate parameterValueFlowArg( - ParameterNode p, ArgumentNode 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, ArgumentNode arg, ReturnKind kind, ReadStepTypesOption read + DataFlowCall call, ArgNode arg, ReturnKind kind, ReadStepTypesOption read ) { - exists(ParameterNode param | viableParamArg(call, param, arg) | + exists(ParamNode param | viableParamArg(call, param, arg) | parameterValueFlowReturn(param, kind, read) ) } @@ -496,18 +577,18 @@ private module Cached { * container type, and the content type. */ pragma[nomagic] - predicate argumentValueFlowsThrough(ArgumentNode 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) | // 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 +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(ArgumentNode arg, Content c, Node out) { + predicate getterStep(ArgNode arg, Content c, Node out) { argumentValueFlowsThrough(arg, TReadStepTypesSome(_, c, _), out) } @@ -529,7 +610,7 @@ private module Cached { * container type, and the content type. */ private predicate parameterValueFlowReturn( - ParameterNode p, ReturnKind kind, ReadStepTypesOption read + ParamNode p, ReturnKind kind, ReadStepTypesOption read ) { exists(ReturnNode ret | parameterValueFlow(p, ret, read) and @@ -553,7 +634,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 +692,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 +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. */ - cached - predicate parameterValueFlowsToPreUpdate(ParameterNode p, PostUpdateNode n) { + private predicate parameterValueFlowsToPreUpdate(ParamNode p, PostUpdateNode n) { parameterValueFlow(p, n.getPreUpdateNode(), TReadStepTypesNone()) } @@ -644,9 +724,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 +734,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 +761,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 +772,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.(ArgNode).argumentOf(c, _) and + simpleLocalFlowStep(toPre.(ArgNode), 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 +794,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 +816,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(ParamNode p | p.isParameterOf(_, pos)) } cached newtype TBooleanOption = @@ -761,23 +851,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 +936,7 @@ class CallContextSomeCall extends CallContextCall, TSomeCall { override string toString() { result = "CcSomeCall" } override predicate relevantFor(DataFlowCallable callable) { - exists(ParameterNode p | getNodeEnclosingCallable(p) = callable) + exists(ParamNode p | getNodeEnclosingCallable(p) = callable) } override predicate matchesCall(DataFlowCall call) { any() } @@ -866,7 +948,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 +981,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 +995,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 ParamNode extends Node { + ParamNode() { 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 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) } +} + /** * 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 +1033,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 +1046,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 +1057,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 +1067,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 +1093,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 +1123,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 +1144,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 +1195,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/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 2e22b7e1963..e3ab07fce1f 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 diff --git a/java/ql/src/semmle/code/java/frameworks/Kryo.qll b/java/ql/src/semmle/code/java/frameworks/Kryo.qll index 049be97ea9c..317148d56b5 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 in 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/frameworks/jackson/JacksonSerializability.qll b/java/ql/src/semmle/code/java/frameworks/jackson/JacksonSerializability.qll index 3356e31d965..6cda84512a0 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. @@ -28,7 +29,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,8 +51,20 @@ library class JacksonWriteValueMethod extends Method, TaintPreservingCallable { } } +private class JacksonReadValueMethod extends Method, TaintPreservingCallable { + JacksonReadValueMethod() { + ( + getDeclaringType().hasQualifiedName("com.fasterxml.jackson.databind", "ObjectReader") or + getDeclaringType().hasQualifiedName("com.fasterxml.jackson.databind", "ObjectMapper") + ) and + 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... @@ -63,7 +76,7 @@ library class ExplicitlyWrittenJacksonSerializableType extends JacksonSerializab } /** 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)) } @@ -96,17 +109,24 @@ 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) ) + 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) + ) } } /** A type used in a `JacksonDeserializableField` declaration. */ -library class FieldReferencedJacksonDeSerializableType extends JacksonDeserializableType { - FieldReferencedJacksonDeSerializableType() { +private class FieldReferencedJacksonDeserializableType extends JacksonDeserializableType { + FieldReferencedJacksonDeserializableType() { exists(JacksonDeserializableField f | usesType(f.getType(), this)) } } @@ -135,6 +155,21 @@ class JacksonDeserializableField extends DeserializableField { } } +/** A call to a field that may be deserialized using the Jackson JSON framework. */ +private 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. + */ +private class JacksonDeserializedTaintStep extends AdditionalTaintStep { + override predicate step(DataFlow::Node node1, DataFlow::Node node2) { + DataFlow::getFieldQualifier(node2.asExpr().(JacksonDeserializableFieldAccess)) = node1 + } +} + /** * A call to the `addMixInAnnotations` or `addMixIn` Jackson method. * @@ -239,3 +274,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/src/semmle/code/java/security/UnsafeDeserialization.qll b/java/ql/src/semmle/code/java/security/UnsafeDeserialization.qll index ab809f07d6d..a1561c0e6cd 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 functional 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/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") + ) } } 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..f9b29fec6cc --- /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 execution of arbitrary Python code + 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 execution of arbitrary Python code + 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/RhinoServlet.java b/java/ql/test/experimental/query-tests/security/CWE-094/RhinoServlet.java new file mode 100644 index 00000000000..e76a9543f87 --- /dev/null +++ b/java/ql/test/experimental/query-tests/security/CWE-094/RhinoServlet.java @@ -0,0 +1,91 @@ +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.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 + */ +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) { + return className.startsWith("com.example."); + } + }); + + 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(); + } + } + + // 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/ScriptEngine.expected b/java/ql/test/experimental/query-tests/security/CWE-094/ScriptEngine.expected deleted file mode 100644 index a65faafc6bd..00000000000 --- a/java/ql/test/experimental/query-tests/security/CWE-094/ScriptEngine.expected +++ /dev/null @@ -1,32 +0,0 @@ -edges -| 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 | -nodes -| 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: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 | -#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 | 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/ScriptEngineTest.java b/java/ql/test/experimental/query-tests/security/CWE-094/ScriptEngineTest.java index e04ec615f30..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(); @@ -33,26 +45,59 @@ public class ScriptEngineTest { MyCustomScriptEngine engine = (MyCustomScriptEngine) factory.getScriptEngine(new String[] { "-scripting" }); Object result = engine.eval(input); } - - 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]); + + 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); + } + 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; } + } + + 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 new file mode 100644 index 00000000000..5f1d250e9a2 --- /dev/null +++ b/java/ql/test/experimental/query-tests/security/CWE-094/ScriptInjection.expected @@ -0,0 +1,58 @@ +edges +| RhinoServlet.java:28:23:28:50 | getParameter(...) : String | RhinoServlet.java:32:55:32:58 | code | +| 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: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: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 | 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 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..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 +//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/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..bcf5e892e1b --- /dev/null +++ b/java/ql/test/experimental/query-tests/security/CWE-601/SpringUrlRedirect.expected @@ -0,0 +1,35 @@ +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 | +| 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 | +| 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 | +| 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 | +| 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 | +| 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 | +| 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 new file mode 100644 index 00000000000..f3958cba102 --- /dev/null +++ b/java/ql/test/experimental/query-tests/security/CWE-601/SpringUrlRedirect.java @@ -0,0 +1,83 @@ +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 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 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)){ + rv.setUrl(redirectUrl); + }else { + rv.setUrl(VALID_REDIRECT); + } + return rv; + } + + @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); + } +} 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/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/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 new file mode 100644 index 00000000000..1bda212cfa4 --- /dev/null +++ b/java/ql/test/experimental/stubs/rhino-1.7.13/org/mozilla/javascript/Context.java @@ -0,0 +1,695 @@ +/* -*- 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; + } + + /** + * @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. + *

    + * 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/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/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/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/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 +{ +} 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/library-tests/dataflow/taint-jackson/Test.java b/java/ql/test/library-tests/dataflow/taint-jackson/Test.java index 2caf9e4ee80..3be85336e26 100644 --- a/java/ql/test/library-tests/dataflow/taint-jackson/Test.java +++ b/java/ql/test/library-tests/dataflow/taint-jackson/Test.java @@ -3,33 +3,53 @@ import java.io.FileOutputStream; 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; 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 +57,56 @@ 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 + } + + 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 + } + } + + 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/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..0836906530b 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 = "" + ) + } +} 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/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..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 @@ -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/MappingIterator.java b/java/ql/test/stubs/jackson-databind-2.10/com/fasterxml/jackson/databind/MappingIterator.java new file mode 100644 index 00000000000..ac427ef01c9 --- /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 { + + } +} 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..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 @@ -26,4 +26,16 @@ public class ObjectMapper { public String writeValueAsString(Object value) { return null; } + + 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; + } } 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..f067a3e95a4 --- /dev/null +++ b/java/ql/test/stubs/jackson-databind-2.10/com/fasterxml/jackson/databind/ObjectReader.java @@ -0,0 +1,82 @@ +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; + } + + 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; + } + +} 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/jython-2.7.2/org/python/core/PyCode.java b/java/ql/test/stubs/jython-2.7.2/org/python/core/PyCode.java new file mode 100644 index 00000000000..9b7c99f94fa --- /dev/null +++ b/java/ql/test/stubs/jython-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/jython-2.7.2/org/python/core/PyException.java b/java/ql/test/stubs/jython-2.7.2/org/python/core/PyException.java new file mode 100644 index 00000000000..3a0a6c52c69 --- /dev/null +++ b/java/ql/test/stubs/jython-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/jython-2.7.2/org/python/core/PyObject.java b/java/ql/test/stubs/jython-2.7.2/org/python/core/PyObject.java new file mode 100644 index 00000000000..00993123461 --- /dev/null +++ b/java/ql/test/stubs/jython-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/jython-2.7.2/org/python/core/PySystemState.java b/java/ql/test/stubs/jython-2.7.2/org/python/core/PySystemState.java new file mode 100644 index 00000000000..8444bbba70e --- /dev/null +++ b/java/ql/test/stubs/jython-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/jython-2.7.2/org/python/core/ThreadState.java b/java/ql/test/stubs/jython-2.7.2/org/python/core/ThreadState.java new file mode 100644 index 00000000000..920270fe053 --- /dev/null +++ b/java/ql/test/stubs/jython-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/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/jython-2.7.2/org/python/util/PythonInterpreter.java b/java/ql/test/stubs/jython-2.7.2/org/python/util/PythonInterpreter.java new file mode 100644 index 00000000000..92c50917b59 --- /dev/null +++ b/java/ql/test/stubs/jython-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() { + } +} 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; + } + } +} 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; } } - 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; + } +} + 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/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/javascript/ql/src/Summary/TaintSinks.ql b/javascript/ql/src/Summary/TaintSinks.ql new file mode 100644 index 00000000000..2da65398935 --- /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 info + * @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..78e544f0bd5 --- /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 info + * @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() 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 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 { 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/PrintAst.qll b/javascript/ql/src/semmle/javascript/PrintAst.qll index 0499e3bd547..029cfd536fc 100644 --- a/javascript/ql/src/semmle/javascript/PrintAst.qll +++ b/javascript/ql/src/semmle/javascript/PrintAst.qll @@ -73,7 +73,12 @@ 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) { + shouldPrint(term, _) and + term.isUsedAsRegExp() and + any(RegExpLiteral lit).getRoot() = term.getRootTerm() + } /** * A node in the output tree. @@ -282,6 +287,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/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/src/semmle/javascript/frameworks/ClientRequests.qll b/javascript/ql/src/semmle/javascript/frameworks/ClientRequests.qll index 451ad3f24d2..18204c5b59b 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"] 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. */ @@ -275,6 +268,10 @@ module ClientRequest { responseType = getResponseType() and promise = true and result = this + or + responseType = getResponseType() and + promise = false and + result = getReturn().getPromisedError().getMember("response").getAnImmediateUse() } } diff --git a/javascript/ql/src/semmle/javascript/security/internal/SensitiveDataHeuristics.qll b/javascript/ql/src/semmle/javascript/security/internal/SensitiveDataHeuristics.qll index ddf95b1b534..589c37120b9 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 = "(?s).*([uU]|^|_|[a-z](?=U))([uU][iI][dD]).*" } /** 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; +} 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] | diff --git a/javascript/ql/test/library-tests/frameworks/ClientRequests/ClientRequests.expected b/javascript/ql/test/library-tests/frameworks/ClientRequests/ClientRequests.expected index e448b888e12..27a9fa10f72 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,7 @@ 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: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 bc15565c072..40dcfc481f4 100644 --- a/javascript/ql/test/library-tests/frameworks/ClientRequests/tst.js +++ b/javascript/ql/test/library-tests/frameworks/ClientRequests/tst.js @@ -290,4 +290,18 @@ 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; + const agent = err.response.headers.useragent; + } + ); } \ No newline at end of file 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 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..42da210c266 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,32 @@ 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 | +| 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() | @@ -114,6 +140,24 @@ 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) | +| 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 | @@ -131,3 +175,9 @@ 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 | +| 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 123799426b5..77393b8983c 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,14 @@ 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 + 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/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 */ diff --git a/python/ql/src/experimental/Security/CWE-730/RegexInjection.qhelp b/python/ql/src/experimental/Security/CWE-730/RegexInjection.qhelp new file mode 100644 index 00000000000..f19f0744469 --- /dev/null +++ b/python/ql/src/experimental/Security/CWE-730/RegexInjection.qhelp @@ -0,0 +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. +

    + + + +

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

    + +
    + + +
  • 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/RegexInjection.ql b/python/ql/src/experimental/Security/CWE-730/RegexInjection.ql new file mode 100644 index 00000000000..7725f636eb0 --- /dev/null +++ b/python/ql/src/experimental/Security/CWE-730/RegexInjection.ql @@ -0,0 +1,29 @@ +/** + * @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 py/regex-injection + * @tags security + * external/cwe/cwe-730 + * external/cwe/cwe-400 + */ + +// determine precision above +import python +import experimental.semmle.python.security.injection.RegexInjection +import DataFlow::PathGraph + +from + RegexInjectionFlowConfig config, DataFlow::PathNode source, DataFlow::PathNode sink, + RegexInjectionSink regexInjectionSink, Attribute methodAttribute +where + config.hasFlowPath(source, sink) and + 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, + regexInjectionSink.getRegexModule() + "." + methodAttribute.getName() 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("") diff --git a/python/ql/src/experimental/semmle/python/Concepts.qll b/python/ql/src/experimental/semmle/python/Concepts.qll index 904b7967ee8..0661ebc5890 100644 --- a/python/ql/src/experimental/semmle/python/Concepts.qll +++ b/python/ql/src/experimental/semmle/python/Concepts.qll @@ -13,3 +13,70 @@ 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 executes a regular expression. + * + * Extend this class to model new APIs. If you want to refine existing API models, + * 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(); + } +} + +/** + * 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. + */ +class RegexExecution extends DataFlow::Node { + RegexExecution::Range range; + + RegexExecution() { this = range } + + DataFlow::Node getRegexNode() { result = range.getRegexNode() } + + string getRegexModule() { result = range.getRegexModule() } +} + +/** Provides classes for modeling Regular Expression escape-related APIs. */ +module RegexEscape { + /** + * 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. + */ + abstract class Range extends DataFlow::Node { + /** + * Gets the argument containing the escaped expression. + */ + abstract DataFlow::Node getRegexNode(); + } +} + +/** + * 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. + */ +class RegexEscape extends DataFlow::Node { + RegexEscape::Range range; + + RegexEscape() { this = range } + + DataFlow::Node getRegexNode() { result = range.getRegexNode() } +} diff --git a/python/ql/src/experimental/semmle/python/frameworks/Stdlib.qll b/python/ql/src/experimental/semmle/python/frameworks/Stdlib.qll index 420caf0d73b..4f3457e0a99 100644 --- a/python/ql/src/experimental/semmle/python/frameworks/Stdlib.qll +++ b/python/ql/src/experimental/semmle/python/frameworks/Stdlib.qll @@ -9,3 +9,92 @@ 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 Python's `re` library. + * + * See https://docs.python.org/3/library/re.html + */ +private module Re { + /** + * 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(RegexExecutionMethods m)).getACall() and + regexNode = this.getArg(0) + } + + override DataFlow::Node getRegexNode() { result = regexNode } + + override string getRegexModule() { result = "re" } + } + + /** + * A class to find `re` methods immediately executing a compiled expression by `re.compile`. + * + * Given the following example: + * + * ```py + * pattern = re.compile(input) + * pattern.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` + * + * See https://docs.python.org/3/library/re.html#regular-expression-objects + */ + 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.flowsTo(reMethod.getObject()) and + reMethod.getAttributeName() instanceof RegexExecutionMethods and + regexNode = patternCall.getArg(0) + ) + } + + override DataFlow::Node getRegexNode() { result = regexNode } + + 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; + + ReEscape() { + this = API::moduleImport("re").getMember("escape").getACall() and + regexNode = this.getArg(0) + } + + override DataFlow::Node getRegexNode() { result = regexNode } + } +} diff --git a/python/ql/src/experimental/semmle/python/security/injection/RegexInjection.qll b/python/ql/src/experimental/semmle/python/security/injection/RegexInjection.qll new file mode 100644 index 00000000000..7b7b08cacab --- /dev/null +++ b/python/ql/src/experimental/semmle/python/security/injection/RegexInjection.qll @@ -0,0 +1,53 @@ +/** + * Provides a taint-tracking configuration for detecting regular expression injection + * vulnerabilities. + */ + +import python +import experimental.semmle.python.Concepts +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; + + RegexInjectionSink() { + exists(RegexExecution reExec | + this = reExec.getRegexNode() and + regexModule = reExec.getRegexModule() and + regexMethod = reExec.(DataFlow::CallCfgNode).getFunction().asExpr().(Attribute) + ) + } + + /** + * Gets the argument containing the executed expression. + */ + string getRegexModule() { result = regexModule } + + /** + * Gets the method used to execute the regular expression. + */ + Attribute getRegexMethod() { result = regexMethod } +} + +/** + * 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 RegexInjectionSink } + + override predicate isSanitizer(DataFlow::Node sanitizer) { + sanitizer = any(RegexEscape reEscape).getRegexNode() + } +} 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..058d66b1496 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(ArgNode arg | fwdFlow(arg, cc, config) and viableParamArg(call, _, arg) ) @@ -515,24 +512,22 @@ private module Stage1 { pragma[nomagic] predicate viableParamArgNodeCandFwd1( - DataFlowCall call, ParameterNode p, ArgumentNode 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, ArgumentNode arg, boolean toReturn, Configuration config - ) { - exists(ParameterNode 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, ArgumentNode arg, Configuration config) { + private predicate revFlowInToReturn(DataFlowCall call, ArgNode arg, Configuration config) { revFlowIn(call, arg, true, config) } @@ -597,7 +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(ParameterNode 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 @@ -663,7 +658,7 @@ private predicate flowOutOfCallNodeCand1( pragma[nomagic] private predicate viableParamArgNodeCand1( - DataFlowCall call, ParameterNode p, ArgumentNode arg, Configuration config + DataFlowCall call, ParamNode p, ArgNode arg, Configuration config ) { Stage1::viableParamArgNodeCandFwd1(call, p, arg, config) and Stage1::revFlow(arg, config) @@ -675,7 +670,7 @@ private predicate viableParamArgNodeCand1( */ pragma[nomagic] private predicate flowIntoCallNodeCand1( - DataFlowCall call, ArgumentNode arg, ParameterNode p, Configuration config + DataFlowCall call, ArgNode arg, ParamNode p, Configuration config ) { viableParamArgNodeCand1(call, p, arg, config) and Stage1::revFlow(p, config) and @@ -735,8 +730,7 @@ private predicate flowOutOfCallNodeCand1( */ pragma[nomagic] private predicate flowIntoCallNodeCand1( - DataFlowCall call, ArgumentNode arg, ParameterNode 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 | @@ -944,10 +938,10 @@ private module Stage2 { pragma[nomagic] private predicate fwdFlowIn( - DataFlowCall call, ParameterNode p, Cc outercc, Cc innercc, ApOption argAp, Ap ap, + DataFlowCall call, ParamNode p, Cc outercc, Cc innercc, ApOption argAp, Ap ap, Configuration config ) { - exists(ArgumentNode 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) @@ -992,7 +986,7 @@ private module Stage2 { private predicate fwdFlowIsEntered( DataFlowCall call, Cc cc, ApOption argAp, Ap ap, Configuration config ) { - exists(ParameterNode p | + exists(ParamNode p | fwdFlowIn(call, p, cc, _, argAp, ap, config) and PrevStage::parameterMayFlowThrough(p, _, getApprox(ap), config) ) @@ -1133,10 +1127,9 @@ private module Stage2 { pragma[nomagic] private predicate revFlowIn( - DataFlowCall call, ArgumentNode arg, boolean toReturn, ApOption returnAp, Ap ap, - Configuration config + DataFlowCall call, ArgNode arg, boolean toReturn, ApOption returnAp, Ap ap, Configuration config ) { - exists(ParameterNode p, boolean allowsFieldFlow | + exists(ParamNode p, boolean allowsFieldFlow | revFlow(p, toReturn, returnAp, ap, config) and flowIntoCall(call, arg, p, allowsFieldFlow, config) | @@ -1146,7 +1139,7 @@ private module Stage2 { pragma[nomagic] private predicate revFlowInToReturn( - DataFlowCall call, ArgumentNode 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) } @@ -1199,13 +1192,13 @@ private module Stage2 { pragma[noinline] private predicate parameterFlow( - ParameterNode 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(ParameterNode 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 @@ -1245,8 +1238,7 @@ private predicate flowOutOfCallNodeCand2( pragma[nomagic] private predicate flowIntoCallNodeCand2( - DataFlowCall call, ArgumentNode node1, ParameterNode 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 @@ -1260,8 +1252,8 @@ private module LocalFlowBigStep { */ private class FlowCheckNode extends Node { FlowCheckNode() { - this instanceof CastNode or - clearsContent(this, _) + castNode(this) or + clearsContentCached(this, _) } } @@ -1275,7 +1267,7 @@ private module LocalFlowBigStep { config.isSource(node) or jumpStep(_, node, config) or additionalJumpStep(_, node, config) or - node instanceof ParameterNode or + node instanceof ParamNode or node instanceof OutNodeExt or store(_, _, node, _) or read(_, _, node) or @@ -1321,21 +1313,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 +1342,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 +1376,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 +1435,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 +1577,10 @@ private module Stage3 { pragma[nomagic] private predicate fwdFlowIn( - DataFlowCall call, ParameterNode p, Cc outercc, Cc innercc, ApOption argAp, Ap ap, + DataFlowCall call, ParamNode p, Cc outercc, Cc innercc, ApOption argAp, Ap ap, Configuration config ) { - exists(ArgumentNode 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) @@ -1631,7 +1625,7 @@ private module Stage3 { private predicate fwdFlowIsEntered( DataFlowCall call, Cc cc, ApOption argAp, Ap ap, Configuration config ) { - exists(ParameterNode p | + exists(ParamNode p | fwdFlowIn(call, p, cc, _, argAp, ap, config) and PrevStage::parameterMayFlowThrough(p, _, unbindApa(getApprox(ap)), config) ) @@ -1772,10 +1766,9 @@ private module Stage3 { pragma[nomagic] private predicate revFlowIn( - DataFlowCall call, ArgumentNode arg, boolean toReturn, ApOption returnAp, Ap ap, - Configuration config + DataFlowCall call, ArgNode arg, boolean toReturn, ApOption returnAp, Ap ap, Configuration config ) { - exists(ParameterNode p, boolean allowsFieldFlow | + exists(ParamNode p, boolean allowsFieldFlow | revFlow(p, toReturn, returnAp, ap, config) and flowIntoCall(call, arg, p, allowsFieldFlow, config) | @@ -1785,7 +1778,7 @@ private module Stage3 { pragma[nomagic] private predicate revFlowInToReturn( - DataFlowCall call, ArgumentNode 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) } @@ -1838,13 +1831,13 @@ private module Stage3 { pragma[noinline] private predicate parameterFlow( - ParameterNode 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(ParameterNode 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 @@ -2088,7 +2081,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,8 +2148,7 @@ private module Stage4 { pragma[nomagic] private predicate flowIntoCall( - DataFlowCall call, ArgumentNode node1, ParameterNode 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 @@ -2300,10 +2292,10 @@ private module Stage4 { pragma[nomagic] private predicate fwdFlowIn( - DataFlowCall call, ParameterNode p, Cc outercc, Cc innercc, ApOption argAp, Ap ap, + DataFlowCall call, ParamNode p, Cc outercc, Cc innercc, ApOption argAp, Ap ap, Configuration config ) { - exists(ArgumentNode 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) @@ -2348,7 +2340,7 @@ private module Stage4 { private predicate fwdFlowIsEntered( DataFlowCall call, Cc cc, ApOption argAp, Ap ap, Configuration config ) { - exists(ParameterNode p | + exists(ParamNode p | fwdFlowIn(call, p, cc, _, argAp, ap, config) and PrevStage::parameterMayFlowThrough(p, _, unbindApa(getApprox(ap)), config) ) @@ -2489,10 +2481,9 @@ private module Stage4 { pragma[nomagic] private predicate revFlowIn( - DataFlowCall call, ArgumentNode arg, boolean toReturn, ApOption returnAp, Ap ap, - Configuration config + DataFlowCall call, ArgNode arg, boolean toReturn, ApOption returnAp, Ap ap, Configuration config ) { - exists(ParameterNode p, boolean allowsFieldFlow | + exists(ParamNode p, boolean allowsFieldFlow | revFlow(p, toReturn, returnAp, ap, config) and flowIntoCall(call, arg, p, allowsFieldFlow, config) | @@ -2502,7 +2493,7 @@ private module Stage4 { pragma[nomagic] private predicate revFlowInToReturn( - DataFlowCall call, ArgumentNode 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) } @@ -2555,13 +2546,13 @@ private module Stage4 { pragma[noinline] private predicate parameterFlow( - ParameterNode 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(ParameterNode 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 @@ -2606,7 +2597,7 @@ private predicate nodeMayUseSummary(Node n, AccessPathApprox apa, Configuration private newtype TSummaryCtx = TSummaryCtxNone() or - TSummaryCtxSome(ParameterNode p, AccessPath ap) { + TSummaryCtxSome(ParamNode p, AccessPath ap) { Stage4::parameterMayFlowThrough(p, _, ap.getApprox(), _) } @@ -2627,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 ParameterNode p; + private ParamNode p; private AccessPath ap; SummaryCtxSome() { this = TSummaryCtxSome(p, ap) } @@ -2758,7 +2749,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 +2970,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 +3139,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 +3226,7 @@ pragma[noinline] private predicate pathIntoArg( PathNodeMid mid, int i, CallContext cc, DataFlowCall call, AccessPath ap, AccessPathApprox apa ) { - exists(ArgumentNode arg | + exists(ArgNode arg | arg = mid.getNode() and cc = mid.getCallContext() and arg.argumentOf(call, i) and @@ -3248,7 +3239,7 @@ pragma[noinline] private predicate parameterCand( DataFlowCallable callable, int i, AccessPathApprox apa, Configuration config ) { - exists(ParameterNode p | + exists(ParamNode p | Stage4::revFlow(p, _, _, apa, config) and p.isParameterOf(callable, i) ) @@ -3272,7 +3263,7 @@ private predicate pathIntoCallable0( * respectively. */ private predicate pathIntoCallable( - PathNodeMid mid, ParameterNode 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 | @@ -3568,7 +3559,7 @@ private module FlowExploration { private newtype TSummaryCtx1 = TSummaryCtx1None() or - TSummaryCtx1Param(ParameterNode p) + TSummaryCtx1Param(ParamNode p) private newtype TSummaryCtx2 = TSummaryCtx2None() or @@ -3591,7 +3582,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 +3602,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 +3616,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 +3774,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 +3788,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 +3804,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 +3818,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 +3915,7 @@ private module FlowExploration { PartialPathNodeFwd mid, int i, CallContext cc, DataFlowCall call, PartialAccessPath ap, Configuration config ) { - exists(ArgumentNode arg | + exists(ArgNode arg | arg = mid.getNode() and cc = mid.getCallContext() and arg.argumentOf(call, i) and @@ -3943,7 +3934,7 @@ private module FlowExploration { } private predicate partialPathIntoCallable( - PartialPathNodeFwd mid, ParameterNode p, CallContext outercc, CallContextCall innercc, + PartialPathNodeFwd mid, ParamNode p, CallContext outercc, CallContextCall innercc, TSummaryCtx1 sc1, TSummaryCtx2 sc2, DataFlowCall call, PartialAccessPath ap, Configuration config ) { @@ -3980,7 +3971,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(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) ) @@ -4037,7 +4028,7 @@ private module FlowExploration { apConsRev(ap, c, ap0, config) ) or - exists(ParameterNode p | + exists(ParamNode p | mid.getNode() = p and viableParamArg(_, p, node) and sc1 = mid.getSummaryCtx1() and @@ -4115,7 +4106,7 @@ private module FlowExploration { int pos, TRevSummaryCtx1Some sc1, TRevSummaryCtx2Some sc2, RevPartialAccessPath ap, Configuration config ) { - exists(PartialPathNodeRev mid, ParameterNode p | + exists(PartialPathNodeRev mid, ParamNode p | mid.getNode() = p and p.isParameterOf(_, pos) and sc1 = mid.getSummaryCtx1() and @@ -4138,7 +4129,7 @@ private module FlowExploration { pragma[nomagic] private predicate revPartialPathThroughCallable( - PartialPathNodeRev mid, ArgumentNode 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 9498e51e7e6..058d66b1496 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(ArgNode arg | fwdFlow(arg, cc, config) and viableParamArg(call, _, arg) ) @@ -515,24 +512,22 @@ private module Stage1 { pragma[nomagic] predicate viableParamArgNodeCandFwd1( - DataFlowCall call, ParameterNode p, ArgumentNode 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, ArgumentNode arg, boolean toReturn, Configuration config - ) { - exists(ParameterNode 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, ArgumentNode arg, Configuration config) { + private predicate revFlowInToReturn(DataFlowCall call, ArgNode arg, Configuration config) { revFlowIn(call, arg, true, config) } @@ -597,7 +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(ParameterNode 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 @@ -663,7 +658,7 @@ private predicate flowOutOfCallNodeCand1( pragma[nomagic] private predicate viableParamArgNodeCand1( - DataFlowCall call, ParameterNode p, ArgumentNode arg, Configuration config + DataFlowCall call, ParamNode p, ArgNode arg, Configuration config ) { Stage1::viableParamArgNodeCandFwd1(call, p, arg, config) and Stage1::revFlow(arg, config) @@ -675,7 +670,7 @@ private predicate viableParamArgNodeCand1( */ pragma[nomagic] private predicate flowIntoCallNodeCand1( - DataFlowCall call, ArgumentNode arg, ParameterNode p, Configuration config + DataFlowCall call, ArgNode arg, ParamNode p, Configuration config ) { viableParamArgNodeCand1(call, p, arg, config) and Stage1::revFlow(p, config) and @@ -735,8 +730,7 @@ private predicate flowOutOfCallNodeCand1( */ pragma[nomagic] private predicate flowIntoCallNodeCand1( - DataFlowCall call, ArgumentNode arg, ParameterNode 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 | @@ -944,10 +938,10 @@ private module Stage2 { pragma[nomagic] private predicate fwdFlowIn( - DataFlowCall call, ParameterNode p, Cc outercc, Cc innercc, ApOption argAp, Ap ap, + DataFlowCall call, ParamNode p, Cc outercc, Cc innercc, ApOption argAp, Ap ap, Configuration config ) { - exists(ArgumentNode 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) @@ -992,7 +986,7 @@ private module Stage2 { private predicate fwdFlowIsEntered( DataFlowCall call, Cc cc, ApOption argAp, Ap ap, Configuration config ) { - exists(ParameterNode p | + exists(ParamNode p | fwdFlowIn(call, p, cc, _, argAp, ap, config) and PrevStage::parameterMayFlowThrough(p, _, getApprox(ap), config) ) @@ -1133,10 +1127,9 @@ private module Stage2 { pragma[nomagic] private predicate revFlowIn( - DataFlowCall call, ArgumentNode arg, boolean toReturn, ApOption returnAp, Ap ap, - Configuration config + DataFlowCall call, ArgNode arg, boolean toReturn, ApOption returnAp, Ap ap, Configuration config ) { - exists(ParameterNode p, boolean allowsFieldFlow | + exists(ParamNode p, boolean allowsFieldFlow | revFlow(p, toReturn, returnAp, ap, config) and flowIntoCall(call, arg, p, allowsFieldFlow, config) | @@ -1146,7 +1139,7 @@ private module Stage2 { pragma[nomagic] private predicate revFlowInToReturn( - DataFlowCall call, ArgumentNode 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) } @@ -1199,13 +1192,13 @@ private module Stage2 { pragma[noinline] private predicate parameterFlow( - ParameterNode 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(ParameterNode 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 @@ -1245,8 +1238,7 @@ private predicate flowOutOfCallNodeCand2( pragma[nomagic] private predicate flowIntoCallNodeCand2( - DataFlowCall call, ArgumentNode node1, ParameterNode 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 @@ -1260,8 +1252,8 @@ private module LocalFlowBigStep { */ private class FlowCheckNode extends Node { FlowCheckNode() { - this instanceof CastNode or - clearsContent(this, _) + castNode(this) or + clearsContentCached(this, _) } } @@ -1275,7 +1267,7 @@ private module LocalFlowBigStep { config.isSource(node) or jumpStep(_, node, config) or additionalJumpStep(_, node, config) or - node instanceof ParameterNode or + node instanceof ParamNode or node instanceof OutNodeExt or store(_, _, node, _) or read(_, _, node) or @@ -1321,21 +1313,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 +1342,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 +1376,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 +1435,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 +1577,10 @@ private module Stage3 { pragma[nomagic] private predicate fwdFlowIn( - DataFlowCall call, ParameterNode p, Cc outercc, Cc innercc, ApOption argAp, Ap ap, + DataFlowCall call, ParamNode p, Cc outercc, Cc innercc, ApOption argAp, Ap ap, Configuration config ) { - exists(ArgumentNode 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) @@ -1631,7 +1625,7 @@ private module Stage3 { private predicate fwdFlowIsEntered( DataFlowCall call, Cc cc, ApOption argAp, Ap ap, Configuration config ) { - exists(ParameterNode p | + exists(ParamNode p | fwdFlowIn(call, p, cc, _, argAp, ap, config) and PrevStage::parameterMayFlowThrough(p, _, unbindApa(getApprox(ap)), config) ) @@ -1772,10 +1766,9 @@ private module Stage3 { pragma[nomagic] private predicate revFlowIn( - DataFlowCall call, ArgumentNode arg, boolean toReturn, ApOption returnAp, Ap ap, - Configuration config + DataFlowCall call, ArgNode arg, boolean toReturn, ApOption returnAp, Ap ap, Configuration config ) { - exists(ParameterNode p, boolean allowsFieldFlow | + exists(ParamNode p, boolean allowsFieldFlow | revFlow(p, toReturn, returnAp, ap, config) and flowIntoCall(call, arg, p, allowsFieldFlow, config) | @@ -1785,7 +1778,7 @@ private module Stage3 { pragma[nomagic] private predicate revFlowInToReturn( - DataFlowCall call, ArgumentNode 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) } @@ -1838,13 +1831,13 @@ private module Stage3 { pragma[noinline] private predicate parameterFlow( - ParameterNode 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(ParameterNode 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 @@ -2088,7 +2081,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,8 +2148,7 @@ private module Stage4 { pragma[nomagic] private predicate flowIntoCall( - DataFlowCall call, ArgumentNode node1, ParameterNode 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 @@ -2300,10 +2292,10 @@ private module Stage4 { pragma[nomagic] private predicate fwdFlowIn( - DataFlowCall call, ParameterNode p, Cc outercc, Cc innercc, ApOption argAp, Ap ap, + DataFlowCall call, ParamNode p, Cc outercc, Cc innercc, ApOption argAp, Ap ap, Configuration config ) { - exists(ArgumentNode 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) @@ -2348,7 +2340,7 @@ private module Stage4 { private predicate fwdFlowIsEntered( DataFlowCall call, Cc cc, ApOption argAp, Ap ap, Configuration config ) { - exists(ParameterNode p | + exists(ParamNode p | fwdFlowIn(call, p, cc, _, argAp, ap, config) and PrevStage::parameterMayFlowThrough(p, _, unbindApa(getApprox(ap)), config) ) @@ -2489,10 +2481,9 @@ private module Stage4 { pragma[nomagic] private predicate revFlowIn( - DataFlowCall call, ArgumentNode arg, boolean toReturn, ApOption returnAp, Ap ap, - Configuration config + DataFlowCall call, ArgNode arg, boolean toReturn, ApOption returnAp, Ap ap, Configuration config ) { - exists(ParameterNode p, boolean allowsFieldFlow | + exists(ParamNode p, boolean allowsFieldFlow | revFlow(p, toReturn, returnAp, ap, config) and flowIntoCall(call, arg, p, allowsFieldFlow, config) | @@ -2502,7 +2493,7 @@ private module Stage4 { pragma[nomagic] private predicate revFlowInToReturn( - DataFlowCall call, ArgumentNode 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) } @@ -2555,13 +2546,13 @@ private module Stage4 { pragma[noinline] private predicate parameterFlow( - ParameterNode 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(ParameterNode 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 @@ -2606,7 +2597,7 @@ private predicate nodeMayUseSummary(Node n, AccessPathApprox apa, Configuration private newtype TSummaryCtx = TSummaryCtxNone() or - TSummaryCtxSome(ParameterNode p, AccessPath ap) { + TSummaryCtxSome(ParamNode p, AccessPath ap) { Stage4::parameterMayFlowThrough(p, _, ap.getApprox(), _) } @@ -2627,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 ParameterNode p; + private ParamNode p; private AccessPath ap; SummaryCtxSome() { this = TSummaryCtxSome(p, ap) } @@ -2758,7 +2749,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 +2970,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 +3139,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 +3226,7 @@ pragma[noinline] private predicate pathIntoArg( PathNodeMid mid, int i, CallContext cc, DataFlowCall call, AccessPath ap, AccessPathApprox apa ) { - exists(ArgumentNode arg | + exists(ArgNode arg | arg = mid.getNode() and cc = mid.getCallContext() and arg.argumentOf(call, i) and @@ -3248,7 +3239,7 @@ pragma[noinline] private predicate parameterCand( DataFlowCallable callable, int i, AccessPathApprox apa, Configuration config ) { - exists(ParameterNode p | + exists(ParamNode p | Stage4::revFlow(p, _, _, apa, config) and p.isParameterOf(callable, i) ) @@ -3272,7 +3263,7 @@ private predicate pathIntoCallable0( * respectively. */ private predicate pathIntoCallable( - PathNodeMid mid, ParameterNode 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 | @@ -3568,7 +3559,7 @@ private module FlowExploration { private newtype TSummaryCtx1 = TSummaryCtx1None() or - TSummaryCtx1Param(ParameterNode p) + TSummaryCtx1Param(ParamNode p) private newtype TSummaryCtx2 = TSummaryCtx2None() or @@ -3591,7 +3582,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 +3602,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 +3616,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 +3774,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 +3788,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 +3804,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 +3818,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 +3915,7 @@ private module FlowExploration { PartialPathNodeFwd mid, int i, CallContext cc, DataFlowCall call, PartialAccessPath ap, Configuration config ) { - exists(ArgumentNode arg | + exists(ArgNode arg | arg = mid.getNode() and cc = mid.getCallContext() and arg.argumentOf(call, i) and @@ -3943,7 +3934,7 @@ private module FlowExploration { } private predicate partialPathIntoCallable( - PartialPathNodeFwd mid, ParameterNode p, CallContext outercc, CallContextCall innercc, + PartialPathNodeFwd mid, ParamNode p, CallContext outercc, CallContextCall innercc, TSummaryCtx1 sc1, TSummaryCtx2 sc2, DataFlowCall call, PartialAccessPath ap, Configuration config ) { @@ -3980,7 +3971,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(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) ) @@ -4037,7 +4028,7 @@ private module FlowExploration { apConsRev(ap, c, ap0, config) ) or - exists(ParameterNode p | + exists(ParamNode p | mid.getNode() = p and viableParamArg(_, p, node) and sc1 = mid.getSummaryCtx1() and @@ -4115,7 +4106,7 @@ private module FlowExploration { int pos, TRevSummaryCtx1Some sc1, TRevSummaryCtx2Some sc2, RevPartialAccessPath ap, Configuration config ) { - exists(PartialPathNodeRev mid, ParameterNode p | + exists(PartialPathNodeRev mid, ParamNode p | mid.getNode() = p and p.isParameterOf(_, pos) and sc1 = mid.getSummaryCtx1() and @@ -4138,7 +4129,7 @@ private module FlowExploration { pragma[nomagic] private predicate revPartialPathThroughCallable( - PartialPathNodeRev mid, ArgumentNode 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 9498e51e7e6..058d66b1496 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(ArgNode arg | fwdFlow(arg, cc, config) and viableParamArg(call, _, arg) ) @@ -515,24 +512,22 @@ private module Stage1 { pragma[nomagic] predicate viableParamArgNodeCandFwd1( - DataFlowCall call, ParameterNode p, ArgumentNode 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, ArgumentNode arg, boolean toReturn, Configuration config - ) { - exists(ParameterNode 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, ArgumentNode arg, Configuration config) { + private predicate revFlowInToReturn(DataFlowCall call, ArgNode arg, Configuration config) { revFlowIn(call, arg, true, config) } @@ -597,7 +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(ParameterNode 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 @@ -663,7 +658,7 @@ private predicate flowOutOfCallNodeCand1( pragma[nomagic] private predicate viableParamArgNodeCand1( - DataFlowCall call, ParameterNode p, ArgumentNode arg, Configuration config + DataFlowCall call, ParamNode p, ArgNode arg, Configuration config ) { Stage1::viableParamArgNodeCandFwd1(call, p, arg, config) and Stage1::revFlow(arg, config) @@ -675,7 +670,7 @@ private predicate viableParamArgNodeCand1( */ pragma[nomagic] private predicate flowIntoCallNodeCand1( - DataFlowCall call, ArgumentNode arg, ParameterNode p, Configuration config + DataFlowCall call, ArgNode arg, ParamNode p, Configuration config ) { viableParamArgNodeCand1(call, p, arg, config) and Stage1::revFlow(p, config) and @@ -735,8 +730,7 @@ private predicate flowOutOfCallNodeCand1( */ pragma[nomagic] private predicate flowIntoCallNodeCand1( - DataFlowCall call, ArgumentNode arg, ParameterNode 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 | @@ -944,10 +938,10 @@ private module Stage2 { pragma[nomagic] private predicate fwdFlowIn( - DataFlowCall call, ParameterNode p, Cc outercc, Cc innercc, ApOption argAp, Ap ap, + DataFlowCall call, ParamNode p, Cc outercc, Cc innercc, ApOption argAp, Ap ap, Configuration config ) { - exists(ArgumentNode 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) @@ -992,7 +986,7 @@ private module Stage2 { private predicate fwdFlowIsEntered( DataFlowCall call, Cc cc, ApOption argAp, Ap ap, Configuration config ) { - exists(ParameterNode p | + exists(ParamNode p | fwdFlowIn(call, p, cc, _, argAp, ap, config) and PrevStage::parameterMayFlowThrough(p, _, getApprox(ap), config) ) @@ -1133,10 +1127,9 @@ private module Stage2 { pragma[nomagic] private predicate revFlowIn( - DataFlowCall call, ArgumentNode arg, boolean toReturn, ApOption returnAp, Ap ap, - Configuration config + DataFlowCall call, ArgNode arg, boolean toReturn, ApOption returnAp, Ap ap, Configuration config ) { - exists(ParameterNode p, boolean allowsFieldFlow | + exists(ParamNode p, boolean allowsFieldFlow | revFlow(p, toReturn, returnAp, ap, config) and flowIntoCall(call, arg, p, allowsFieldFlow, config) | @@ -1146,7 +1139,7 @@ private module Stage2 { pragma[nomagic] private predicate revFlowInToReturn( - DataFlowCall call, ArgumentNode 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) } @@ -1199,13 +1192,13 @@ private module Stage2 { pragma[noinline] private predicate parameterFlow( - ParameterNode 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(ParameterNode 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 @@ -1245,8 +1238,7 @@ private predicate flowOutOfCallNodeCand2( pragma[nomagic] private predicate flowIntoCallNodeCand2( - DataFlowCall call, ArgumentNode node1, ParameterNode 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 @@ -1260,8 +1252,8 @@ private module LocalFlowBigStep { */ private class FlowCheckNode extends Node { FlowCheckNode() { - this instanceof CastNode or - clearsContent(this, _) + castNode(this) or + clearsContentCached(this, _) } } @@ -1275,7 +1267,7 @@ private module LocalFlowBigStep { config.isSource(node) or jumpStep(_, node, config) or additionalJumpStep(_, node, config) or - node instanceof ParameterNode or + node instanceof ParamNode or node instanceof OutNodeExt or store(_, _, node, _) or read(_, _, node) or @@ -1321,21 +1313,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 +1342,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 +1376,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 +1435,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 +1577,10 @@ private module Stage3 { pragma[nomagic] private predicate fwdFlowIn( - DataFlowCall call, ParameterNode p, Cc outercc, Cc innercc, ApOption argAp, Ap ap, + DataFlowCall call, ParamNode p, Cc outercc, Cc innercc, ApOption argAp, Ap ap, Configuration config ) { - exists(ArgumentNode 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) @@ -1631,7 +1625,7 @@ private module Stage3 { private predicate fwdFlowIsEntered( DataFlowCall call, Cc cc, ApOption argAp, Ap ap, Configuration config ) { - exists(ParameterNode p | + exists(ParamNode p | fwdFlowIn(call, p, cc, _, argAp, ap, config) and PrevStage::parameterMayFlowThrough(p, _, unbindApa(getApprox(ap)), config) ) @@ -1772,10 +1766,9 @@ private module Stage3 { pragma[nomagic] private predicate revFlowIn( - DataFlowCall call, ArgumentNode arg, boolean toReturn, ApOption returnAp, Ap ap, - Configuration config + DataFlowCall call, ArgNode arg, boolean toReturn, ApOption returnAp, Ap ap, Configuration config ) { - exists(ParameterNode p, boolean allowsFieldFlow | + exists(ParamNode p, boolean allowsFieldFlow | revFlow(p, toReturn, returnAp, ap, config) and flowIntoCall(call, arg, p, allowsFieldFlow, config) | @@ -1785,7 +1778,7 @@ private module Stage3 { pragma[nomagic] private predicate revFlowInToReturn( - DataFlowCall call, ArgumentNode 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) } @@ -1838,13 +1831,13 @@ private module Stage3 { pragma[noinline] private predicate parameterFlow( - ParameterNode 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(ParameterNode 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 @@ -2088,7 +2081,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,8 +2148,7 @@ private module Stage4 { pragma[nomagic] private predicate flowIntoCall( - DataFlowCall call, ArgumentNode node1, ParameterNode 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 @@ -2300,10 +2292,10 @@ private module Stage4 { pragma[nomagic] private predicate fwdFlowIn( - DataFlowCall call, ParameterNode p, Cc outercc, Cc innercc, ApOption argAp, Ap ap, + DataFlowCall call, ParamNode p, Cc outercc, Cc innercc, ApOption argAp, Ap ap, Configuration config ) { - exists(ArgumentNode 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) @@ -2348,7 +2340,7 @@ private module Stage4 { private predicate fwdFlowIsEntered( DataFlowCall call, Cc cc, ApOption argAp, Ap ap, Configuration config ) { - exists(ParameterNode p | + exists(ParamNode p | fwdFlowIn(call, p, cc, _, argAp, ap, config) and PrevStage::parameterMayFlowThrough(p, _, unbindApa(getApprox(ap)), config) ) @@ -2489,10 +2481,9 @@ private module Stage4 { pragma[nomagic] private predicate revFlowIn( - DataFlowCall call, ArgumentNode arg, boolean toReturn, ApOption returnAp, Ap ap, - Configuration config + DataFlowCall call, ArgNode arg, boolean toReturn, ApOption returnAp, Ap ap, Configuration config ) { - exists(ParameterNode p, boolean allowsFieldFlow | + exists(ParamNode p, boolean allowsFieldFlow | revFlow(p, toReturn, returnAp, ap, config) and flowIntoCall(call, arg, p, allowsFieldFlow, config) | @@ -2502,7 +2493,7 @@ private module Stage4 { pragma[nomagic] private predicate revFlowInToReturn( - DataFlowCall call, ArgumentNode 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) } @@ -2555,13 +2546,13 @@ private module Stage4 { pragma[noinline] private predicate parameterFlow( - ParameterNode 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(ParameterNode 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 @@ -2606,7 +2597,7 @@ private predicate nodeMayUseSummary(Node n, AccessPathApprox apa, Configuration private newtype TSummaryCtx = TSummaryCtxNone() or - TSummaryCtxSome(ParameterNode p, AccessPath ap) { + TSummaryCtxSome(ParamNode p, AccessPath ap) { Stage4::parameterMayFlowThrough(p, _, ap.getApprox(), _) } @@ -2627,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 ParameterNode p; + private ParamNode p; private AccessPath ap; SummaryCtxSome() { this = TSummaryCtxSome(p, ap) } @@ -2758,7 +2749,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 +2970,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 +3139,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 +3226,7 @@ pragma[noinline] private predicate pathIntoArg( PathNodeMid mid, int i, CallContext cc, DataFlowCall call, AccessPath ap, AccessPathApprox apa ) { - exists(ArgumentNode arg | + exists(ArgNode arg | arg = mid.getNode() and cc = mid.getCallContext() and arg.argumentOf(call, i) and @@ -3248,7 +3239,7 @@ pragma[noinline] private predicate parameterCand( DataFlowCallable callable, int i, AccessPathApprox apa, Configuration config ) { - exists(ParameterNode p | + exists(ParamNode p | Stage4::revFlow(p, _, _, apa, config) and p.isParameterOf(callable, i) ) @@ -3272,7 +3263,7 @@ private predicate pathIntoCallable0( * respectively. */ private predicate pathIntoCallable( - PathNodeMid mid, ParameterNode 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 | @@ -3568,7 +3559,7 @@ private module FlowExploration { private newtype TSummaryCtx1 = TSummaryCtx1None() or - TSummaryCtx1Param(ParameterNode p) + TSummaryCtx1Param(ParamNode p) private newtype TSummaryCtx2 = TSummaryCtx2None() or @@ -3591,7 +3582,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 +3602,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 +3616,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 +3774,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 +3788,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 +3804,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 +3818,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 +3915,7 @@ private module FlowExploration { PartialPathNodeFwd mid, int i, CallContext cc, DataFlowCall call, PartialAccessPath ap, Configuration config ) { - exists(ArgumentNode arg | + exists(ArgNode arg | arg = mid.getNode() and cc = mid.getCallContext() and arg.argumentOf(call, i) and @@ -3943,7 +3934,7 @@ private module FlowExploration { } private predicate partialPathIntoCallable( - PartialPathNodeFwd mid, ParameterNode p, CallContext outercc, CallContextCall innercc, + PartialPathNodeFwd mid, ParamNode p, CallContext outercc, CallContextCall innercc, TSummaryCtx1 sc1, TSummaryCtx2 sc2, DataFlowCall call, PartialAccessPath ap, Configuration config ) { @@ -3980,7 +3971,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(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) ) @@ -4037,7 +4028,7 @@ private module FlowExploration { apConsRev(ap, c, ap0, config) ) or - exists(ParameterNode p | + exists(ParamNode p | mid.getNode() = p and viableParamArg(_, p, node) and sc1 = mid.getSummaryCtx1() and @@ -4115,7 +4106,7 @@ private module FlowExploration { int pos, TRevSummaryCtx1Some sc1, TRevSummaryCtx2Some sc2, RevPartialAccessPath ap, Configuration config ) { - exists(PartialPathNodeRev mid, ParameterNode p | + exists(PartialPathNodeRev mid, ParamNode p | mid.getNode() = p and p.isParameterOf(_, pos) and sc1 = mid.getSummaryCtx1() and @@ -4138,7 +4129,7 @@ private module FlowExploration { pragma[nomagic] private predicate revPartialPathThroughCallable( - PartialPathNodeRev mid, ArgumentNode 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 9498e51e7e6..058d66b1496 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(ArgNode arg | fwdFlow(arg, cc, config) and viableParamArg(call, _, arg) ) @@ -515,24 +512,22 @@ private module Stage1 { pragma[nomagic] predicate viableParamArgNodeCandFwd1( - DataFlowCall call, ParameterNode p, ArgumentNode 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, ArgumentNode arg, boolean toReturn, Configuration config - ) { - exists(ParameterNode 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, ArgumentNode arg, Configuration config) { + private predicate revFlowInToReturn(DataFlowCall call, ArgNode arg, Configuration config) { revFlowIn(call, arg, true, config) } @@ -597,7 +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(ParameterNode 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 @@ -663,7 +658,7 @@ private predicate flowOutOfCallNodeCand1( pragma[nomagic] private predicate viableParamArgNodeCand1( - DataFlowCall call, ParameterNode p, ArgumentNode arg, Configuration config + DataFlowCall call, ParamNode p, ArgNode arg, Configuration config ) { Stage1::viableParamArgNodeCandFwd1(call, p, arg, config) and Stage1::revFlow(arg, config) @@ -675,7 +670,7 @@ private predicate viableParamArgNodeCand1( */ pragma[nomagic] private predicate flowIntoCallNodeCand1( - DataFlowCall call, ArgumentNode arg, ParameterNode p, Configuration config + DataFlowCall call, ArgNode arg, ParamNode p, Configuration config ) { viableParamArgNodeCand1(call, p, arg, config) and Stage1::revFlow(p, config) and @@ -735,8 +730,7 @@ private predicate flowOutOfCallNodeCand1( */ pragma[nomagic] private predicate flowIntoCallNodeCand1( - DataFlowCall call, ArgumentNode arg, ParameterNode 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 | @@ -944,10 +938,10 @@ private module Stage2 { pragma[nomagic] private predicate fwdFlowIn( - DataFlowCall call, ParameterNode p, Cc outercc, Cc innercc, ApOption argAp, Ap ap, + DataFlowCall call, ParamNode p, Cc outercc, Cc innercc, ApOption argAp, Ap ap, Configuration config ) { - exists(ArgumentNode 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) @@ -992,7 +986,7 @@ private module Stage2 { private predicate fwdFlowIsEntered( DataFlowCall call, Cc cc, ApOption argAp, Ap ap, Configuration config ) { - exists(ParameterNode p | + exists(ParamNode p | fwdFlowIn(call, p, cc, _, argAp, ap, config) and PrevStage::parameterMayFlowThrough(p, _, getApprox(ap), config) ) @@ -1133,10 +1127,9 @@ private module Stage2 { pragma[nomagic] private predicate revFlowIn( - DataFlowCall call, ArgumentNode arg, boolean toReturn, ApOption returnAp, Ap ap, - Configuration config + DataFlowCall call, ArgNode arg, boolean toReturn, ApOption returnAp, Ap ap, Configuration config ) { - exists(ParameterNode p, boolean allowsFieldFlow | + exists(ParamNode p, boolean allowsFieldFlow | revFlow(p, toReturn, returnAp, ap, config) and flowIntoCall(call, arg, p, allowsFieldFlow, config) | @@ -1146,7 +1139,7 @@ private module Stage2 { pragma[nomagic] private predicate revFlowInToReturn( - DataFlowCall call, ArgumentNode 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) } @@ -1199,13 +1192,13 @@ private module Stage2 { pragma[noinline] private predicate parameterFlow( - ParameterNode 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(ParameterNode 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 @@ -1245,8 +1238,7 @@ private predicate flowOutOfCallNodeCand2( pragma[nomagic] private predicate flowIntoCallNodeCand2( - DataFlowCall call, ArgumentNode node1, ParameterNode 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 @@ -1260,8 +1252,8 @@ private module LocalFlowBigStep { */ private class FlowCheckNode extends Node { FlowCheckNode() { - this instanceof CastNode or - clearsContent(this, _) + castNode(this) or + clearsContentCached(this, _) } } @@ -1275,7 +1267,7 @@ private module LocalFlowBigStep { config.isSource(node) or jumpStep(_, node, config) or additionalJumpStep(_, node, config) or - node instanceof ParameterNode or + node instanceof ParamNode or node instanceof OutNodeExt or store(_, _, node, _) or read(_, _, node) or @@ -1321,21 +1313,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 +1342,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 +1376,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 +1435,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 +1577,10 @@ private module Stage3 { pragma[nomagic] private predicate fwdFlowIn( - DataFlowCall call, ParameterNode p, Cc outercc, Cc innercc, ApOption argAp, Ap ap, + DataFlowCall call, ParamNode p, Cc outercc, Cc innercc, ApOption argAp, Ap ap, Configuration config ) { - exists(ArgumentNode 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) @@ -1631,7 +1625,7 @@ private module Stage3 { private predicate fwdFlowIsEntered( DataFlowCall call, Cc cc, ApOption argAp, Ap ap, Configuration config ) { - exists(ParameterNode p | + exists(ParamNode p | fwdFlowIn(call, p, cc, _, argAp, ap, config) and PrevStage::parameterMayFlowThrough(p, _, unbindApa(getApprox(ap)), config) ) @@ -1772,10 +1766,9 @@ private module Stage3 { pragma[nomagic] private predicate revFlowIn( - DataFlowCall call, ArgumentNode arg, boolean toReturn, ApOption returnAp, Ap ap, - Configuration config + DataFlowCall call, ArgNode arg, boolean toReturn, ApOption returnAp, Ap ap, Configuration config ) { - exists(ParameterNode p, boolean allowsFieldFlow | + exists(ParamNode p, boolean allowsFieldFlow | revFlow(p, toReturn, returnAp, ap, config) and flowIntoCall(call, arg, p, allowsFieldFlow, config) | @@ -1785,7 +1778,7 @@ private module Stage3 { pragma[nomagic] private predicate revFlowInToReturn( - DataFlowCall call, ArgumentNode 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) } @@ -1838,13 +1831,13 @@ private module Stage3 { pragma[noinline] private predicate parameterFlow( - ParameterNode 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(ParameterNode 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 @@ -2088,7 +2081,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,8 +2148,7 @@ private module Stage4 { pragma[nomagic] private predicate flowIntoCall( - DataFlowCall call, ArgumentNode node1, ParameterNode 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 @@ -2300,10 +2292,10 @@ private module Stage4 { pragma[nomagic] private predicate fwdFlowIn( - DataFlowCall call, ParameterNode p, Cc outercc, Cc innercc, ApOption argAp, Ap ap, + DataFlowCall call, ParamNode p, Cc outercc, Cc innercc, ApOption argAp, Ap ap, Configuration config ) { - exists(ArgumentNode 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) @@ -2348,7 +2340,7 @@ private module Stage4 { private predicate fwdFlowIsEntered( DataFlowCall call, Cc cc, ApOption argAp, Ap ap, Configuration config ) { - exists(ParameterNode p | + exists(ParamNode p | fwdFlowIn(call, p, cc, _, argAp, ap, config) and PrevStage::parameterMayFlowThrough(p, _, unbindApa(getApprox(ap)), config) ) @@ -2489,10 +2481,9 @@ private module Stage4 { pragma[nomagic] private predicate revFlowIn( - DataFlowCall call, ArgumentNode arg, boolean toReturn, ApOption returnAp, Ap ap, - Configuration config + DataFlowCall call, ArgNode arg, boolean toReturn, ApOption returnAp, Ap ap, Configuration config ) { - exists(ParameterNode p, boolean allowsFieldFlow | + exists(ParamNode p, boolean allowsFieldFlow | revFlow(p, toReturn, returnAp, ap, config) and flowIntoCall(call, arg, p, allowsFieldFlow, config) | @@ -2502,7 +2493,7 @@ private module Stage4 { pragma[nomagic] private predicate revFlowInToReturn( - DataFlowCall call, ArgumentNode 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) } @@ -2555,13 +2546,13 @@ private module Stage4 { pragma[noinline] private predicate parameterFlow( - ParameterNode 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(ParameterNode 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 @@ -2606,7 +2597,7 @@ private predicate nodeMayUseSummary(Node n, AccessPathApprox apa, Configuration private newtype TSummaryCtx = TSummaryCtxNone() or - TSummaryCtxSome(ParameterNode p, AccessPath ap) { + TSummaryCtxSome(ParamNode p, AccessPath ap) { Stage4::parameterMayFlowThrough(p, _, ap.getApprox(), _) } @@ -2627,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 ParameterNode p; + private ParamNode p; private AccessPath ap; SummaryCtxSome() { this = TSummaryCtxSome(p, ap) } @@ -2758,7 +2749,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 +2970,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 +3139,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 +3226,7 @@ pragma[noinline] private predicate pathIntoArg( PathNodeMid mid, int i, CallContext cc, DataFlowCall call, AccessPath ap, AccessPathApprox apa ) { - exists(ArgumentNode arg | + exists(ArgNode arg | arg = mid.getNode() and cc = mid.getCallContext() and arg.argumentOf(call, i) and @@ -3248,7 +3239,7 @@ pragma[noinline] private predicate parameterCand( DataFlowCallable callable, int i, AccessPathApprox apa, Configuration config ) { - exists(ParameterNode p | + exists(ParamNode p | Stage4::revFlow(p, _, _, apa, config) and p.isParameterOf(callable, i) ) @@ -3272,7 +3263,7 @@ private predicate pathIntoCallable0( * respectively. */ private predicate pathIntoCallable( - PathNodeMid mid, ParameterNode 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 | @@ -3568,7 +3559,7 @@ private module FlowExploration { private newtype TSummaryCtx1 = TSummaryCtx1None() or - TSummaryCtx1Param(ParameterNode p) + TSummaryCtx1Param(ParamNode p) private newtype TSummaryCtx2 = TSummaryCtx2None() or @@ -3591,7 +3582,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 +3602,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 +3616,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 +3774,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 +3788,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 +3804,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 +3818,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 +3915,7 @@ private module FlowExploration { PartialPathNodeFwd mid, int i, CallContext cc, DataFlowCall call, PartialAccessPath ap, Configuration config ) { - exists(ArgumentNode arg | + exists(ArgNode arg | arg = mid.getNode() and cc = mid.getCallContext() and arg.argumentOf(call, i) and @@ -3943,7 +3934,7 @@ private module FlowExploration { } private predicate partialPathIntoCallable( - PartialPathNodeFwd mid, ParameterNode p, CallContext outercc, CallContextCall innercc, + PartialPathNodeFwd mid, ParamNode p, CallContext outercc, CallContextCall innercc, TSummaryCtx1 sc1, TSummaryCtx2 sc2, DataFlowCall call, PartialAccessPath ap, Configuration config ) { @@ -3980,7 +3971,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(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) ) @@ -4037,7 +4028,7 @@ private module FlowExploration { apConsRev(ap, c, ap0, config) ) or - exists(ParameterNode p | + exists(ParamNode p | mid.getNode() = p and viableParamArg(_, p, node) and sc1 = mid.getSummaryCtx1() and @@ -4115,7 +4106,7 @@ private module FlowExploration { int pos, TRevSummaryCtx1Some sc1, TRevSummaryCtx2Some sc2, RevPartialAccessPath ap, Configuration config ) { - exists(PartialPathNodeRev mid, ParameterNode p | + exists(PartialPathNodeRev mid, ParamNode p | mid.getNode() = p and p.isParameterOf(_, pos) and sc1 = mid.getSummaryCtx1() and @@ -4138,7 +4129,7 @@ private module FlowExploration { pragma[nomagic] private predicate revPartialPathThroughCallable( - PartialPathNodeRev mid, ArgumentNode 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 966c30038cc..462e89ac9ed 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,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, ParameterNode p) { + private predicate viableParamNonLambda(DataFlowCall call, int i, ParamNode p) { p.isParameterOf(viableCallable(call), i) } - private predicate viableParamLambda(DataFlowCall call, int i, ParameterNode p) { + private predicate viableParamLambda(DataFlowCall call, int i, ParamNode p) { p.isParameterOf(viableCallableLambda(call, _), i) } - private predicate viableParamArgNonLambda(DataFlowCall call, ParameterNode p, ArgumentNode 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, ParameterNode p, ArgumentNode arg) { + private predicate viableParamArgLambda(DataFlowCall call, ParamNode p, ArgNode arg) { exists(int i | viableParamLambda(call, i, p) and arg.argumentOf(call, i) @@ -118,8 +118,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 ArgNode or node instanceof ReturnNode + 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 @@ -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 | @@ -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 @@ -176,7 +176,7 @@ private module LambdaFlow { ) or // flow into a callable - exists(ParameterNode 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 @@ -227,7 +227,7 @@ private module LambdaFlow { pragma[nomagic] predicate revLambdaFlowIn( - DataFlowCall lambdaCall, LambdaCallKind kind, ParameterNode p, DataFlowType t, boolean toJump, + DataFlowCall lambdaCall, LambdaCallKind kind, ParamNode p, DataFlowType t, boolean toJump, DataFlowCallOption lastCall ) { revLambdaFlow(lambdaCall, kind, p, t, false, toJump, lastCall) @@ -242,6 +242,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 ArgNode + } + + cached + predicate hiddenNode(Node n) { nodeIsHidden(n) } + + cached + OutNodeExt getAnOutNodeExt(DataFlowCall call, ReturnKindExt k) { + result = getAnOutNode(call, k.(ValueReturnKind).getKind()) + or + exists(ArgNode 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(ParamNode 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 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 + // 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 +344,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, ParamNode p) { p.isParameterOf(viableCallableExt(call), i) } @@ -270,11 +353,11 @@ private module Cached { * dispatch into account. */ cached - predicate viableParamArg(DataFlowCall call, ParameterNode p, ArgumentNode arg) { + predicate viableParamArg(DataFlowCall call, ParamNode p, ArgNode 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 +395,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(ParamNode p, Node node, boolean read) { p = node and read = false or @@ -325,30 +408,30 @@ 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(ArgNode arg | parameterValueFlowArgCand(p, arg, false) and argumentValueFlowsThroughCand(arg, node, read) ) or // flow through: no read inside method - exists(ArgumentNode arg | + exists(ArgNode arg | parameterValueFlowArgCand(p, arg, read) and argumentValueFlowsThroughCand(arg, node, false) ) } pragma[nomagic] - private predicate parameterValueFlowArgCand(ParameterNode p, ArgumentNode arg, boolean read) { + private predicate parameterValueFlowArgCand(ParamNode p, ArgNode arg, boolean read) { parameterValueFlowCand(p, arg, read) } pragma[nomagic] - predicate parameterValueFlowsToPreUpdateCand(ParameterNode p, PostUpdateNode n) { + predicate parameterValueFlowsToPreUpdateCand(ParamNode p, PostUpdateNode n) { parameterValueFlowCand(p, n.getPreUpdateNode(), false) } @@ -360,7 +443,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(ParamNode p, ReturnKind kind, boolean read) { exists(ReturnNode ret | parameterValueFlowCand(p, ret, read) and kind = ret.getKind() @@ -369,9 +452,9 @@ private module Cached { pragma[nomagic] private predicate argumentValueFlowsThroughCand0( - DataFlowCall call, ArgumentNode arg, ReturnKind kind, boolean read + DataFlowCall call, ArgNode arg, ReturnKind kind, boolean read ) { - exists(ParameterNode param | viableParamArg(call, param, arg) | + exists(ParamNode param | viableParamArg(call, param, arg) | parameterValueFlowReturnCand(param, kind, read) ) } @@ -382,14 +465,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(ArgNode 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(ParamNode p, Node n) { parameterValueFlowCand(p, n, _) and ( parameterValueFlowReturnCand(p, _, _) @@ -416,21 +499,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(ParamNode 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(ParamNode p, Node node, ReadStepTypesOption read) { p = node and Cand::cand(p, _) and read = TReadStepTypesNone() @@ -447,7 +530,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,34 +538,32 @@ private module Cached { pragma[nomagic] private predicate parameterValueFlow0_0( - ReadStepTypesOption mustBeNone, ParameterNode p, Node node, ReadStepTypesOption read + ReadStepTypesOption mustBeNone, ParamNode p, Node node, ReadStepTypesOption read ) { // flow through: no prior read - exists(ArgumentNode arg | + exists(ArgNode arg | parameterValueFlowArg(p, arg, mustBeNone) and argumentValueFlowsThrough(arg, read, node) ) or // flow through: no read inside method - exists(ArgumentNode arg | + exists(ArgNode arg | parameterValueFlowArg(p, arg, read) and argumentValueFlowsThrough(arg, mustBeNone, node) ) } pragma[nomagic] - private predicate parameterValueFlowArg( - ParameterNode p, ArgumentNode 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, ArgumentNode arg, ReturnKind kind, ReadStepTypesOption read + DataFlowCall call, ArgNode arg, ReturnKind kind, ReadStepTypesOption read ) { - exists(ParameterNode param | viableParamArg(call, param, arg) | + exists(ParamNode param | viableParamArg(call, param, arg) | parameterValueFlowReturn(param, kind, read) ) } @@ -496,18 +577,18 @@ private module Cached { * container type, and the content type. */ pragma[nomagic] - predicate argumentValueFlowsThrough(ArgumentNode 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) | // 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 +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(ArgumentNode arg, Content c, Node out) { + predicate getterStep(ArgNode arg, Content c, Node out) { argumentValueFlowsThrough(arg, TReadStepTypesSome(_, c, _), out) } @@ -529,7 +610,7 @@ private module Cached { * container type, and the content type. */ private predicate parameterValueFlowReturn( - ParameterNode p, ReturnKind kind, ReadStepTypesOption read + ParamNode p, ReturnKind kind, ReadStepTypesOption read ) { exists(ReturnNode ret | parameterValueFlow(p, ret, read) and @@ -553,7 +634,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 +692,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 +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. */ - cached - predicate parameterValueFlowsToPreUpdate(ParameterNode p, PostUpdateNode n) { + private predicate parameterValueFlowsToPreUpdate(ParamNode p, PostUpdateNode n) { parameterValueFlow(p, n.getPreUpdateNode(), TReadStepTypesNone()) } @@ -644,9 +724,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 +734,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 +761,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 +772,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.(ArgNode).argumentOf(c, _) and + simpleLocalFlowStep(toPre.(ArgNode), 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 +794,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 +816,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(ParamNode p | p.isParameterOf(_, pos)) } cached newtype TBooleanOption = @@ -761,23 +851,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 +936,7 @@ class CallContextSomeCall extends CallContextCall, TSomeCall { override string toString() { result = "CcSomeCall" } override predicate relevantFor(DataFlowCallable callable) { - exists(ParameterNode p | getNodeEnclosingCallable(p) = callable) + exists(ParamNode p | getNodeEnclosingCallable(p) = callable) } override predicate matchesCall(DataFlowCall call) { any() } @@ -866,7 +948,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 +981,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 +995,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 ParamNode extends Node { + ParamNode() { 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 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) } +} + /** * 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 +1033,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 +1046,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 +1057,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 +1067,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 +1093,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 +1123,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 +1144,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 +1195,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/DataFlowPrivate.qll b/python/ql/src/semmle/python/dataflow/new/internal/DataFlowPrivate.qll index 82c1751bb07..074289b00fd 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. diff --git a/python/ql/src/semmle/python/security/internal/SensitiveDataHeuristics.qll b/python/ql/src/semmle/python/security/internal/SensitiveDataHeuristics.qll index ddf95b1b534..589c37120b9 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 = "(?s).*([uU]|^|_|[a-z](?=U))([uU][iI][dD]).*" } /** 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 new file mode 100644 index 00000000000..97ed0d1bdfa --- /dev/null +++ b/python/ql/test/experimental/query-tests/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: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: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: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 | diff --git a/python/ql/test/experimental/query-tests/Security/CWE-730/RegexInjection.qlref b/python/ql/test/experimental/query-tests/Security/CWE-730/RegexInjection.qlref new file mode 100644 index 00000000000..c0c506c4707 --- /dev/null +++ b/python/ql/test/experimental/query-tests/Security/CWE-730/RegexInjection.qlref @@ -0,0 +1 @@ +experimental/Security/CWE-730/RegexInjection.ql 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 new file mode 100644 index 00000000000..622eaf199f6 --- /dev/null +++ b/python/ql/test/experimental/query-tests/Security/CWE-730/re_bad.py @@ -0,0 +1,40 @@ +from flask import request, Flask +import re + +app = Flask(__name__) + + +@app.route("/direct") +def direct(): + """ + A RemoteFlowSource is used directly as re.search's pattern + """ + + unsafe_pattern = request.args["pattern"] + re.search(unsafe_pattern, "") + + +@app.route("/compile") +def compile(): + """ + 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(): + """ + 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/test/experimental/query-tests/Security/CWE-730/re_good.py b/python/ql/test/experimental/query-tests/Security/CWE-730/re_good.py new file mode 100644 index 00000000000..6dc58b87f85 --- /dev/null +++ b/python/ql/test/experimental/query-tests/Security/CWE-730/re_good.py @@ -0,0 +1,45 @@ +from flask import request, Flask +import re + +app = Flask(__name__) + + +@app.route("/direct") +def direct(): + """ + 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(): + """ + 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(): + """ + 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__": +# app.run(debug=True)